You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sl...@apache.org on 2016/08/03 15:45:07 UTC

[01/14] cassandra git commit: Update build.xml and CHANGES.txt for 3.8

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.8 b27e2f93c -> 26838063d
  refs/heads/cassandra-3.9 1323ad0fb -> b603720e4
  refs/heads/trunk 5cc0d152b -> 4e21f7267


Update build.xml and CHANGES.txt for 3.8


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c3ded055
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c3ded055
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c3ded055

Branch: refs/heads/cassandra-3.9
Commit: c3ded0551f538f7845602b27d53240cd8129265c
Parents: 2aa7663
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Mon Jul 18 16:47:52 2016 +0100
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Jul 18 16:47:52 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt | 48 +++++++++++++++++++++---------------------------
 build.xml   |  2 +-
 2 files changed, 22 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/c3ded055/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 3307fb3..4330fde 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,33 +1,7 @@
-3.9
+3.8
  * Fix hdr logging for single operation workloads (CASSANDRA-12145)
  * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
  * Increase size of flushExecutor thread pool (CASSANDRA-12071)
-Merged from 3.0:
- * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
- * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
- * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
- * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
- * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
- * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
- * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
- * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
- * Fix column ordering of results with static columns for Thrift requests in
-   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
-   those static columns in query results (CASSANDRA-12123)
- * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
- * Fix EOF exception when altering column type (CASSANDRA-11820)
-Merged from 2.2:
- * Synchronize ThriftServer::stop() (CASSANDRA-12105)
- * Use dedicated thread for JMX notifications (CASSANDRA-12146)
- * Improve streaming synchronization and fault tolerance (CASSANDRA-11414)
- * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
-Merged from 2.1:
- * Fix filtering on clustering columns when 2i is used (CASSANDRA-11907)
- * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043)
- * Remove finished incoming streaming connections from MessagingService (CASSANDRA-11854)
-
-
-3.8
  * Partial revert of CASSANDRA-11971, cannot recycle buffer in SP.sendMessagesToNonlocalDC (CASSANDRA-11950)
  * Upgrade netty to 4.0.39 (CASSANDRA-12032, CASSANDRA-12034)
  * Improve details in compaction log message (CASSANDRA-12080)
@@ -53,18 +27,38 @@ Merged from 2.1:
  * Add repaired percentage metric (CASSANDRA-11503)
  * Add Change-Data-Capture (CASSANDRA-8844)
 Merged from 3.0:
+ * Fix paging logic for deleted partitions with static columns (CASSANDRA-12107)
+ * Wait until the message is being send to decide which serializer must be used (CASSANDRA-11393)
+ * Fix migration of static thrift column names with non-text comparators (CASSANDRA-12147)
+ * Fix upgrading sparse tables that are incorrectly marked as dense (CASSANDRA-11315)
+ * Fix reverse queries ignoring range tombstones (CASSANDRA-11733)
+ * Avoid potential race when rebuilding CFMetaData (CASSANDRA-12098)
+ * Avoid missing sstables when getting the canonical sstables (CASSANDRA-11996)
+ * Always select the live sstables when getting sstables in bounds (CASSANDRA-11944)
+ * Fix column ordering of results with static columns for Thrift requests in
+   a mixed 2.x/3.x cluster, also fix potential non-resolved duplication of
+   those static columns in query results (CASSANDRA-12123)
+ * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
  * cqlsh: fix error handling in rare COPY FROM failure scenario (CASSANDRA-12070)
  * Disable autocompaction during drain (CASSANDRA-11878)
  * Add a metrics timer to MemtablePool and use it to track time spent blocked on memory in MemtableAllocator (CASSANDRA-11327)
  * Fix upgrading schema with super columns with non-text subcomparators (CASSANDRA-12023)
  * Add TimeWindowCompactionStrategy (CASSANDRA-9666)
 Merged from 2.2:
+ * Synchronize ThriftServer::stop() (CASSANDRA-12105)
+ * Use dedicated thread for JMX notifications (CASSANDRA-12146)
+ * Improve streaming synchronization and fault tolerance (CASSANDRA-11414)
+ * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
  * Allow nodetool info to run with readonly JMX access (CASSANDRA-11755)
  * Validate bloom_filter_fp_chance against lowest supported
    value when the table is created (CASSANDRA-11920)
  * Don't send erroneous NEW_NODE notifications on restart (CASSANDRA-11038)
  * StorageService shutdown hook should use a volatile variable (CASSANDRA-11984)
 Merged from 2.1:
+ * Fix filtering on clustering columns when 2i is used (CASSANDRA-11907)
+ * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043)
+ * Remove finished incoming streaming connections from MessagingService (CASSANDRA-11854)
  * Don't try to get sstables for non-repairing column families (CASSANDRA-12077)
  * Avoid marking too many sstables as repaired (CASSANDRA-11696)
  * Prevent select statements with clustering key > 64k (CASSANDRA-11882)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c3ded055/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index e13fdc0..f16d917 100644
--- a/build.xml
+++ b/build.xml
@@ -25,7 +25,7 @@
     <property name="debuglevel" value="source,lines,vars"/>
 
     <!-- default version and SCM information -->
-    <property name="base.version" value="3.9"/>
+    <property name="base.version" value="3.8"/>
     <property name="scm.connection" value="scm:git://git.apache.org/cassandra.git"/>
     <property name="scm.developerConnection" value="scm:git://git.apache.org/cassandra.git"/>
     <property name="scm.url" value="http://git-wip-us.apache.org/repos/asf?p=cassandra.git;a=tree"/>


[02/14] cassandra git commit: c* uses commons-lang3, not commons-lang

Posted by sl...@apache.org.
c* uses commons-lang3, not commons-lang


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b27e2f93
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b27e2f93
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b27e2f93

Branch: refs/heads/cassandra-3.9
Commit: b27e2f93cc9bc33a95d531f43442b93e85ba4a30
Parents: c3ded05
Author: Dave Brosius <db...@mebigfatguy.com>
Authored: Mon Jul 4 17:23:46 2016 -0400
Committer: Dave Brosius <db...@mebigfatguy.com>
Committed: Tue Jul 26 20:04:42 2016 -0400

----------------------------------------------------------------------
 src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b27e2f93/src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java
index a914cc9..6acbd0d 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogReader.java
@@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.zip.CRC32;
 
 import com.google.common.annotations.VisibleForTesting;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 


[09/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
index 2de671c..72441cd 100644
--- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
+++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
@@ -34,6 +34,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.FieldIdentifier;
 import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.Row;
 import org.apache.cassandra.db.marshal.*;
 import org.apache.cassandra.index.TargetParser;
 import org.apache.cassandra.thrift.ThriftConversion;
@@ -564,35 +565,40 @@ public class LegacySchemaMigratorTest
         setLegacyIndexStatus(keyspace);
     }
 
-    private static Mutation makeLegacyCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    private static DecoratedKey decorate(CFMetaData metadata, Object value)
     {
-        // Note that because Keyspaces is a COMPACT TABLE, we're really only setting static columns internally and shouldn't set any clustering.
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyKeyspaces, timestamp, keyspace.name);
+        return metadata.decorateKey(((AbstractType)metadata.getKeyValidator()).decompose(value));
+    }
 
-        adder.add("durable_writes", keyspace.params.durableWrites)
-             .add("strategy_class", keyspace.params.replication.klass.getName())
-             .add("strategy_options", json(keyspace.params.replication.options));
+    private static Mutation makeLegacyCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    {
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(SystemKeyspace.NAME, decorate(SystemKeyspace.LegacyKeyspaces, keyspace.name))
+                                                 .timestamp(timestamp);
 
-        Mutation mutation = adder.build();
+        builder.update(SystemKeyspace.LegacyKeyspaces)
+               .row()
+               .add("durable_writes", keyspace.params.durableWrites)
+               .add("strategy_class", keyspace.params.replication.klass.getName())
+               .add("strategy_options", json(keyspace.params.replication.options));
 
-        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation));
-        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation));
-        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation));
-        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation));
+        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, true, builder));
+        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, builder));
+        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, builder));
+        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, builder));
 
-        return mutation;
+        return builder.build();
     }
 
     /*
      * Serializing tables
      */
 
-    private static void addTableToSchemaMutation(CFMetaData table, long timestamp, boolean withColumnsAndTriggers, Mutation mutation)
+    private static void addTableToSchemaMutation(CFMetaData table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder)
     {
         // For property that can be null (and can be changed), we insert tombstones, to make sure
         // we don't keep a property the user has removed
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyColumnfamilies, timestamp, mutation)
-                                 .clustering(table.cfName);
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyColumnfamilies)
+                                         .row(table.cfName);
 
         adder.add("cf_id", table.cfId)
              .add("type", table.isSuper() ? "Super" : "Standard");
@@ -625,12 +631,14 @@ public class LegacySchemaMigratorTest
              .add("read_repair_chance", table.params.readRepairChance)
              .add("speculative_retry", table.params.speculativeRetry.toString());
 
+        Map<String, Long> dropped = new HashMap<>();
         for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry : table.getDroppedColumns().entrySet())
         {
             String name = UTF8Type.instance.getString(entry.getKey());
             CFMetaData.DroppedColumn column = entry.getValue();
-            adder.addMapEntry("dropped_columns", name, column.droppedTime);
+            dropped.put(name, column.droppedTime);
         }
+        adder.add("dropped_columns", dropped);
 
         adder.add("is_dense", table.isDense());
 
@@ -639,13 +647,11 @@ public class LegacySchemaMigratorTest
         if (withColumnsAndTriggers)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (TriggerMetadata trigger : table.getTriggers())
-                addTriggerToSchemaMutation(table, trigger, timestamp, mutation);
+                addTriggerToSchemaMutation(table, trigger, builder);
         }
-
-        adder.build();
     }
 
     private static String cachingToString(CachingParams caching)
@@ -655,14 +661,14 @@ public class LegacySchemaMigratorTest
                       caching.rowsPerPartitionAsString());
     }
 
-    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
         // We need to special case pk-only dense tables. See CASSANDRA-9874.
         String name = table.isDense() && column.kind == ColumnDefinition.Kind.REGULAR && column.type instanceof EmptyType
                     ? ""
                     : column.name.toString();
 
-        final RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyColumns, timestamp, mutation).clustering(table.cfName, name);
+        final Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyColumns).row(table.cfName, name);
 
         adder.add("validator", column.type.toString())
              .add("type", serializeKind(column.kind, table.isDense()))
@@ -682,8 +688,6 @@ public class LegacySchemaMigratorTest
             adder.add("index_type", null);
             adder.add("index_options", null);
         }
-
-        adder.build();
     }
 
     private static Optional<IndexMetadata> findIndexForColumn(Indexes indexes,
@@ -712,71 +716,67 @@ public class LegacySchemaMigratorTest
         return kind.toString().toLowerCase();
     }
 
-    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        new RowUpdateBuilder(SystemKeyspace.LegacyTriggers, timestamp, mutation)
-            .clustering(table.cfName, trigger.name)
-            .addMapEntry("trigger_options", "class", trigger.classOption)
-            .build();
+        builder.update(SystemKeyspace.LegacyTriggers)
+               .row(table.cfName, trigger.name)
+               .add("trigger_options", Collections.singletonMap("class", trigger.classOption));
     }
 
     /*
      * Serializing types
      */
 
-    private static void addTypeToSchemaMutation(UserType type, long timestamp, Mutation mutation)
+    private static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyUsertypes, timestamp, mutation)
-                                 .clustering(type.getNameAsString());
-
-        adder.resetCollection("field_names")
-             .resetCollection("field_types");
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyUsertypes)
+                                         .row(type.getNameAsString());
 
+        List<String> names = new ArrayList<>();
+        List<String> types = new ArrayList<>();
         for (int i = 0; i < type.size(); i++)
         {
-            adder.addListEntry("field_names", type.fieldName(i).toString())
-                 .addListEntry("field_types", type.fieldType(i).toString());
+            names.add(type.fieldName(i).toString());
+            types.add(type.fieldType(i).toString());
         }
 
-        adder.build();
+        adder.add("field_names", names)
+             .add("field_types", types);
     }
 
     /*
      * Serializing functions
      */
 
-    private static void addFunctionToSchemaMutation(UDFunction function, long timestamp, Mutation mutation)
+    private static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyFunctions, timestamp, mutation)
-                                 .clustering(function.name().name, functionSignatureWithTypes(function));
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyFunctions)
+                                         .row(function.name().name, functionSignatureWithTypes(function));
 
         adder.add("body", function.body())
              .add("language", function.language())
              .add("return_type", function.returnType().toString())
              .add("called_on_null_input", function.isCalledOnNullInput());
 
-        adder.resetCollection("argument_names")
-             .resetCollection("argument_types");
-
+        List<ByteBuffer> names = new ArrayList<>();
+        List<String> types = new ArrayList<>();
         for (int i = 0; i < function.argNames().size(); i++)
         {
-            adder.addListEntry("argument_names", function.argNames().get(i).bytes)
-                 .addListEntry("argument_types", function.argTypes().get(i).toString());
+            names.add(function.argNames().get(i).bytes);
+            types.add(function.argTypes().get(i).toString());
         }
-
-        adder.build();
+        adder.add("argument_names", names)
+             .add("argument_types", types);
     }
 
     /*
      * Serializing aggregates
      */
 
-    private static void addAggregateToSchemaMutation(UDAggregate aggregate, long timestamp, Mutation mutation)
+    private static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyAggregates, timestamp, mutation)
-                                 .clustering(aggregate.name().name, functionSignatureWithTypes(aggregate));
-
-        adder.resetCollection("argument_types");
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyAggregates)
+                                 .row(aggregate.name().name, functionSignatureWithTypes(aggregate));
 
         adder.add("return_type", aggregate.returnType().toString())
              .add("state_func", aggregate.stateFunction().name().name);
@@ -788,10 +788,11 @@ public class LegacySchemaMigratorTest
         if (aggregate.initialCondition() != null)
             adder.add("initcond", aggregate.initialCondition());
 
+        List<String> types = new ArrayList<>();
         for (AbstractType<?> argType : aggregate.argTypes())
-            adder.addListEntry("argument_types", argType.toString());
+            types.add(argType.toString());
 
-        adder.build();
+        adder.add("argument_types", types);
     }
 
     // We allow method overloads, so a function is not uniquely identified by its name only, but

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java b/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
index b2e3535..d686fdb 100644
--- a/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
+++ b/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
@@ -164,7 +164,7 @@ public class SchemaKeyspaceTest
     private static void updateTable(String keyspace, CFMetaData oldTable, CFMetaData newTable)
     {
         KeyspaceMetadata ksm = Schema.instance.getKeyspaceInstance(keyspace).getMetadata();
-        Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros());
+        Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros()).build();
         SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
     }
 
@@ -173,7 +173,7 @@ public class SchemaKeyspaceTest
         CFMetaData table = CFMetaData.compile(cql, keyspace);
 
         KeyspaceMetadata ksm = KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.of(table));
-        Mutation mutation = SchemaKeyspace.makeCreateTableMutation(ksm, table, FBUtilities.timestampMicros());
+        Mutation mutation = SchemaKeyspace.makeCreateTableMutation(ksm, table, FBUtilities.timestampMicros()).build();
         SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
     }
 
@@ -187,7 +187,7 @@ public class SchemaKeyspaceTest
         assert before.equals(after) : String.format("%n%s%n!=%n%s", before, after);
 
         // Test schema conversion
-        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
+        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros()).build();
         PartitionUpdate serializedCf = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
         PartitionUpdate serializedCD = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/service/DataResolverTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/service/DataResolverTest.java b/test/unit/org/apache/cassandra/service/DataResolverTest.java
index b20dfc0..b48512f 100644
--- a/test/unit/org/apache/cassandra/service/DataResolverTest.java
+++ b/test/unit/org/apache/cassandra/service/DataResolverTest.java
@@ -245,7 +245,7 @@ public class DataResolverTest
 
         RangeTombstone tombstone1 = tombstone("1", "11", 1, nowInSec);
         RangeTombstone tombstone2 = tombstone("3", "31", 1, nowInSec);
-        PartitionUpdate update =new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1)
+        PartitionUpdate update = new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1)
                                                                                   .addRangeTombstone(tombstone2)
                                                                                   .buildUpdate();
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
index 6be880c..879f44c 100644
--- a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
+++ b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
@@ -363,7 +363,7 @@ public class StreamingTransferTest
 
 
         updates = new RowUpdateBuilder(cfs.metadata, FBUtilities.timestampMicros() + 1, key);
-        updates.addRangeTombstone(Slice.make(comparator.make(5), comparator.make(7)))
+        updates.addRangeTombstone(5, 7)
                 .build()
                 .apply();
 


[04/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index 8e3961e..7a90dab 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -266,8 +266,8 @@ public final class SchemaKeyspace
         }
 
         // (+1 to timestamp to make sure we don't get shadowed by the tombstones we just added)
-        makeCreateKeyspaceMutation(system, timestamp + 1).apply();
-        makeCreateKeyspaceMutation(schema, timestamp + 1).apply();
+        makeCreateKeyspaceMutation(system, timestamp + 1).build().apply();
+        makeCreateKeyspaceMutation(schema, timestamp + 1).build().apply();
     }
 
     public static void truncate()
@@ -377,11 +377,6 @@ public final class SchemaKeyspace
         }
     }
 
-    private static ByteBuffer getSchemaKSKey(String ksName)
-    {
-        return AsciiType.instance.fromString(ksName);
-    }
-
     private static boolean isSystemKeyspaceSchemaPartition(DecoratedKey partitionKey)
     {
         return Schema.isSystemKeyspace(UTF8Type.instance.compose(partitionKey.getKey()));
@@ -391,152 +386,152 @@ public final class SchemaKeyspace
      * Schema entities to mutations
      */
 
-    public static Mutation makeCreateKeyspaceMutation(String name, KeyspaceParams params, long timestamp)
+    private static DecoratedKey decorate(CFMetaData metadata, Object value)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Keyspaces, timestamp, name).clustering();
-        return adder.add(KeyspaceParams.Option.DURABLE_WRITES.toString(), params.durableWrites)
-                    .frozenMap(KeyspaceParams.Option.REPLICATION.toString(), params.replication.asMap())
-                    .build();
+        return metadata.decorateKey(((AbstractType)metadata.getKeyValidator()).decompose(value));
     }
 
-    public static Mutation makeCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateKeyspaceMutation(String name, KeyspaceParams params, long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(Keyspaces.ksName, decorate(Keyspaces, name))
+                                                 .timestamp(timestamp);
 
-        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation));
-        keyspace.views.forEach(view -> addViewToSchemaMutation(view, timestamp, true, mutation));
-        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation));
-        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation));
-        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation));
+        builder.update(Keyspaces)
+               .row()
+               .add(KeyspaceParams.Option.DURABLE_WRITES.toString(), params.durableWrites)
+               .add(KeyspaceParams.Option.REPLICATION.toString(), params.replication.asMap());
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeDropKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
     {
-        int nowInSec = FBUtilities.nowInSeconds();
-        Mutation mutation = new Mutation(NAME, Keyspaces.decorateKey(getSchemaKSKey(keyspace.name)));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        for (CFMetaData schemaTable : ALL_TABLE_METADATA)
-            mutation.add(PartitionUpdate.fullPartitionDelete(schemaTable, mutation.key(), timestamp, nowInSec));
+        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, true, builder));
+        keyspace.views.forEach(view -> addViewToSchemaMutation(view, true, builder));
+        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, builder));
+        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, builder));
+        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, builder));
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
+    public static Mutation.SimpleBuilder makeDropKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
     {
-        // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addTypeToSchemaMutation(type, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(NAME, decorate(Keyspaces, keyspace.name))
+                                                 .timestamp(timestamp);
+
+        for (CFMetaData schemaTable : ALL_TABLE_METADATA)
+            builder.update(schemaTable).delete();
+
+        return builder;
     }
 
-    static void addTypeToSchemaMutation(UserType type, long timestamp, Mutation mutation)
+    public static Mutation.SimpleBuilder makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Types, timestamp, mutation)
-                                 .clustering(type.getNameAsString())
-                                 .frozenList("field_names", type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList()))
-                                 .frozenList("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList()));
-
-        adder.build();
+        // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addTypeToSchemaMutation(type, builder);
+        return builder;
     }
 
-    private static String bbToString(ByteBuffer bb)
+    static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder mutation)
     {
-        try
-        {
-            return ByteBufferUtil.string(bb);
-        }
-        catch (CharacterCodingException e)
-        {
-            throw new RuntimeException(e);
-        }
+        mutation.update(Types)
+                .row(type.getNameAsString())
+                .add("field_names", type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList()))
+                .add("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList()));
     }
 
-    public static Mutation dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
+    public static Mutation.SimpleBuilder dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Types, timestamp, mutation, type.name);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Types).row(type.name).delete();
+        return builder;
     }
 
-    public static Mutation makeCreateTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addTableToSchemaMutation(table, timestamp, true, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addTableToSchemaMutation(table, true, builder);
+        return builder;
     }
 
-    static void addTableToSchemaMutation(CFMetaData table, long timestamp, boolean withColumnsAndTriggers, Mutation mutation)
+    static void addTableToSchemaMutation(CFMetaData table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Tables, timestamp, mutation).clustering(table.cfName);
-
-        addTableParamsToSchemaMutation(table.params, adder);
+        Row.SimpleBuilder rowBuilder = builder.update(Tables)
+                                              .row(table.cfName)
+                                              .add("id", table.cfId)
+                                              .add("flags", CFMetaData.flagsToStrings(table.flags()));
 
-        adder.add("id", table.cfId)
-             .frozenSet("flags", CFMetaData.flagsToStrings(table.flags()))
-             .build();
+        addTableParamsToRowBuilder(table.params, rowBuilder);
 
         if (withColumnsAndTriggers)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values())
-                addDroppedColumnToSchemaMutation(table, column, timestamp, mutation);
+                addDroppedColumnToSchemaMutation(table, column, builder);
 
             for (TriggerMetadata trigger : table.getTriggers())
-                addTriggerToSchemaMutation(table, trigger, timestamp, mutation);
+                addTriggerToSchemaMutation(table, trigger, builder);
 
             for (IndexMetadata index : table.getIndexes())
-                addIndexToSchemaMutation(table, index, timestamp, mutation);
+                addIndexToSchemaMutation(table, index, builder);
         }
     }
 
-    private static void addTableParamsToSchemaMutation(TableParams params, RowUpdateBuilder adder)
+    private static void addTableParamsToRowBuilder(TableParams params, Row.SimpleBuilder builder)
     {
-        adder.add("bloom_filter_fp_chance", params.bloomFilterFpChance)
-             .add("comment", params.comment)
-             .add("dclocal_read_repair_chance", params.dcLocalReadRepairChance)
-             .add("default_time_to_live", params.defaultTimeToLive)
-             .add("gc_grace_seconds", params.gcGraceSeconds)
-             .add("max_index_interval", params.maxIndexInterval)
-             .add("memtable_flush_period_in_ms", params.memtableFlushPeriodInMs)
-             .add("min_index_interval", params.minIndexInterval)
-             .add("read_repair_chance", params.readRepairChance)
-             .add("speculative_retry", params.speculativeRetry.toString())
-             .add("crc_check_chance", params.crcCheckChance)
-             .frozenMap("caching", params.caching.asMap())
-             .frozenMap("compaction", params.compaction.asMap())
-             .frozenMap("compression", params.compression.asMap())
-             .frozenMap("extensions", params.extensions)
-             .add("cdc", params.cdc);
+        builder.add("bloom_filter_fp_chance", params.bloomFilterFpChance)
+               .add("comment", params.comment)
+               .add("dclocal_read_repair_chance", params.dcLocalReadRepairChance)
+               .add("default_time_to_live", params.defaultTimeToLive)
+               .add("gc_grace_seconds", params.gcGraceSeconds)
+               .add("max_index_interval", params.maxIndexInterval)
+               .add("memtable_flush_period_in_ms", params.memtableFlushPeriodInMs)
+               .add("min_index_interval", params.minIndexInterval)
+               .add("read_repair_chance", params.readRepairChance)
+               .add("speculative_retry", params.speculativeRetry.toString())
+               .add("crc_check_chance", params.crcCheckChance)
+               .add("caching", params.caching.asMap())
+               .add("compaction", params.compaction.asMap())
+               .add("compression", params.compression.asMap())
+               .add("extensions", params.extensions);
+
+        // Only add CDC-enabled flag to schema if it's enabled on the node. This is to work around RTE's post-8099 if a 3.8+
+        // node sends table schema to a < 3.8 versioned node with an unknown column.
+        if (DatabaseDescriptor.isCDCEnabled())
+            builder.add("cdc", params.cdc);
     }
 
-    public static Mutation makeUpdateTableMutation(KeyspaceMetadata keyspace,
-                                                   CFMetaData oldTable,
-                                                   CFMetaData newTable,
-                                                   long timestamp)
+    public static Mutation.SimpleBuilder makeUpdateTableMutation(KeyspaceMetadata keyspace,
+                                                                 CFMetaData oldTable,
+                                                                 CFMetaData newTable,
+                                                                 long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        addTableToSchemaMutation(newTable, timestamp, false, mutation);
+        addTableToSchemaMutation(newTable, false, builder);
 
         MapDifference<ByteBuffer, ColumnDefinition> columnDiff = Maps.difference(oldTable.getColumnMetadata(),
                                                                                  newTable.getColumnMetadata());
 
         // columns that are no longer needed
         for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values())
-            dropColumnFromSchemaMutation(oldTable, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(oldTable, column, builder);
 
         // newly added columns
         for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values())
-            addColumnToSchemaMutation(newTable, column, timestamp, mutation);
+            addColumnToSchemaMutation(newTable, column, builder);
 
         // old columns with updated attributes
         for (ByteBuffer name : columnDiff.entriesDiffering().keySet())
-            addColumnToSchemaMutation(newTable, newTable.getColumnDefinition(name), timestamp, mutation);
+            addColumnToSchemaMutation(newTable, newTable.getColumnDefinition(name), builder);
 
         // dropped columns
         MapDifference<ByteBuffer, CFMetaData.DroppedColumn> droppedColumnDiff =
@@ -544,38 +539,38 @@ public final class SchemaKeyspace
 
         // newly dropped columns
         for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values())
-            addDroppedColumnToSchemaMutation(newTable, column, timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newTable, column, builder);
 
         // columns added then dropped again
         for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet())
-            addDroppedColumnToSchemaMutation(newTable, newTable.getDroppedColumns().get(name), timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newTable, newTable.getDroppedColumns().get(name), builder);
 
         MapDifference<String, TriggerMetadata> triggerDiff = triggersDiff(oldTable.getTriggers(), newTable.getTriggers());
 
         // dropped triggers
         for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnLeft().values())
-            dropTriggerFromSchemaMutation(oldTable, trigger, timestamp, mutation);
+            dropTriggerFromSchemaMutation(oldTable, trigger, builder);
 
         // newly created triggers
         for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnRight().values())
-            addTriggerToSchemaMutation(newTable, trigger, timestamp, mutation);
+            addTriggerToSchemaMutation(newTable, trigger, builder);
 
         MapDifference<String, IndexMetadata> indexesDiff = indexesDiff(oldTable.getIndexes(),
                                                                        newTable.getIndexes());
 
         // dropped indexes
         for (IndexMetadata index : indexesDiff.entriesOnlyOnLeft().values())
-            dropIndexFromSchemaMutation(oldTable, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(oldTable, index, builder);
 
         // newly created indexes
         for (IndexMetadata index : indexesDiff.entriesOnlyOnRight().values())
-            addIndexToSchemaMutation(newTable, index, timestamp, mutation);
+            addIndexToSchemaMutation(newTable, index, builder);
 
         // updated indexes need to be updated
         for (MapDifference.ValueDifference<IndexMetadata> diff : indexesDiff.entriesDiffering().values())
-            addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), timestamp, mutation);
+            addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), builder);
 
-        return mutation;
+        return builder;
     }
 
     private static MapDifference<String, IndexMetadata> indexesDiff(Indexes before, Indexes after)
@@ -600,144 +595,137 @@ public final class SchemaKeyspace
         return Maps.difference(beforeMap, afterMap);
     }
 
-    public static Mutation makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
+    public static Mutation.SimpleBuilder makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        RowUpdateBuilder.deleteRow(Tables, timestamp, mutation, table.cfName);
+        builder.update(Tables).row(table.cfName).delete();
 
         for (ColumnDefinition column : table.allColumns())
-            dropColumnFromSchemaMutation(table, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(table, column, builder);
 
         for (TriggerMetadata trigger : table.getTriggers())
-            dropTriggerFromSchemaMutation(table, trigger, timestamp, mutation);
+            dropTriggerFromSchemaMutation(table, trigger, builder);
 
         for (IndexMetadata index : table.getIndexes())
-            dropIndexFromSchemaMutation(table, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(table, index, builder);
 
-        return mutation;
+        return builder;
     }
 
-    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Columns, timestamp, mutation).clustering(table.cfName, column.name.toString());
-
         AbstractType<?> type = column.type;
         if (type instanceof ReversedType)
             type = ((ReversedType) type).baseType;
 
-        adder.add("column_name_bytes", column.name.bytes)
-             .add("kind", column.kind.toString().toLowerCase())
-             .add("position", column.position())
-             .add("clustering_order", column.clusteringOrder().toString().toLowerCase())
-             .add("type", type.asCQL3Type().toString())
-             .build();
+        builder.update(Columns)
+               .row(table.cfName, column.name.toString())
+               .add("column_name_bytes", column.name.bytes)
+               .add("kind", column.kind.toString().toLowerCase())
+               .add("position", column.position())
+               .add("clustering_order", column.clusteringOrder().toString().toLowerCase())
+               .add("type", type.asCQL3Type().toString());
     }
 
-    private static void dropColumnFromSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void dropColumnFromSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
         // Note: we do want to use name.toString(), not name.bytes directly for backward compatibility (For CQL3, this won't make a difference).
-        RowUpdateBuilder.deleteRow(Columns, timestamp, mutation, table.cfName, column.name.toString());
+        builder.update(Columns).row(table.cfName, column.name.toString()).delete();
     }
 
-    private static void addDroppedColumnToSchemaMutation(CFMetaData table, CFMetaData.DroppedColumn column, long timestamp, Mutation mutation)
+    private static void addDroppedColumnToSchemaMutation(CFMetaData table, CFMetaData.DroppedColumn column, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(DroppedColumns, timestamp, mutation).clustering(table.cfName, column.name);
-
-        adder.add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime)))
-             .add("type", expandUserTypes(column.type).asCQL3Type().toString())
-             .build();
+        builder.update(DroppedColumns)
+               .row(table.cfName, column.name)
+               .add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime)))
+               .add("type", expandUserTypes(column.type).asCQL3Type().toString());
     }
 
-    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        new RowUpdateBuilder(Triggers, timestamp, mutation)
-            .clustering(table.cfName, trigger.name)
-            .frozenMap("options", Collections.singletonMap("class", trigger.classOption))
-            .build();
+        builder.update(Triggers)
+               .row(table.cfName, trigger.name)
+               .add("options", Collections.singletonMap("class", trigger.classOption));
     }
 
-    private static void dropTriggerFromSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void dropTriggerFromSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder.deleteRow(Triggers, timestamp, mutation, table.cfName, trigger.name);
+        builder.update(Triggers).row(table.cfName, trigger.name).delete();
     }
 
-    public static Mutation makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addViewToSchemaMutation(view, timestamp, true, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addViewToSchemaMutation(view, true, builder);
+        return builder;
     }
 
-    private static void addViewToSchemaMutation(ViewDefinition view, long timestamp, boolean includeColumns, Mutation mutation)
+    private static void addViewToSchemaMutation(ViewDefinition view, boolean includeColumns, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder builder = new RowUpdateBuilder(Views, timestamp, mutation)
-            .clustering(view.viewName);
-
         CFMetaData table = view.metadata;
+        Row.SimpleBuilder rowBuilder = builder.update(Views)
+                                              .row(view.viewName)
+                                              .add("include_all_columns", view.includeAllColumns)
+                                              .add("base_table_id", view.baseTableId)
+                                              .add("base_table_name", view.baseTableMetadata().cfName)
+                                              .add("where_clause", view.whereClause)
+                                              .add("id", table.cfId);
 
-        builder.add("include_all_columns", view.includeAllColumns)
-               .add("base_table_id", view.baseTableId)
-               .add("base_table_name", view.baseTableMetadata().cfName)
-               .add("where_clause", view.whereClause)
-               .add("id", table.cfId);
-
-        addTableParamsToSchemaMutation(table.params, builder);
+        addTableParamsToRowBuilder(table.params, rowBuilder);
 
         if (includeColumns)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values())
-                addDroppedColumnToSchemaMutation(table, column, timestamp, mutation);
+                addDroppedColumnToSchemaMutation(table, column, builder);
         }
-
-        builder.build();
     }
 
-    public static Mutation makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
+    public static Mutation.SimpleBuilder makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        RowUpdateBuilder.deleteRow(Views, timestamp, mutation, view.viewName);
+        builder.update(Views).row(view.viewName).delete();
 
         CFMetaData table = view.metadata;
         for (ColumnDefinition column : table.allColumns())
-            dropColumnFromSchemaMutation(table, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(table, column, builder);
 
         for (IndexMetadata index : table.getIndexes())
-            dropIndexFromSchemaMutation(table, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(table, index, builder);
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeUpdateViewMutation(KeyspaceMetadata keyspace,
-                                                  ViewDefinition oldView,
-                                                  ViewDefinition newView,
-                                                  long timestamp)
+    public static Mutation.SimpleBuilder makeUpdateViewMutation(KeyspaceMetadata keyspace,
+                                                                ViewDefinition oldView,
+                                                                ViewDefinition newView,
+                                                                long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        addViewToSchemaMutation(newView, timestamp, false, mutation);
+        addViewToSchemaMutation(newView, false, builder);
 
         MapDifference<ByteBuffer, ColumnDefinition> columnDiff = Maps.difference(oldView.metadata.getColumnMetadata(),
                                                                                  newView.metadata.getColumnMetadata());
 
         // columns that are no longer needed
         for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values())
-            dropColumnFromSchemaMutation(oldView.metadata, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(oldView.metadata, column, builder);
 
         // newly added columns
         for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values())
-            addColumnToSchemaMutation(newView.metadata, column, timestamp, mutation);
+            addColumnToSchemaMutation(newView.metadata, column, builder);
 
         // old columns with updated attributes
         for (ByteBuffer name : columnDiff.entriesDiffering().keySet())
-            addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), timestamp, mutation);
+            addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), builder);
 
         // dropped columns
         MapDifference<ByteBuffer, CFMetaData.DroppedColumn> droppedColumnDiff =
@@ -745,63 +733,68 @@ public final class SchemaKeyspace
 
         // newly dropped columns
         for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values())
-            addDroppedColumnToSchemaMutation(oldView.metadata, column, timestamp, mutation);
+            addDroppedColumnToSchemaMutation(oldView.metadata, column, builder);
 
         // columns added then dropped again
         for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet())
-            addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), builder);
 
-        return mutation;
+        return builder;
     }
 
     private static void addIndexToSchemaMutation(CFMetaData table,
                                                  IndexMetadata index,
-                                                 long timestamp,
-                                                 Mutation mutation)
+                                                 Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder builder = new RowUpdateBuilder(Indexes, timestamp, mutation).clustering(table.cfName, index.name);
-
-        builder.add("kind", index.kind.toString());
-        builder.frozenMap("options", index.options);
-        builder.build();
+        builder.update(Indexes)
+               .row(table.cfName, index.name)
+               .add("kind", index.kind.toString())
+               .add("options", index.options);
     }
 
     private static void dropIndexFromSchemaMutation(CFMetaData table,
                                                     IndexMetadata index,
-                                                    long timestamp,
-                                                    Mutation mutation)
+                                                    Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder.deleteRow(Indexes, timestamp, mutation, table.cfName, index.name);
+        builder.update(Indexes).row(table.cfName, index.name).delete();
     }
 
     private static void addUpdatedIndexToSchemaMutation(CFMetaData table,
                                                         IndexMetadata index,
-                                                        long timestamp,
-                                                        Mutation mutation)
+                                                        Mutation.SimpleBuilder builder)
     {
-        addIndexToSchemaMutation(table, index, timestamp, mutation);
+        addIndexToSchemaMutation(table, index, builder);
     }
 
-    public static Mutation makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addFunctionToSchemaMutation(function, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addFunctionToSchemaMutation(function, builder);
+        return builder;
     }
 
-    static void addFunctionToSchemaMutation(UDFunction function, long timestamp, Mutation mutation)
+    static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder =
-            new RowUpdateBuilder(Functions, timestamp, mutation).clustering(function.name().name, functionArgumentsList(function));
-
-        adder.add("body", function.body())
-             .add("language", function.language())
-             .add("return_type", function.returnType().asCQL3Type().toString())
-             .add("called_on_null_input", function.isCalledOnNullInput())
-             .frozenList("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
+        builder.update(Functions)
+               .row(function.name().name, functionArgumentsList(function))
+               .add("body", function.body())
+               .add("language", function.language())
+               .add("return_type", function.returnType().asCQL3Type().toString())
+               .add("called_on_null_input", function.isCalledOnNullInput())
+               .add("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
+    }
 
-        adder.build();
+    private static String bbToString(ByteBuffer bb)
+    {
+        try
+        {
+            return ByteBufferUtil.string(bb);
+        }
+        catch (CharacterCodingException e)
+        {
+            throw new RuntimeException(e);
+        }
     }
 
     private static List<String> functionArgumentsList(AbstractFunction fun)
@@ -813,42 +806,42 @@ public final class SchemaKeyspace
                   .collect(toList());
     }
 
-    public static Mutation makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
+    public static Mutation.SimpleBuilder makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Functions, timestamp, mutation, function.name().name, functionArgumentsList(function));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Functions).row(function.name().name, functionArgumentsList(function)).delete();
+        return builder;
     }
 
-    public static Mutation makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addAggregateToSchemaMutation(aggregate, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addAggregateToSchemaMutation(aggregate, builder);
+        return builder;
     }
 
-    static void addAggregateToSchemaMutation(UDAggregate aggregate, long timestamp, Mutation mutation)
+    static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder =
-            new RowUpdateBuilder(Aggregates, timestamp, mutation) .clustering(aggregate.name().name, functionArgumentsList(aggregate));
-
-        adder.add("return_type", aggregate.returnType().asCQL3Type().toString())
-             .add("state_func", aggregate.stateFunction().name().name)
-             .add("state_type", aggregate.stateType().asCQL3Type().toString())
-             .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
-             .add("initcond", aggregate.initialCondition() != null
-                              // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
-                              ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
-                              : null)
-             .build();
+        builder.update(Aggregates)
+               .row(aggregate.name().name, functionArgumentsList(aggregate))
+               .add("return_type", aggregate.returnType().asCQL3Type().toString())
+               .add("state_func", aggregate.stateFunction().name().name)
+               .add("state_type", aggregate.stateType().asCQL3Type().toString())
+               .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
+               .add("initcond", aggregate.initialCondition() != null
+                                // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
+                                ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
+                                : null);
     }
 
-    public static Mutation makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
+    public static Mutation.SimpleBuilder makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Aggregates, timestamp, mutation, aggregate.name().name, functionArgumentsList(aggregate));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Aggregates).row(aggregate.name().name, functionArgumentsList(aggregate)).delete();
+        return builder;
     }
 
     /*

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/service/MigrationManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java b/src/java/org/apache/cassandra/service/MigrationManager.java
index ba239b3..7eac678 100644
--- a/src/java/org/apache/cassandra/service/MigrationManager.java
+++ b/src/java/org/apache/cassandra/service/MigrationManager.java
@@ -506,12 +506,14 @@ public class MigrationManager
      * actively announce a new version to active hosts via rpc
      * @param schema The schema mutation to be applied
      */
-    private static void announce(Mutation schema, boolean announceLocally)
+    private static void announce(Mutation.SimpleBuilder schema, boolean announceLocally)
     {
+        List<Mutation> mutations = Collections.singletonList(schema.build());
+
         if (announceLocally)
-            SchemaKeyspace.mergeSchema(Collections.singletonList(schema));
+            SchemaKeyspace.mergeSchema(mutations);
         else
-            FBUtilities.waitOnFuture(announce(Collections.singletonList(schema)));
+            FBUtilities.waitOnFuture(announce(mutations));
     }
 
     private static void pushSchemaMutation(InetAddress endpoint, Collection<Mutation> schema)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
index d7b804a..5c400a9 100644
--- a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
+++ b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
@@ -23,7 +23,8 @@ import java.util.*;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.db.RowUpdateBuilder;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.schema.KeyspaceMetadata;
 import org.apache.cassandra.schema.KeyspaceParams;
 import org.apache.cassandra.schema.Tables;
@@ -86,36 +87,41 @@ public final class TraceKeyspace
                                              String command,
                                              int ttl)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Sessions, FBUtilities.timestampMicros(), ttl, sessionId)
-                                 .clustering()
-                                 .add("client", client)
-                                 .add("coordinator", FBUtilities.getBroadcastAddress())
-                                 .add("request", request)
-                                 .add("started_at", new Date(startedAt))
-                                 .add("command", command);
-
-        for (Map.Entry<String, String> entry : parameters.entrySet())
-            adder.addMapEntry("parameters", entry.getKey(), entry.getValue());
-        return adder.build();
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Sessions, sessionId);
+        builder.row()
+               .ttl(ttl)
+               .add("client", client)
+               .add("coordinator", FBUtilities.getBroadcastAddress())
+               .add("request", request)
+               .add("started_at", new Date(startedAt))
+               .add("command", command)
+               .appendAll("parameters", parameters);
+
+        return builder.buildAsMutation();
     }
 
     static Mutation makeStopSessionMutation(ByteBuffer sessionId, int elapsed, int ttl)
     {
-        return new RowUpdateBuilder(Sessions, FBUtilities.timestampMicros(), ttl, sessionId)
-               .clustering()
-               .add("duration", elapsed)
-               .build();
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Sessions, sessionId);
+        builder.row()
+               .ttl(ttl)
+               .add("duration", elapsed);
+        return builder.buildAsMutation();
     }
 
     static Mutation makeEventMutation(ByteBuffer sessionId, String message, int elapsed, String threadName, int ttl)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Events, FBUtilities.timestampMicros(), ttl, sessionId)
-                                 .clustering(UUIDGen.getTimeUUID());
-        adder.add("activity", message);
-        adder.add("source", FBUtilities.getBroadcastAddress());
-        adder.add("thread", threadName);
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Events, sessionId);
+        Row.SimpleBuilder rowBuilder = builder.row(UUIDGen.getTimeUUID())
+                                              .ttl(ttl);
+
+        rowBuilder.add("activity", message)
+                  .add("source", FBUtilities.getBroadcastAddress())
+                  .add("thread", threadName);
+
         if (elapsed >= 0)
-            adder.add("source_elapsed", elapsed);
-        return adder.build();
+            rowBuilder.add("source_elapsed", elapsed);
+
+        return builder.buildAsMutation();
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/UpdateBuilder.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/UpdateBuilder.java b/test/unit/org/apache/cassandra/UpdateBuilder.java
index 3a5fbe6..19e48f2 100644
--- a/test/unit/org/apache/cassandra/UpdateBuilder.java
+++ b/test/unit/org/apache/cassandra/UpdateBuilder.java
@@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.*;
 import org.apache.cassandra.db.partitions.*;
 import org.apache.cassandra.utils.FBUtilities;
 import org.apache.cassandra.service.StorageService;
@@ -34,32 +35,30 @@ import org.apache.cassandra.service.StorageService;
  */
 public class UpdateBuilder
 {
-    private final PartitionUpdate update;
-    private RowUpdateBuilder currentRow;
-    private long timestamp = FBUtilities.timestampMicros();
+    private final PartitionUpdate.SimpleBuilder updateBuilder;
+    private Row.SimpleBuilder currentRow;
 
-    private UpdateBuilder(CFMetaData metadata, DecoratedKey partitionKey)
+    private UpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder)
     {
-        this.update = new PartitionUpdate(metadata, partitionKey, metadata.partitionColumns(), 4);
+        this.updateBuilder = updateBuilder;
     }
 
     public static UpdateBuilder create(CFMetaData metadata, Object... partitionKey)
     {
-        return new UpdateBuilder(metadata, makeKey(metadata, partitionKey));
+        return new UpdateBuilder(PartitionUpdate.simpleBuilder(metadata, partitionKey));
     }
 
     public UpdateBuilder withTimestamp(long timestamp)
     {
-        this.timestamp = timestamp;
+        updateBuilder.timestamp(timestamp);
+        if (currentRow != null)
+            currentRow.timestamp(timestamp);
         return this;
     }
 
     public UpdateBuilder newRow(Object... clustering)
     {
-        maybeBuildCurrentRow();
-        currentRow = new RowUpdateBuilder(update, timestamp, 0);
-        if (clustering.length > 0)
-            currentRow.clustering(clustering);
+        currentRow = updateBuilder.row(clustering);
         return this;
     }
 
@@ -72,48 +71,25 @@ public class UpdateBuilder
 
     public PartitionUpdate build()
     {
-        maybeBuildCurrentRow();
-        return update;
+        return updateBuilder.build();
     }
 
     public IMutation makeMutation()
     {
-        Mutation m = new Mutation(build());
-        return update.metadata().isCounter()
+        Mutation m = updateBuilder.buildAsMutation();
+        return updateBuilder.metadata().isCounter()
              ? new CounterMutation(m, ConsistencyLevel.ONE)
              : m;
     }
 
     public void apply()
     {
-        Mutation m = new Mutation(build());
-        if (update.metadata().isCounter())
-            new CounterMutation(m, ConsistencyLevel.ONE).apply();
-        else
-            m.apply();
+        makeMutation().apply();
     }
 
     public void applyUnsafe()
     {
-        assert !update.metadata().isCounter() : "Counters have currently no applyUnsafe() option";
-        new Mutation(build()).applyUnsafe();
-    }
-
-    private void maybeBuildCurrentRow()
-    {
-        if (currentRow != null)
-        {
-            currentRow.build();
-            currentRow = null;
-        }
-    }
-
-    private static DecoratedKey makeKey(CFMetaData metadata, Object[] partitionKey)
-    {
-        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
-            return (DecoratedKey)partitionKey[0];
-
-        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
-        return metadata.decorateKey(key);
+        assert !updateBuilder.metadata().isCounter() : "Counters have currently no applyUnsafe() option";
+        updateBuilder.buildAsMutation().applyUnsafe();
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/Util.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/Util.java b/test/unit/org/apache/cassandra/Util.java
index 9689628..151d3d4 100644
--- a/test/unit/org/apache/cassandra/Util.java
+++ b/test/unit/org/apache/cassandra/Util.java
@@ -459,15 +459,37 @@ public class Util
     public static boolean equal(UnfilteredRowIterator a, UnfilteredRowIterator b)
     {
         return Objects.equals(a.columns(), b.columns())
-            && Objects.equals(a.metadata(), b.metadata())
+            && Objects.equals(a.stats(), b.stats())
+            && sameContent(a, b);
+    }
+
+    // Test equality of the iterators, but without caring too much about the "metadata" of said iterator. This is often
+    // what we want in tests. In particular, the columns() reported by the iterators will sometimes differ because they
+    // are a superset of what the iterator actually contains, and depending on the method used to get each iterator
+    // tested, one may include a defined column the other don't while there is not actual content for that column.
+    public static boolean sameContent(UnfilteredRowIterator a, UnfilteredRowIterator b)
+    {
+        return Objects.equals(a.metadata(), b.metadata())
             && Objects.equals(a.isReverseOrder(), b.isReverseOrder())
             && Objects.equals(a.partitionKey(), b.partitionKey())
             && Objects.equals(a.partitionLevelDeletion(), b.partitionLevelDeletion())
             && Objects.equals(a.staticRow(), b.staticRow())
-            && Objects.equals(a.stats(), b.stats())
             && Iterators.elementsEqual(a, b);
     }
 
+    public static boolean sameContent(Mutation a, Mutation b)
+    {
+        if (!a.key().equals(b.key()) || !a.getColumnFamilyIds().equals(b.getColumnFamilyIds()))
+            return false;
+
+        for (UUID cfId : a.getColumnFamilyIds())
+        {
+            if (!sameContent(a.getPartitionUpdate(cfId).unfilteredIterator(), b.getPartitionUpdate(cfId).unfilteredIterator()))
+                return false;
+        }
+        return true;
+    }
+
     // moved & refactored from KeyspaceTest in < 3.0
     public static void assertColumns(Row row, String... expectedColumnNames)
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/batchlog/BatchTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/batchlog/BatchTest.java b/test/unit/org/apache/cassandra/batchlog/BatchTest.java
index b7a4100..4e64ec6 100644
--- a/test/unit/org/apache/cassandra/batchlog/BatchTest.java
+++ b/test/unit/org/apache/cassandra/batchlog/BatchTest.java
@@ -28,6 +28,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.Util;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.Keyspace;
 import org.apache.cassandra.db.Mutation;
@@ -44,6 +45,7 @@ import org.apache.cassandra.utils.UUIDGen;
 
 import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class BatchTest
 {
@@ -148,6 +150,19 @@ public class BatchTest
         Iterator<Mutation> it1 = batch1.decodedMutations.iterator();
         Iterator<Mutation> it2 = batch2.decodedMutations.iterator();
         while (it1.hasNext())
-            assertEquals(it1.next().toString(), it2.next().toString());
+        {
+            // We can't simply test the equality of both mutation string representation, that is do:
+            //   assertEquals(it1.next().toString(), it2.next().toString());
+            // because when deserializing from the old format, the returned iterator will always have it's 'columns()'
+            // method return all the table columns (no matter what's the actual content), and the table contains a
+            // 'val0' column we're not setting in that test.
+            //
+            // And it's actually not easy to fix legacy deserialization as we'd need to know which columns are actually
+            // set upfront, which would require use to iterate over the whole content first, which would be costly. And
+            // as the result of 'columns()' is only meant as a superset of the columns in the iterator, we don't bother.
+            Mutation mut1 = it1.next();
+            Mutation mut2 = it2.next();
+            assertTrue(mut1 + " != " + mut2, Util.sameContent(mut1, mut2));
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/config/CFMetaDataTest.java b/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
index 6bfe5c0..8616987 100644
--- a/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
+++ b/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
@@ -150,7 +150,7 @@ public class CFMetaDataTest
         assert before.equals(after) : String.format("%n%s%n!=%n%s", before, after);
 
         // Test schema conversion
-        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
+        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros()).build();
         PartitionUpdate cfU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
         PartitionUpdate cdU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java b/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
index 632c290..0b18eec 100644
--- a/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
+++ b/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
@@ -19,10 +19,20 @@
 package org.apache.cassandra.cql3;
 
 import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
+import org.apache.cassandra.config.DatabaseDescriptor;
+
 public class CDCStatementTest extends CQLTester
 {
+    @BeforeClass
+    public static void checkConfig()
+    {
+        Assume.assumeTrue(DatabaseDescriptor.isCDCEnabled());
+    }
+
     @Test
     public void testEnableOnCreate() throws Throwable
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
deleted file mode 100644
index afe2455..0000000
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.cassandra.cql3.validation.entities;
-
-import org.junit.Test;
-
-import org.apache.cassandra.Util;
-import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.db.RowUpdateBuilder;
-import org.apache.cassandra.utils.FBUtilities;
-
-// see CASSANDRA-9743, CASSANDRA-9746
-public class RowUpdateBuilderTest extends CQLTester
-{
-    @Test
-    public void testAddListEntryDurable() throws Throwable
-    {
-        testAddListEntry(false);
-    }
-
-    @Test
-    public void testAddListEntryTransient() throws Throwable
-    {
-        testAddListEntry(true);
-    }
-
-    public void testAddListEntry(boolean skipCommitLog) throws Throwable
-    {
-        createTable("CREATE TABLE %s ("
-                    + "pk text,"
-                    + "ck text,"
-                    + "l1 list<int>,"
-                    + "l2 list<int>,"
-                    + "PRIMARY KEY ((pk), ck))");
-
-        long timestamp = FBUtilities.timestampMicros();
-
-        Mutation mutation = new Mutation(keyspace(), Util.dk("test"));
-        addToMutation("row1", timestamp, mutation);
-        addToMutation("row2", timestamp, mutation);
-
-        if (skipCommitLog)
-            mutation.applyUnsafe();
-        else
-            mutation.apply();
-
-        assertRowCount(execute("SELECT ck FROM %s"), 2);
-    }
-
-    private void addToMutation(String typeName, long timestamp, Mutation mutation)
-    {
-        RowUpdateBuilder adder = new RowUpdateBuilder(getCurrentColumnFamilyStore().metadata, timestamp, mutation)
-                                 .clustering(typeName);
-
-        for (int i = 0; i < 2; i++)
-        {
-            adder.addListEntry("l1", i)
-                 .addListEntry("l2", i);
-        }
-
-        adder.build();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java b/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
index a67e9e5..8897700 100644
--- a/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
+++ b/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
@@ -118,7 +118,7 @@ public class RecoveryManagerMissingHeaderTest
 
         CommitLog.instance.resetUnsafe(false);
 
-        Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-        Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java b/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
index 37d719e..cbc412d 100644
--- a/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
+++ b/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
@@ -188,8 +188,8 @@ public class RecoveryManagerTest
             }
             Assert.assertFalse(t.isAlive());
 
-            Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-            Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+            Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+            Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
         }
         finally
         {
@@ -220,8 +220,8 @@ public class RecoveryManagerTest
         CommitLog.instance.resetUnsafe(false);
 
         DecoratedKey dk = Util.dk("keymulti");
-        Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-        Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java b/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
new file mode 100644
index 0000000..8e71d64
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
@@ -0,0 +1,196 @@
+/*
+ * 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.cassandra.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.db.marshal.SetType;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.context.CounterContext;
+import org.apache.cassandra.db.partitions.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.ListType;
+import org.apache.cassandra.db.marshal.MapType;
+import org.apache.cassandra.utils.*;
+
+/**
+ * Convenience object to create single row updates for tests.
+ *
+ * This is a thin wrapper over the builders in SimpleBuilders for historical reasons.
+ * We could modify all the tests using this class to use the simple builders directly
+ * instead, but there is a fair amount of use so the value of such effort is unclear.
+ */
+public class RowUpdateBuilder
+{
+    private final PartitionUpdate.SimpleBuilder updateBuilder;
+    private Row.SimpleBuilder rowBuilder;
+    private boolean noRowMarker;
+
+    private List<RangeTombstone> rts = new ArrayList<>();
+
+    private RowUpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder)
+    {
+        this.updateBuilder = updateBuilder;
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
+    {
+        this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
+    {
+        this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
+    {
+        this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
+    {
+        this(PartitionUpdate.simpleBuilder(metadata, partitionKey));
+
+        this.updateBuilder.timestamp(timestamp);
+        this.updateBuilder.ttl(ttl);
+        this.updateBuilder.nowInSec(localDeletionTime);
+    }
+
+    private Row.SimpleBuilder rowBuilder()
+    {
+        // Normally, rowBuilder is created by the call to clustering(), but we allow skipping that call for an empty
+        // clustering.
+        if (rowBuilder == null)
+        {
+            rowBuilder = updateBuilder.row();
+            if (noRowMarker)
+                rowBuilder.noPrimaryKeyLivenessInfo();
+        }
+
+        return rowBuilder;
+    }
+
+    // This must be called before any addition or deletion if used.
+    public RowUpdateBuilder noRowMarker()
+    {
+        this.noRowMarker = true;
+        if (rowBuilder != null)
+            rowBuilder.noPrimaryKeyLivenessInfo();
+        return this;
+    }
+
+    public RowUpdateBuilder clustering(Object... clusteringValues)
+    {
+        assert rowBuilder == null;
+        rowBuilder = updateBuilder.row(clusteringValues);
+        if (noRowMarker)
+            rowBuilder.noPrimaryKeyLivenessInfo();
+        return this;
+    }
+
+    public Mutation build()
+    {
+        return new Mutation(buildUpdate());
+    }
+
+    public PartitionUpdate buildUpdate()
+    {
+        PartitionUpdate update = updateBuilder.build();
+        for (RangeTombstone rt : rts)
+            update.add(rt);
+        return update;
+    }
+
+    private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues)
+    {
+        assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty());
+
+        boolean isStatic = clusteringValues.length != update.metadata().comparator.size();
+        Row.Builder builder = BTreeRow.sortedBuilder();
+
+        if (isStatic)
+            builder.newRow(Clustering.STATIC_CLUSTERING);
+        else
+            builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
+        builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime)));
+
+        update.add(builder.build());
+    }
+
+    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
+    {
+        return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
+    }
+
+    public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
+    {
+        PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 0);
+        deleteRow(update, timestamp, localDeletionTime, clusteringValues);
+        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
+        // underneath (this class if for convenience, not performance)
+        return new Mutation(update.metadata().ksName, update.partitionKey()).add(update);
+    }
+
+    private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
+    {
+        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
+            return (DecoratedKey)partitionKey[0];
+
+        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
+        return metadata.decorateKey(key);
+    }
+
+    public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
+    {
+        rts.add(rt);
+        return this;
+    }
+
+    public RowUpdateBuilder addRangeTombstone(Object start, Object end)
+    {
+        updateBuilder.addRangeTombstone().start(start).end(end);
+        return this;
+    }
+
+    public RowUpdateBuilder add(String columnName, Object value)
+    {
+        rowBuilder().add(columnName, value);
+        return this;
+    }
+
+    public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
+    {
+        return add(columnDefinition.name.toString(), value);
+    }
+
+    public RowUpdateBuilder delete(String columnName)
+    {
+        rowBuilder().delete(columnName);
+        return this;
+    }
+
+    public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
+    {
+        return delete(columnDefinition.name.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java b/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
index b264553..3dd798d 100644
--- a/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
+++ b/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
@@ -172,7 +172,7 @@ public class TTLExpiryTest
 
         new RowUpdateBuilder(cfs.metadata, timestamp, 1, key)
             .add("col2", ByteBufferUtil.EMPTY_BYTE_BUFFER)
-            .addMapEntry("col8", "bar", "foo")
+            .add("col8", Collections.singletonMap("bar", "foo"))
             .delete("col1")
             .build()
             .applyUnsafe();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java b/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
index a069db1..bfa9796 100644
--- a/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
+++ b/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.cassandra.db.partition;
 
+import org.apache.cassandra.UpdateBuilder;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.cql3.CQLTester;
 import org.apache.cassandra.db.RowUpdateBuilder;
@@ -34,21 +35,17 @@ public class PartitionUpdateTest extends CQLTester
         createTable("CREATE TABLE %s (key text, clustering int, a int, s int static, PRIMARY KEY(key, clustering))");
         CFMetaData cfm = currentTableMetadata();
 
-        long timestamp = FBUtilities.timestampMicros();
-        PartitionUpdate update = new RowUpdateBuilder(cfm, timestamp, "key0").clustering(1).add("a", 1).buildUpdate();
-        Assert.assertEquals(1, update.operationCount());
-
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").buildUpdate();
-        Assert.assertEquals(0, update.operationCount());
+        UpdateBuilder builder = UpdateBuilder.create(cfm, "key0");
+        Assert.assertEquals(0, builder.build().operationCount());
+        Assert.assertEquals(1, builder.newRow(1).add("a", 1).build().operationCount());
 
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").add("s", 1).buildUpdate();
-        Assert.assertEquals(1, update.operationCount());
+        builder = UpdateBuilder.create(cfm, "key0");
+        Assert.assertEquals(1, builder.newRow().add("s", 1).build().operationCount());
 
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").add("s", 1).buildUpdate();
-        update = new RowUpdateBuilder(update, timestamp, cfm.params.defaultTimeToLive).clustering(1)
-                                                                                      .add("a", 1)
-                                                                                      .buildUpdate();
-        Assert.assertEquals(2, update.operationCount());
+        builder = UpdateBuilder.create(cfm, "key0");
+        builder.newRow().add("s", 1);
+        builder.newRow(1).add("a", 1);
+        Assert.assertEquals(2, builder.build().operationCount());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/hints/HintTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/hints/HintTest.java b/test/unit/org/apache/cassandra/hints/HintTest.java
index 658a41c..4cc2188 100644
--- a/test/unit/org/apache/cassandra/hints/HintTest.java
+++ b/test/unit/org/apache/cassandra/hints/HintTest.java
@@ -30,9 +30,7 @@ import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
 import org.apache.cassandra.Util;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.config.*;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.partitions.FilteredPartition;
 import org.apache.cassandra.db.partitions.PartitionIterator;
@@ -127,7 +125,7 @@ public class HintTest
 
         // assert that we can read the inserted partitions
         for (PartitionUpdate partition : mutation.getPartitionUpdates())
-            assertPartitionsEqual(partition, readPartition(key, partition.metadata().cfName));
+            assertPartitionsEqual(partition, readPartition(key, partition.metadata().cfName, partition.columns()));
     }
 
     @Test
@@ -152,8 +150,10 @@ public class HintTest
         assertNoPartitions(key, TABLE1);
 
         // TABLE0 and TABLE2 updates should have been applied successfully
-        assertPartitionsEqual(mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE0)), readPartition(key, TABLE0));
-        assertPartitionsEqual(mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE2)), readPartition(key, TABLE2));
+        PartitionUpdate upd0 = mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE0));
+        assertPartitionsEqual(upd0, readPartition(key, TABLE0, upd0.columns()));
+        PartitionUpdate upd2 = mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE2));
+        assertPartitionsEqual(upd2, readPartition(key, TABLE2, upd2.columns()));
     }
 
     @Test
@@ -296,40 +296,44 @@ public class HintTest
 
     private static Mutation createMutation(String key, long now)
     {
-        Mutation mutation = new Mutation(KEYSPACE, dk(key));
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(KEYSPACE, dk(key));
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE0), now, mutation)
-            .clustering("column0")
-            .add("val", "value0")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE0))
+               .timestamp(now)
+               .row("column0")
+               .add("val", "value0");
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE1), now + 1, mutation)
-            .clustering("column1")
-            .add("val", "value1")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE1))
+               .timestamp(now + 1)
+               .row("column1")
+               .add("val", "value1");
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE2), now + 2, mutation)
-            .clustering("column2")
-            .add("val", "value2")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE2))
+               .timestamp(now + 2)
+               .row("column2")
+               .add("val", "value2");
 
-        return mutation;
+        return builder.build();
     }
 
-    private static SinglePartitionReadCommand cmd(String key, String table)
+    private static ColumnFamilyStore cfs(String table)
     {
-        CFMetaData meta = Schema.instance.getCFMetaData(KEYSPACE, table);
-        return SinglePartitionReadCommand.fullPartitionRead(meta, FBUtilities.nowInSeconds(), bytes(key));
+        return Schema.instance.getColumnFamilyStoreInstance(Schema.instance.getCFMetaData(KEYSPACE, table).cfId);
     }
 
-    private static FilteredPartition readPartition(String key, String table)
+    private static FilteredPartition readPartition(String key, String table, PartitionColumns columns)
     {
-        return Util.getOnlyPartition(cmd(key, table));
+        String[] columnNames = new String[columns.size()];
+        int i = 0;
+        for (ColumnDefinition column : columns)
+            columnNames[i++] = column.name.toString();
+
+        return Util.getOnlyPartition(Util.cmd(cfs(table), key).columns(columnNames).build());
     }
 
     private static void assertNoPartitions(String key, String table)
     {
-        ReadCommand cmd = cmd(key, table);
+        ReadCommand cmd = Util.cmd(cfs(table), key).build();
 
         try (ReadExecutionController executionController = cmd.executionController();
              PartitionIterator iterator = cmd.executeInternal(executionController))

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java b/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
index cc97df0..87abdac 100644
--- a/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
+++ b/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
@@ -27,6 +27,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.Util;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.*;
@@ -159,7 +160,7 @@ public class LegacyHintsMigratorTest
 
                 assertEquals(timestamp, hint.creationTime);
                 assertEquals(ttl, hint.gcgs);
-                assertMutationsEqual(mutation, hint.mutation);
+                assertTrue(mutation + " != " + hint.mutation, Util.sameContent(mutation, hint.mutation));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/DefsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/DefsTest.java b/test/unit/org/apache/cassandra/schema/DefsTest.java
index e9980f6..d4ac1dc 100644
--- a/test/unit/org/apache/cassandra/schema/DefsTest.java
+++ b/test/unit/org/apache/cassandra/schema/DefsTest.java
@@ -498,7 +498,7 @@ public class DefsTest
     public void testDropIndex() throws ConfigurationException
     {
         // persist keyspace definition in the system keyspace
-        SchemaKeyspace.makeCreateKeyspaceMutation(Schema.instance.getKSMetaData(KEYSPACE6), FBUtilities.timestampMicros()).applyUnsafe();
+        SchemaKeyspace.makeCreateKeyspaceMutation(Schema.instance.getKSMetaData(KEYSPACE6), FBUtilities.timestampMicros()).build().applyUnsafe();
         ColumnFamilyStore cfs = Keyspace.open(KEYSPACE6).getColumnFamilyStore(TABLE1i);
         String indexName = "birthdate_key_index";
 


[03/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
index 2de671c..72441cd 100644
--- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
+++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
@@ -34,6 +34,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.FieldIdentifier;
 import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.Row;
 import org.apache.cassandra.db.marshal.*;
 import org.apache.cassandra.index.TargetParser;
 import org.apache.cassandra.thrift.ThriftConversion;
@@ -564,35 +565,40 @@ public class LegacySchemaMigratorTest
         setLegacyIndexStatus(keyspace);
     }
 
-    private static Mutation makeLegacyCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    private static DecoratedKey decorate(CFMetaData metadata, Object value)
     {
-        // Note that because Keyspaces is a COMPACT TABLE, we're really only setting static columns internally and shouldn't set any clustering.
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyKeyspaces, timestamp, keyspace.name);
+        return metadata.decorateKey(((AbstractType)metadata.getKeyValidator()).decompose(value));
+    }
 
-        adder.add("durable_writes", keyspace.params.durableWrites)
-             .add("strategy_class", keyspace.params.replication.klass.getName())
-             .add("strategy_options", json(keyspace.params.replication.options));
+    private static Mutation makeLegacyCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    {
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(SystemKeyspace.NAME, decorate(SystemKeyspace.LegacyKeyspaces, keyspace.name))
+                                                 .timestamp(timestamp);
 
-        Mutation mutation = adder.build();
+        builder.update(SystemKeyspace.LegacyKeyspaces)
+               .row()
+               .add("durable_writes", keyspace.params.durableWrites)
+               .add("strategy_class", keyspace.params.replication.klass.getName())
+               .add("strategy_options", json(keyspace.params.replication.options));
 
-        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation));
-        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation));
-        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation));
-        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation));
+        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, true, builder));
+        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, builder));
+        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, builder));
+        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, builder));
 
-        return mutation;
+        return builder.build();
     }
 
     /*
      * Serializing tables
      */
 
-    private static void addTableToSchemaMutation(CFMetaData table, long timestamp, boolean withColumnsAndTriggers, Mutation mutation)
+    private static void addTableToSchemaMutation(CFMetaData table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder)
     {
         // For property that can be null (and can be changed), we insert tombstones, to make sure
         // we don't keep a property the user has removed
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyColumnfamilies, timestamp, mutation)
-                                 .clustering(table.cfName);
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyColumnfamilies)
+                                         .row(table.cfName);
 
         adder.add("cf_id", table.cfId)
              .add("type", table.isSuper() ? "Super" : "Standard");
@@ -625,12 +631,14 @@ public class LegacySchemaMigratorTest
              .add("read_repair_chance", table.params.readRepairChance)
              .add("speculative_retry", table.params.speculativeRetry.toString());
 
+        Map<String, Long> dropped = new HashMap<>();
         for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry : table.getDroppedColumns().entrySet())
         {
             String name = UTF8Type.instance.getString(entry.getKey());
             CFMetaData.DroppedColumn column = entry.getValue();
-            adder.addMapEntry("dropped_columns", name, column.droppedTime);
+            dropped.put(name, column.droppedTime);
         }
+        adder.add("dropped_columns", dropped);
 
         adder.add("is_dense", table.isDense());
 
@@ -639,13 +647,11 @@ public class LegacySchemaMigratorTest
         if (withColumnsAndTriggers)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (TriggerMetadata trigger : table.getTriggers())
-                addTriggerToSchemaMutation(table, trigger, timestamp, mutation);
+                addTriggerToSchemaMutation(table, trigger, builder);
         }
-
-        adder.build();
     }
 
     private static String cachingToString(CachingParams caching)
@@ -655,14 +661,14 @@ public class LegacySchemaMigratorTest
                       caching.rowsPerPartitionAsString());
     }
 
-    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
         // We need to special case pk-only dense tables. See CASSANDRA-9874.
         String name = table.isDense() && column.kind == ColumnDefinition.Kind.REGULAR && column.type instanceof EmptyType
                     ? ""
                     : column.name.toString();
 
-        final RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyColumns, timestamp, mutation).clustering(table.cfName, name);
+        final Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyColumns).row(table.cfName, name);
 
         adder.add("validator", column.type.toString())
              .add("type", serializeKind(column.kind, table.isDense()))
@@ -682,8 +688,6 @@ public class LegacySchemaMigratorTest
             adder.add("index_type", null);
             adder.add("index_options", null);
         }
-
-        adder.build();
     }
 
     private static Optional<IndexMetadata> findIndexForColumn(Indexes indexes,
@@ -712,71 +716,67 @@ public class LegacySchemaMigratorTest
         return kind.toString().toLowerCase();
     }
 
-    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        new RowUpdateBuilder(SystemKeyspace.LegacyTriggers, timestamp, mutation)
-            .clustering(table.cfName, trigger.name)
-            .addMapEntry("trigger_options", "class", trigger.classOption)
-            .build();
+        builder.update(SystemKeyspace.LegacyTriggers)
+               .row(table.cfName, trigger.name)
+               .add("trigger_options", Collections.singletonMap("class", trigger.classOption));
     }
 
     /*
      * Serializing types
      */
 
-    private static void addTypeToSchemaMutation(UserType type, long timestamp, Mutation mutation)
+    private static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyUsertypes, timestamp, mutation)
-                                 .clustering(type.getNameAsString());
-
-        adder.resetCollection("field_names")
-             .resetCollection("field_types");
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyUsertypes)
+                                         .row(type.getNameAsString());
 
+        List<String> names = new ArrayList<>();
+        List<String> types = new ArrayList<>();
         for (int i = 0; i < type.size(); i++)
         {
-            adder.addListEntry("field_names", type.fieldName(i).toString())
-                 .addListEntry("field_types", type.fieldType(i).toString());
+            names.add(type.fieldName(i).toString());
+            types.add(type.fieldType(i).toString());
         }
 
-        adder.build();
+        adder.add("field_names", names)
+             .add("field_types", types);
     }
 
     /*
      * Serializing functions
      */
 
-    private static void addFunctionToSchemaMutation(UDFunction function, long timestamp, Mutation mutation)
+    private static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyFunctions, timestamp, mutation)
-                                 .clustering(function.name().name, functionSignatureWithTypes(function));
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyFunctions)
+                                         .row(function.name().name, functionSignatureWithTypes(function));
 
         adder.add("body", function.body())
              .add("language", function.language())
              .add("return_type", function.returnType().toString())
              .add("called_on_null_input", function.isCalledOnNullInput());
 
-        adder.resetCollection("argument_names")
-             .resetCollection("argument_types");
-
+        List<ByteBuffer> names = new ArrayList<>();
+        List<String> types = new ArrayList<>();
         for (int i = 0; i < function.argNames().size(); i++)
         {
-            adder.addListEntry("argument_names", function.argNames().get(i).bytes)
-                 .addListEntry("argument_types", function.argTypes().get(i).toString());
+            names.add(function.argNames().get(i).bytes);
+            types.add(function.argTypes().get(i).toString());
         }
-
-        adder.build();
+        adder.add("argument_names", names)
+             .add("argument_types", types);
     }
 
     /*
      * Serializing aggregates
      */
 
-    private static void addAggregateToSchemaMutation(UDAggregate aggregate, long timestamp, Mutation mutation)
+    private static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyAggregates, timestamp, mutation)
-                                 .clustering(aggregate.name().name, functionSignatureWithTypes(aggregate));
-
-        adder.resetCollection("argument_types");
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyAggregates)
+                                 .row(aggregate.name().name, functionSignatureWithTypes(aggregate));
 
         adder.add("return_type", aggregate.returnType().toString())
              .add("state_func", aggregate.stateFunction().name().name);
@@ -788,10 +788,11 @@ public class LegacySchemaMigratorTest
         if (aggregate.initialCondition() != null)
             adder.add("initcond", aggregate.initialCondition());
 
+        List<String> types = new ArrayList<>();
         for (AbstractType<?> argType : aggregate.argTypes())
-            adder.addListEntry("argument_types", argType.toString());
+            types.add(argType.toString());
 
-        adder.build();
+        adder.add("argument_types", types);
     }
 
     // We allow method overloads, so a function is not uniquely identified by its name only, but

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java b/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
index b2e3535..d686fdb 100644
--- a/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
+++ b/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
@@ -164,7 +164,7 @@ public class SchemaKeyspaceTest
     private static void updateTable(String keyspace, CFMetaData oldTable, CFMetaData newTable)
     {
         KeyspaceMetadata ksm = Schema.instance.getKeyspaceInstance(keyspace).getMetadata();
-        Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros());
+        Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros()).build();
         SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
     }
 
@@ -173,7 +173,7 @@ public class SchemaKeyspaceTest
         CFMetaData table = CFMetaData.compile(cql, keyspace);
 
         KeyspaceMetadata ksm = KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.of(table));
-        Mutation mutation = SchemaKeyspace.makeCreateTableMutation(ksm, table, FBUtilities.timestampMicros());
+        Mutation mutation = SchemaKeyspace.makeCreateTableMutation(ksm, table, FBUtilities.timestampMicros()).build();
         SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
     }
 
@@ -187,7 +187,7 @@ public class SchemaKeyspaceTest
         assert before.equals(after) : String.format("%n%s%n!=%n%s", before, after);
 
         // Test schema conversion
-        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
+        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros()).build();
         PartitionUpdate serializedCf = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
         PartitionUpdate serializedCD = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/service/DataResolverTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/service/DataResolverTest.java b/test/unit/org/apache/cassandra/service/DataResolverTest.java
index b20dfc0..b48512f 100644
--- a/test/unit/org/apache/cassandra/service/DataResolverTest.java
+++ b/test/unit/org/apache/cassandra/service/DataResolverTest.java
@@ -245,7 +245,7 @@ public class DataResolverTest
 
         RangeTombstone tombstone1 = tombstone("1", "11", 1, nowInSec);
         RangeTombstone tombstone2 = tombstone("3", "31", 1, nowInSec);
-        PartitionUpdate update =new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1)
+        PartitionUpdate update = new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1)
                                                                                   .addRangeTombstone(tombstone2)
                                                                                   .buildUpdate();
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
index 6be880c..879f44c 100644
--- a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
+++ b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
@@ -363,7 +363,7 @@ public class StreamingTransferTest
 
 
         updates = new RowUpdateBuilder(cfs.metadata, FBUtilities.timestampMicros() + 1, key);
-        updates.addRangeTombstone(Slice.make(comparator.make(5), comparator.make(7)))
+        updates.addRangeTombstone(5, 7)
                 .build()
                 .apply();
 


[07/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index 8e3961e..7a90dab 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -266,8 +266,8 @@ public final class SchemaKeyspace
         }
 
         // (+1 to timestamp to make sure we don't get shadowed by the tombstones we just added)
-        makeCreateKeyspaceMutation(system, timestamp + 1).apply();
-        makeCreateKeyspaceMutation(schema, timestamp + 1).apply();
+        makeCreateKeyspaceMutation(system, timestamp + 1).build().apply();
+        makeCreateKeyspaceMutation(schema, timestamp + 1).build().apply();
     }
 
     public static void truncate()
@@ -377,11 +377,6 @@ public final class SchemaKeyspace
         }
     }
 
-    private static ByteBuffer getSchemaKSKey(String ksName)
-    {
-        return AsciiType.instance.fromString(ksName);
-    }
-
     private static boolean isSystemKeyspaceSchemaPartition(DecoratedKey partitionKey)
     {
         return Schema.isSystemKeyspace(UTF8Type.instance.compose(partitionKey.getKey()));
@@ -391,152 +386,152 @@ public final class SchemaKeyspace
      * Schema entities to mutations
      */
 
-    public static Mutation makeCreateKeyspaceMutation(String name, KeyspaceParams params, long timestamp)
+    private static DecoratedKey decorate(CFMetaData metadata, Object value)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Keyspaces, timestamp, name).clustering();
-        return adder.add(KeyspaceParams.Option.DURABLE_WRITES.toString(), params.durableWrites)
-                    .frozenMap(KeyspaceParams.Option.REPLICATION.toString(), params.replication.asMap())
-                    .build();
+        return metadata.decorateKey(((AbstractType)metadata.getKeyValidator()).decompose(value));
     }
 
-    public static Mutation makeCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateKeyspaceMutation(String name, KeyspaceParams params, long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(Keyspaces.ksName, decorate(Keyspaces, name))
+                                                 .timestamp(timestamp);
 
-        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation));
-        keyspace.views.forEach(view -> addViewToSchemaMutation(view, timestamp, true, mutation));
-        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation));
-        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation));
-        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation));
+        builder.update(Keyspaces)
+               .row()
+               .add(KeyspaceParams.Option.DURABLE_WRITES.toString(), params.durableWrites)
+               .add(KeyspaceParams.Option.REPLICATION.toString(), params.replication.asMap());
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeDropKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
     {
-        int nowInSec = FBUtilities.nowInSeconds();
-        Mutation mutation = new Mutation(NAME, Keyspaces.decorateKey(getSchemaKSKey(keyspace.name)));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        for (CFMetaData schemaTable : ALL_TABLE_METADATA)
-            mutation.add(PartitionUpdate.fullPartitionDelete(schemaTable, mutation.key(), timestamp, nowInSec));
+        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, true, builder));
+        keyspace.views.forEach(view -> addViewToSchemaMutation(view, true, builder));
+        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, builder));
+        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, builder));
+        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, builder));
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
+    public static Mutation.SimpleBuilder makeDropKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
     {
-        // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addTypeToSchemaMutation(type, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(NAME, decorate(Keyspaces, keyspace.name))
+                                                 .timestamp(timestamp);
+
+        for (CFMetaData schemaTable : ALL_TABLE_METADATA)
+            builder.update(schemaTable).delete();
+
+        return builder;
     }
 
-    static void addTypeToSchemaMutation(UserType type, long timestamp, Mutation mutation)
+    public static Mutation.SimpleBuilder makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Types, timestamp, mutation)
-                                 .clustering(type.getNameAsString())
-                                 .frozenList("field_names", type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList()))
-                                 .frozenList("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList()));
-
-        adder.build();
+        // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addTypeToSchemaMutation(type, builder);
+        return builder;
     }
 
-    private static String bbToString(ByteBuffer bb)
+    static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder mutation)
     {
-        try
-        {
-            return ByteBufferUtil.string(bb);
-        }
-        catch (CharacterCodingException e)
-        {
-            throw new RuntimeException(e);
-        }
+        mutation.update(Types)
+                .row(type.getNameAsString())
+                .add("field_names", type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList()))
+                .add("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList()));
     }
 
-    public static Mutation dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
+    public static Mutation.SimpleBuilder dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Types, timestamp, mutation, type.name);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Types).row(type.name).delete();
+        return builder;
     }
 
-    public static Mutation makeCreateTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addTableToSchemaMutation(table, timestamp, true, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addTableToSchemaMutation(table, true, builder);
+        return builder;
     }
 
-    static void addTableToSchemaMutation(CFMetaData table, long timestamp, boolean withColumnsAndTriggers, Mutation mutation)
+    static void addTableToSchemaMutation(CFMetaData table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Tables, timestamp, mutation).clustering(table.cfName);
-
-        addTableParamsToSchemaMutation(table.params, adder);
+        Row.SimpleBuilder rowBuilder = builder.update(Tables)
+                                              .row(table.cfName)
+                                              .add("id", table.cfId)
+                                              .add("flags", CFMetaData.flagsToStrings(table.flags()));
 
-        adder.add("id", table.cfId)
-             .frozenSet("flags", CFMetaData.flagsToStrings(table.flags()))
-             .build();
+        addTableParamsToRowBuilder(table.params, rowBuilder);
 
         if (withColumnsAndTriggers)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values())
-                addDroppedColumnToSchemaMutation(table, column, timestamp, mutation);
+                addDroppedColumnToSchemaMutation(table, column, builder);
 
             for (TriggerMetadata trigger : table.getTriggers())
-                addTriggerToSchemaMutation(table, trigger, timestamp, mutation);
+                addTriggerToSchemaMutation(table, trigger, builder);
 
             for (IndexMetadata index : table.getIndexes())
-                addIndexToSchemaMutation(table, index, timestamp, mutation);
+                addIndexToSchemaMutation(table, index, builder);
         }
     }
 
-    private static void addTableParamsToSchemaMutation(TableParams params, RowUpdateBuilder adder)
+    private static void addTableParamsToRowBuilder(TableParams params, Row.SimpleBuilder builder)
     {
-        adder.add("bloom_filter_fp_chance", params.bloomFilterFpChance)
-             .add("comment", params.comment)
-             .add("dclocal_read_repair_chance", params.dcLocalReadRepairChance)
-             .add("default_time_to_live", params.defaultTimeToLive)
-             .add("gc_grace_seconds", params.gcGraceSeconds)
-             .add("max_index_interval", params.maxIndexInterval)
-             .add("memtable_flush_period_in_ms", params.memtableFlushPeriodInMs)
-             .add("min_index_interval", params.minIndexInterval)
-             .add("read_repair_chance", params.readRepairChance)
-             .add("speculative_retry", params.speculativeRetry.toString())
-             .add("crc_check_chance", params.crcCheckChance)
-             .frozenMap("caching", params.caching.asMap())
-             .frozenMap("compaction", params.compaction.asMap())
-             .frozenMap("compression", params.compression.asMap())
-             .frozenMap("extensions", params.extensions)
-             .add("cdc", params.cdc);
+        builder.add("bloom_filter_fp_chance", params.bloomFilterFpChance)
+               .add("comment", params.comment)
+               .add("dclocal_read_repair_chance", params.dcLocalReadRepairChance)
+               .add("default_time_to_live", params.defaultTimeToLive)
+               .add("gc_grace_seconds", params.gcGraceSeconds)
+               .add("max_index_interval", params.maxIndexInterval)
+               .add("memtable_flush_period_in_ms", params.memtableFlushPeriodInMs)
+               .add("min_index_interval", params.minIndexInterval)
+               .add("read_repair_chance", params.readRepairChance)
+               .add("speculative_retry", params.speculativeRetry.toString())
+               .add("crc_check_chance", params.crcCheckChance)
+               .add("caching", params.caching.asMap())
+               .add("compaction", params.compaction.asMap())
+               .add("compression", params.compression.asMap())
+               .add("extensions", params.extensions);
+
+        // Only add CDC-enabled flag to schema if it's enabled on the node. This is to work around RTE's post-8099 if a 3.8+
+        // node sends table schema to a < 3.8 versioned node with an unknown column.
+        if (DatabaseDescriptor.isCDCEnabled())
+            builder.add("cdc", params.cdc);
     }
 
-    public static Mutation makeUpdateTableMutation(KeyspaceMetadata keyspace,
-                                                   CFMetaData oldTable,
-                                                   CFMetaData newTable,
-                                                   long timestamp)
+    public static Mutation.SimpleBuilder makeUpdateTableMutation(KeyspaceMetadata keyspace,
+                                                                 CFMetaData oldTable,
+                                                                 CFMetaData newTable,
+                                                                 long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        addTableToSchemaMutation(newTable, timestamp, false, mutation);
+        addTableToSchemaMutation(newTable, false, builder);
 
         MapDifference<ByteBuffer, ColumnDefinition> columnDiff = Maps.difference(oldTable.getColumnMetadata(),
                                                                                  newTable.getColumnMetadata());
 
         // columns that are no longer needed
         for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values())
-            dropColumnFromSchemaMutation(oldTable, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(oldTable, column, builder);
 
         // newly added columns
         for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values())
-            addColumnToSchemaMutation(newTable, column, timestamp, mutation);
+            addColumnToSchemaMutation(newTable, column, builder);
 
         // old columns with updated attributes
         for (ByteBuffer name : columnDiff.entriesDiffering().keySet())
-            addColumnToSchemaMutation(newTable, newTable.getColumnDefinition(name), timestamp, mutation);
+            addColumnToSchemaMutation(newTable, newTable.getColumnDefinition(name), builder);
 
         // dropped columns
         MapDifference<ByteBuffer, CFMetaData.DroppedColumn> droppedColumnDiff =
@@ -544,38 +539,38 @@ public final class SchemaKeyspace
 
         // newly dropped columns
         for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values())
-            addDroppedColumnToSchemaMutation(newTable, column, timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newTable, column, builder);
 
         // columns added then dropped again
         for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet())
-            addDroppedColumnToSchemaMutation(newTable, newTable.getDroppedColumns().get(name), timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newTable, newTable.getDroppedColumns().get(name), builder);
 
         MapDifference<String, TriggerMetadata> triggerDiff = triggersDiff(oldTable.getTriggers(), newTable.getTriggers());
 
         // dropped triggers
         for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnLeft().values())
-            dropTriggerFromSchemaMutation(oldTable, trigger, timestamp, mutation);
+            dropTriggerFromSchemaMutation(oldTable, trigger, builder);
 
         // newly created triggers
         for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnRight().values())
-            addTriggerToSchemaMutation(newTable, trigger, timestamp, mutation);
+            addTriggerToSchemaMutation(newTable, trigger, builder);
 
         MapDifference<String, IndexMetadata> indexesDiff = indexesDiff(oldTable.getIndexes(),
                                                                        newTable.getIndexes());
 
         // dropped indexes
         for (IndexMetadata index : indexesDiff.entriesOnlyOnLeft().values())
-            dropIndexFromSchemaMutation(oldTable, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(oldTable, index, builder);
 
         // newly created indexes
         for (IndexMetadata index : indexesDiff.entriesOnlyOnRight().values())
-            addIndexToSchemaMutation(newTable, index, timestamp, mutation);
+            addIndexToSchemaMutation(newTable, index, builder);
 
         // updated indexes need to be updated
         for (MapDifference.ValueDifference<IndexMetadata> diff : indexesDiff.entriesDiffering().values())
-            addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), timestamp, mutation);
+            addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), builder);
 
-        return mutation;
+        return builder;
     }
 
     private static MapDifference<String, IndexMetadata> indexesDiff(Indexes before, Indexes after)
@@ -600,144 +595,137 @@ public final class SchemaKeyspace
         return Maps.difference(beforeMap, afterMap);
     }
 
-    public static Mutation makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
+    public static Mutation.SimpleBuilder makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        RowUpdateBuilder.deleteRow(Tables, timestamp, mutation, table.cfName);
+        builder.update(Tables).row(table.cfName).delete();
 
         for (ColumnDefinition column : table.allColumns())
-            dropColumnFromSchemaMutation(table, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(table, column, builder);
 
         for (TriggerMetadata trigger : table.getTriggers())
-            dropTriggerFromSchemaMutation(table, trigger, timestamp, mutation);
+            dropTriggerFromSchemaMutation(table, trigger, builder);
 
         for (IndexMetadata index : table.getIndexes())
-            dropIndexFromSchemaMutation(table, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(table, index, builder);
 
-        return mutation;
+        return builder;
     }
 
-    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Columns, timestamp, mutation).clustering(table.cfName, column.name.toString());
-
         AbstractType<?> type = column.type;
         if (type instanceof ReversedType)
             type = ((ReversedType) type).baseType;
 
-        adder.add("column_name_bytes", column.name.bytes)
-             .add("kind", column.kind.toString().toLowerCase())
-             .add("position", column.position())
-             .add("clustering_order", column.clusteringOrder().toString().toLowerCase())
-             .add("type", type.asCQL3Type().toString())
-             .build();
+        builder.update(Columns)
+               .row(table.cfName, column.name.toString())
+               .add("column_name_bytes", column.name.bytes)
+               .add("kind", column.kind.toString().toLowerCase())
+               .add("position", column.position())
+               .add("clustering_order", column.clusteringOrder().toString().toLowerCase())
+               .add("type", type.asCQL3Type().toString());
     }
 
-    private static void dropColumnFromSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void dropColumnFromSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
         // Note: we do want to use name.toString(), not name.bytes directly for backward compatibility (For CQL3, this won't make a difference).
-        RowUpdateBuilder.deleteRow(Columns, timestamp, mutation, table.cfName, column.name.toString());
+        builder.update(Columns).row(table.cfName, column.name.toString()).delete();
     }
 
-    private static void addDroppedColumnToSchemaMutation(CFMetaData table, CFMetaData.DroppedColumn column, long timestamp, Mutation mutation)
+    private static void addDroppedColumnToSchemaMutation(CFMetaData table, CFMetaData.DroppedColumn column, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(DroppedColumns, timestamp, mutation).clustering(table.cfName, column.name);
-
-        adder.add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime)))
-             .add("type", expandUserTypes(column.type).asCQL3Type().toString())
-             .build();
+        builder.update(DroppedColumns)
+               .row(table.cfName, column.name)
+               .add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime)))
+               .add("type", expandUserTypes(column.type).asCQL3Type().toString());
     }
 
-    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        new RowUpdateBuilder(Triggers, timestamp, mutation)
-            .clustering(table.cfName, trigger.name)
-            .frozenMap("options", Collections.singletonMap("class", trigger.classOption))
-            .build();
+        builder.update(Triggers)
+               .row(table.cfName, trigger.name)
+               .add("options", Collections.singletonMap("class", trigger.classOption));
     }
 
-    private static void dropTriggerFromSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void dropTriggerFromSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder.deleteRow(Triggers, timestamp, mutation, table.cfName, trigger.name);
+        builder.update(Triggers).row(table.cfName, trigger.name).delete();
     }
 
-    public static Mutation makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addViewToSchemaMutation(view, timestamp, true, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addViewToSchemaMutation(view, true, builder);
+        return builder;
     }
 
-    private static void addViewToSchemaMutation(ViewDefinition view, long timestamp, boolean includeColumns, Mutation mutation)
+    private static void addViewToSchemaMutation(ViewDefinition view, boolean includeColumns, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder builder = new RowUpdateBuilder(Views, timestamp, mutation)
-            .clustering(view.viewName);
-
         CFMetaData table = view.metadata;
+        Row.SimpleBuilder rowBuilder = builder.update(Views)
+                                              .row(view.viewName)
+                                              .add("include_all_columns", view.includeAllColumns)
+                                              .add("base_table_id", view.baseTableId)
+                                              .add("base_table_name", view.baseTableMetadata().cfName)
+                                              .add("where_clause", view.whereClause)
+                                              .add("id", table.cfId);
 
-        builder.add("include_all_columns", view.includeAllColumns)
-               .add("base_table_id", view.baseTableId)
-               .add("base_table_name", view.baseTableMetadata().cfName)
-               .add("where_clause", view.whereClause)
-               .add("id", table.cfId);
-
-        addTableParamsToSchemaMutation(table.params, builder);
+        addTableParamsToRowBuilder(table.params, rowBuilder);
 
         if (includeColumns)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values())
-                addDroppedColumnToSchemaMutation(table, column, timestamp, mutation);
+                addDroppedColumnToSchemaMutation(table, column, builder);
         }
-
-        builder.build();
     }
 
-    public static Mutation makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
+    public static Mutation.SimpleBuilder makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        RowUpdateBuilder.deleteRow(Views, timestamp, mutation, view.viewName);
+        builder.update(Views).row(view.viewName).delete();
 
         CFMetaData table = view.metadata;
         for (ColumnDefinition column : table.allColumns())
-            dropColumnFromSchemaMutation(table, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(table, column, builder);
 
         for (IndexMetadata index : table.getIndexes())
-            dropIndexFromSchemaMutation(table, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(table, index, builder);
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeUpdateViewMutation(KeyspaceMetadata keyspace,
-                                                  ViewDefinition oldView,
-                                                  ViewDefinition newView,
-                                                  long timestamp)
+    public static Mutation.SimpleBuilder makeUpdateViewMutation(KeyspaceMetadata keyspace,
+                                                                ViewDefinition oldView,
+                                                                ViewDefinition newView,
+                                                                long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        addViewToSchemaMutation(newView, timestamp, false, mutation);
+        addViewToSchemaMutation(newView, false, builder);
 
         MapDifference<ByteBuffer, ColumnDefinition> columnDiff = Maps.difference(oldView.metadata.getColumnMetadata(),
                                                                                  newView.metadata.getColumnMetadata());
 
         // columns that are no longer needed
         for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values())
-            dropColumnFromSchemaMutation(oldView.metadata, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(oldView.metadata, column, builder);
 
         // newly added columns
         for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values())
-            addColumnToSchemaMutation(newView.metadata, column, timestamp, mutation);
+            addColumnToSchemaMutation(newView.metadata, column, builder);
 
         // old columns with updated attributes
         for (ByteBuffer name : columnDiff.entriesDiffering().keySet())
-            addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), timestamp, mutation);
+            addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), builder);
 
         // dropped columns
         MapDifference<ByteBuffer, CFMetaData.DroppedColumn> droppedColumnDiff =
@@ -745,63 +733,68 @@ public final class SchemaKeyspace
 
         // newly dropped columns
         for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values())
-            addDroppedColumnToSchemaMutation(oldView.metadata, column, timestamp, mutation);
+            addDroppedColumnToSchemaMutation(oldView.metadata, column, builder);
 
         // columns added then dropped again
         for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet())
-            addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), builder);
 
-        return mutation;
+        return builder;
     }
 
     private static void addIndexToSchemaMutation(CFMetaData table,
                                                  IndexMetadata index,
-                                                 long timestamp,
-                                                 Mutation mutation)
+                                                 Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder builder = new RowUpdateBuilder(Indexes, timestamp, mutation).clustering(table.cfName, index.name);
-
-        builder.add("kind", index.kind.toString());
-        builder.frozenMap("options", index.options);
-        builder.build();
+        builder.update(Indexes)
+               .row(table.cfName, index.name)
+               .add("kind", index.kind.toString())
+               .add("options", index.options);
     }
 
     private static void dropIndexFromSchemaMutation(CFMetaData table,
                                                     IndexMetadata index,
-                                                    long timestamp,
-                                                    Mutation mutation)
+                                                    Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder.deleteRow(Indexes, timestamp, mutation, table.cfName, index.name);
+        builder.update(Indexes).row(table.cfName, index.name).delete();
     }
 
     private static void addUpdatedIndexToSchemaMutation(CFMetaData table,
                                                         IndexMetadata index,
-                                                        long timestamp,
-                                                        Mutation mutation)
+                                                        Mutation.SimpleBuilder builder)
     {
-        addIndexToSchemaMutation(table, index, timestamp, mutation);
+        addIndexToSchemaMutation(table, index, builder);
     }
 
-    public static Mutation makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addFunctionToSchemaMutation(function, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addFunctionToSchemaMutation(function, builder);
+        return builder;
     }
 
-    static void addFunctionToSchemaMutation(UDFunction function, long timestamp, Mutation mutation)
+    static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder =
-            new RowUpdateBuilder(Functions, timestamp, mutation).clustering(function.name().name, functionArgumentsList(function));
-
-        adder.add("body", function.body())
-             .add("language", function.language())
-             .add("return_type", function.returnType().asCQL3Type().toString())
-             .add("called_on_null_input", function.isCalledOnNullInput())
-             .frozenList("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
+        builder.update(Functions)
+               .row(function.name().name, functionArgumentsList(function))
+               .add("body", function.body())
+               .add("language", function.language())
+               .add("return_type", function.returnType().asCQL3Type().toString())
+               .add("called_on_null_input", function.isCalledOnNullInput())
+               .add("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
+    }
 
-        adder.build();
+    private static String bbToString(ByteBuffer bb)
+    {
+        try
+        {
+            return ByteBufferUtil.string(bb);
+        }
+        catch (CharacterCodingException e)
+        {
+            throw new RuntimeException(e);
+        }
     }
 
     private static List<String> functionArgumentsList(AbstractFunction fun)
@@ -813,42 +806,42 @@ public final class SchemaKeyspace
                   .collect(toList());
     }
 
-    public static Mutation makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
+    public static Mutation.SimpleBuilder makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Functions, timestamp, mutation, function.name().name, functionArgumentsList(function));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Functions).row(function.name().name, functionArgumentsList(function)).delete();
+        return builder;
     }
 
-    public static Mutation makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addAggregateToSchemaMutation(aggregate, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addAggregateToSchemaMutation(aggregate, builder);
+        return builder;
     }
 
-    static void addAggregateToSchemaMutation(UDAggregate aggregate, long timestamp, Mutation mutation)
+    static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder =
-            new RowUpdateBuilder(Aggregates, timestamp, mutation) .clustering(aggregate.name().name, functionArgumentsList(aggregate));
-
-        adder.add("return_type", aggregate.returnType().asCQL3Type().toString())
-             .add("state_func", aggregate.stateFunction().name().name)
-             .add("state_type", aggregate.stateType().asCQL3Type().toString())
-             .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
-             .add("initcond", aggregate.initialCondition() != null
-                              // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
-                              ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
-                              : null)
-             .build();
+        builder.update(Aggregates)
+               .row(aggregate.name().name, functionArgumentsList(aggregate))
+               .add("return_type", aggregate.returnType().asCQL3Type().toString())
+               .add("state_func", aggregate.stateFunction().name().name)
+               .add("state_type", aggregate.stateType().asCQL3Type().toString())
+               .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
+               .add("initcond", aggregate.initialCondition() != null
+                                // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
+                                ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
+                                : null);
     }
 
-    public static Mutation makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
+    public static Mutation.SimpleBuilder makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Aggregates, timestamp, mutation, aggregate.name().name, functionArgumentsList(aggregate));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Aggregates).row(aggregate.name().name, functionArgumentsList(aggregate)).delete();
+        return builder;
     }
 
     /*

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/service/MigrationManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java b/src/java/org/apache/cassandra/service/MigrationManager.java
index ba239b3..7eac678 100644
--- a/src/java/org/apache/cassandra/service/MigrationManager.java
+++ b/src/java/org/apache/cassandra/service/MigrationManager.java
@@ -506,12 +506,14 @@ public class MigrationManager
      * actively announce a new version to active hosts via rpc
      * @param schema The schema mutation to be applied
      */
-    private static void announce(Mutation schema, boolean announceLocally)
+    private static void announce(Mutation.SimpleBuilder schema, boolean announceLocally)
     {
+        List<Mutation> mutations = Collections.singletonList(schema.build());
+
         if (announceLocally)
-            SchemaKeyspace.mergeSchema(Collections.singletonList(schema));
+            SchemaKeyspace.mergeSchema(mutations);
         else
-            FBUtilities.waitOnFuture(announce(Collections.singletonList(schema)));
+            FBUtilities.waitOnFuture(announce(mutations));
     }
 
     private static void pushSchemaMutation(InetAddress endpoint, Collection<Mutation> schema)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
index d7b804a..5c400a9 100644
--- a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
+++ b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
@@ -23,7 +23,8 @@ import java.util.*;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.db.RowUpdateBuilder;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.schema.KeyspaceMetadata;
 import org.apache.cassandra.schema.KeyspaceParams;
 import org.apache.cassandra.schema.Tables;
@@ -86,36 +87,41 @@ public final class TraceKeyspace
                                              String command,
                                              int ttl)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Sessions, FBUtilities.timestampMicros(), ttl, sessionId)
-                                 .clustering()
-                                 .add("client", client)
-                                 .add("coordinator", FBUtilities.getBroadcastAddress())
-                                 .add("request", request)
-                                 .add("started_at", new Date(startedAt))
-                                 .add("command", command);
-
-        for (Map.Entry<String, String> entry : parameters.entrySet())
-            adder.addMapEntry("parameters", entry.getKey(), entry.getValue());
-        return adder.build();
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Sessions, sessionId);
+        builder.row()
+               .ttl(ttl)
+               .add("client", client)
+               .add("coordinator", FBUtilities.getBroadcastAddress())
+               .add("request", request)
+               .add("started_at", new Date(startedAt))
+               .add("command", command)
+               .appendAll("parameters", parameters);
+
+        return builder.buildAsMutation();
     }
 
     static Mutation makeStopSessionMutation(ByteBuffer sessionId, int elapsed, int ttl)
     {
-        return new RowUpdateBuilder(Sessions, FBUtilities.timestampMicros(), ttl, sessionId)
-               .clustering()
-               .add("duration", elapsed)
-               .build();
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Sessions, sessionId);
+        builder.row()
+               .ttl(ttl)
+               .add("duration", elapsed);
+        return builder.buildAsMutation();
     }
 
     static Mutation makeEventMutation(ByteBuffer sessionId, String message, int elapsed, String threadName, int ttl)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Events, FBUtilities.timestampMicros(), ttl, sessionId)
-                                 .clustering(UUIDGen.getTimeUUID());
-        adder.add("activity", message);
-        adder.add("source", FBUtilities.getBroadcastAddress());
-        adder.add("thread", threadName);
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Events, sessionId);
+        Row.SimpleBuilder rowBuilder = builder.row(UUIDGen.getTimeUUID())
+                                              .ttl(ttl);
+
+        rowBuilder.add("activity", message)
+                  .add("source", FBUtilities.getBroadcastAddress())
+                  .add("thread", threadName);
+
         if (elapsed >= 0)
-            adder.add("source_elapsed", elapsed);
-        return adder.build();
+            rowBuilder.add("source_elapsed", elapsed);
+
+        return builder.buildAsMutation();
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/UpdateBuilder.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/UpdateBuilder.java b/test/unit/org/apache/cassandra/UpdateBuilder.java
index 3a5fbe6..19e48f2 100644
--- a/test/unit/org/apache/cassandra/UpdateBuilder.java
+++ b/test/unit/org/apache/cassandra/UpdateBuilder.java
@@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.*;
 import org.apache.cassandra.db.partitions.*;
 import org.apache.cassandra.utils.FBUtilities;
 import org.apache.cassandra.service.StorageService;
@@ -34,32 +35,30 @@ import org.apache.cassandra.service.StorageService;
  */
 public class UpdateBuilder
 {
-    private final PartitionUpdate update;
-    private RowUpdateBuilder currentRow;
-    private long timestamp = FBUtilities.timestampMicros();
+    private final PartitionUpdate.SimpleBuilder updateBuilder;
+    private Row.SimpleBuilder currentRow;
 
-    private UpdateBuilder(CFMetaData metadata, DecoratedKey partitionKey)
+    private UpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder)
     {
-        this.update = new PartitionUpdate(metadata, partitionKey, metadata.partitionColumns(), 4);
+        this.updateBuilder = updateBuilder;
     }
 
     public static UpdateBuilder create(CFMetaData metadata, Object... partitionKey)
     {
-        return new UpdateBuilder(metadata, makeKey(metadata, partitionKey));
+        return new UpdateBuilder(PartitionUpdate.simpleBuilder(metadata, partitionKey));
     }
 
     public UpdateBuilder withTimestamp(long timestamp)
     {
-        this.timestamp = timestamp;
+        updateBuilder.timestamp(timestamp);
+        if (currentRow != null)
+            currentRow.timestamp(timestamp);
         return this;
     }
 
     public UpdateBuilder newRow(Object... clustering)
     {
-        maybeBuildCurrentRow();
-        currentRow = new RowUpdateBuilder(update, timestamp, 0);
-        if (clustering.length > 0)
-            currentRow.clustering(clustering);
+        currentRow = updateBuilder.row(clustering);
         return this;
     }
 
@@ -72,48 +71,25 @@ public class UpdateBuilder
 
     public PartitionUpdate build()
     {
-        maybeBuildCurrentRow();
-        return update;
+        return updateBuilder.build();
     }
 
     public IMutation makeMutation()
     {
-        Mutation m = new Mutation(build());
-        return update.metadata().isCounter()
+        Mutation m = updateBuilder.buildAsMutation();
+        return updateBuilder.metadata().isCounter()
              ? new CounterMutation(m, ConsistencyLevel.ONE)
              : m;
     }
 
     public void apply()
     {
-        Mutation m = new Mutation(build());
-        if (update.metadata().isCounter())
-            new CounterMutation(m, ConsistencyLevel.ONE).apply();
-        else
-            m.apply();
+        makeMutation().apply();
     }
 
     public void applyUnsafe()
     {
-        assert !update.metadata().isCounter() : "Counters have currently no applyUnsafe() option";
-        new Mutation(build()).applyUnsafe();
-    }
-
-    private void maybeBuildCurrentRow()
-    {
-        if (currentRow != null)
-        {
-            currentRow.build();
-            currentRow = null;
-        }
-    }
-
-    private static DecoratedKey makeKey(CFMetaData metadata, Object[] partitionKey)
-    {
-        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
-            return (DecoratedKey)partitionKey[0];
-
-        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
-        return metadata.decorateKey(key);
+        assert !updateBuilder.metadata().isCounter() : "Counters have currently no applyUnsafe() option";
+        updateBuilder.buildAsMutation().applyUnsafe();
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/Util.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/Util.java b/test/unit/org/apache/cassandra/Util.java
index 9689628..151d3d4 100644
--- a/test/unit/org/apache/cassandra/Util.java
+++ b/test/unit/org/apache/cassandra/Util.java
@@ -459,15 +459,37 @@ public class Util
     public static boolean equal(UnfilteredRowIterator a, UnfilteredRowIterator b)
     {
         return Objects.equals(a.columns(), b.columns())
-            && Objects.equals(a.metadata(), b.metadata())
+            && Objects.equals(a.stats(), b.stats())
+            && sameContent(a, b);
+    }
+
+    // Test equality of the iterators, but without caring too much about the "metadata" of said iterator. This is often
+    // what we want in tests. In particular, the columns() reported by the iterators will sometimes differ because they
+    // are a superset of what the iterator actually contains, and depending on the method used to get each iterator
+    // tested, one may include a defined column the other don't while there is not actual content for that column.
+    public static boolean sameContent(UnfilteredRowIterator a, UnfilteredRowIterator b)
+    {
+        return Objects.equals(a.metadata(), b.metadata())
             && Objects.equals(a.isReverseOrder(), b.isReverseOrder())
             && Objects.equals(a.partitionKey(), b.partitionKey())
             && Objects.equals(a.partitionLevelDeletion(), b.partitionLevelDeletion())
             && Objects.equals(a.staticRow(), b.staticRow())
-            && Objects.equals(a.stats(), b.stats())
             && Iterators.elementsEqual(a, b);
     }
 
+    public static boolean sameContent(Mutation a, Mutation b)
+    {
+        if (!a.key().equals(b.key()) || !a.getColumnFamilyIds().equals(b.getColumnFamilyIds()))
+            return false;
+
+        for (UUID cfId : a.getColumnFamilyIds())
+        {
+            if (!sameContent(a.getPartitionUpdate(cfId).unfilteredIterator(), b.getPartitionUpdate(cfId).unfilteredIterator()))
+                return false;
+        }
+        return true;
+    }
+
     // moved & refactored from KeyspaceTest in < 3.0
     public static void assertColumns(Row row, String... expectedColumnNames)
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/batchlog/BatchTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/batchlog/BatchTest.java b/test/unit/org/apache/cassandra/batchlog/BatchTest.java
index b7a4100..4e64ec6 100644
--- a/test/unit/org/apache/cassandra/batchlog/BatchTest.java
+++ b/test/unit/org/apache/cassandra/batchlog/BatchTest.java
@@ -28,6 +28,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.Util;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.Keyspace;
 import org.apache.cassandra.db.Mutation;
@@ -44,6 +45,7 @@ import org.apache.cassandra.utils.UUIDGen;
 
 import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class BatchTest
 {
@@ -148,6 +150,19 @@ public class BatchTest
         Iterator<Mutation> it1 = batch1.decodedMutations.iterator();
         Iterator<Mutation> it2 = batch2.decodedMutations.iterator();
         while (it1.hasNext())
-            assertEquals(it1.next().toString(), it2.next().toString());
+        {
+            // We can't simply test the equality of both mutation string representation, that is do:
+            //   assertEquals(it1.next().toString(), it2.next().toString());
+            // because when deserializing from the old format, the returned iterator will always have it's 'columns()'
+            // method return all the table columns (no matter what's the actual content), and the table contains a
+            // 'val0' column we're not setting in that test.
+            //
+            // And it's actually not easy to fix legacy deserialization as we'd need to know which columns are actually
+            // set upfront, which would require use to iterate over the whole content first, which would be costly. And
+            // as the result of 'columns()' is only meant as a superset of the columns in the iterator, we don't bother.
+            Mutation mut1 = it1.next();
+            Mutation mut2 = it2.next();
+            assertTrue(mut1 + " != " + mut2, Util.sameContent(mut1, mut2));
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/config/CFMetaDataTest.java b/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
index 6bfe5c0..8616987 100644
--- a/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
+++ b/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
@@ -150,7 +150,7 @@ public class CFMetaDataTest
         assert before.equals(after) : String.format("%n%s%n!=%n%s", before, after);
 
         // Test schema conversion
-        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
+        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros()).build();
         PartitionUpdate cfU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
         PartitionUpdate cdU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java b/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
index 632c290..0b18eec 100644
--- a/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
+++ b/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
@@ -19,10 +19,20 @@
 package org.apache.cassandra.cql3;
 
 import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
+import org.apache.cassandra.config.DatabaseDescriptor;
+
 public class CDCStatementTest extends CQLTester
 {
+    @BeforeClass
+    public static void checkConfig()
+    {
+        Assume.assumeTrue(DatabaseDescriptor.isCDCEnabled());
+    }
+
     @Test
     public void testEnableOnCreate() throws Throwable
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
deleted file mode 100644
index afe2455..0000000
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.cassandra.cql3.validation.entities;
-
-import org.junit.Test;
-
-import org.apache.cassandra.Util;
-import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.db.RowUpdateBuilder;
-import org.apache.cassandra.utils.FBUtilities;
-
-// see CASSANDRA-9743, CASSANDRA-9746
-public class RowUpdateBuilderTest extends CQLTester
-{
-    @Test
-    public void testAddListEntryDurable() throws Throwable
-    {
-        testAddListEntry(false);
-    }
-
-    @Test
-    public void testAddListEntryTransient() throws Throwable
-    {
-        testAddListEntry(true);
-    }
-
-    public void testAddListEntry(boolean skipCommitLog) throws Throwable
-    {
-        createTable("CREATE TABLE %s ("
-                    + "pk text,"
-                    + "ck text,"
-                    + "l1 list<int>,"
-                    + "l2 list<int>,"
-                    + "PRIMARY KEY ((pk), ck))");
-
-        long timestamp = FBUtilities.timestampMicros();
-
-        Mutation mutation = new Mutation(keyspace(), Util.dk("test"));
-        addToMutation("row1", timestamp, mutation);
-        addToMutation("row2", timestamp, mutation);
-
-        if (skipCommitLog)
-            mutation.applyUnsafe();
-        else
-            mutation.apply();
-
-        assertRowCount(execute("SELECT ck FROM %s"), 2);
-    }
-
-    private void addToMutation(String typeName, long timestamp, Mutation mutation)
-    {
-        RowUpdateBuilder adder = new RowUpdateBuilder(getCurrentColumnFamilyStore().metadata, timestamp, mutation)
-                                 .clustering(typeName);
-
-        for (int i = 0; i < 2; i++)
-        {
-            adder.addListEntry("l1", i)
-                 .addListEntry("l2", i);
-        }
-
-        adder.build();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java b/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
index a67e9e5..8897700 100644
--- a/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
+++ b/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
@@ -118,7 +118,7 @@ public class RecoveryManagerMissingHeaderTest
 
         CommitLog.instance.resetUnsafe(false);
 
-        Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-        Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java b/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
index 37d719e..cbc412d 100644
--- a/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
+++ b/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
@@ -188,8 +188,8 @@ public class RecoveryManagerTest
             }
             Assert.assertFalse(t.isAlive());
 
-            Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-            Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+            Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+            Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
         }
         finally
         {
@@ -220,8 +220,8 @@ public class RecoveryManagerTest
         CommitLog.instance.resetUnsafe(false);
 
         DecoratedKey dk = Util.dk("keymulti");
-        Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-        Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java b/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
new file mode 100644
index 0000000..8e71d64
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
@@ -0,0 +1,196 @@
+/*
+ * 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.cassandra.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.db.marshal.SetType;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.context.CounterContext;
+import org.apache.cassandra.db.partitions.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.ListType;
+import org.apache.cassandra.db.marshal.MapType;
+import org.apache.cassandra.utils.*;
+
+/**
+ * Convenience object to create single row updates for tests.
+ *
+ * This is a thin wrapper over the builders in SimpleBuilders for historical reasons.
+ * We could modify all the tests using this class to use the simple builders directly
+ * instead, but there is a fair amount of use so the value of such effort is unclear.
+ */
+public class RowUpdateBuilder
+{
+    private final PartitionUpdate.SimpleBuilder updateBuilder;
+    private Row.SimpleBuilder rowBuilder;
+    private boolean noRowMarker;
+
+    private List<RangeTombstone> rts = new ArrayList<>();
+
+    private RowUpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder)
+    {
+        this.updateBuilder = updateBuilder;
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
+    {
+        this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
+    {
+        this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
+    {
+        this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
+    {
+        this(PartitionUpdate.simpleBuilder(metadata, partitionKey));
+
+        this.updateBuilder.timestamp(timestamp);
+        this.updateBuilder.ttl(ttl);
+        this.updateBuilder.nowInSec(localDeletionTime);
+    }
+
+    private Row.SimpleBuilder rowBuilder()
+    {
+        // Normally, rowBuilder is created by the call to clustering(), but we allow skipping that call for an empty
+        // clustering.
+        if (rowBuilder == null)
+        {
+            rowBuilder = updateBuilder.row();
+            if (noRowMarker)
+                rowBuilder.noPrimaryKeyLivenessInfo();
+        }
+
+        return rowBuilder;
+    }
+
+    // This must be called before any addition or deletion if used.
+    public RowUpdateBuilder noRowMarker()
+    {
+        this.noRowMarker = true;
+        if (rowBuilder != null)
+            rowBuilder.noPrimaryKeyLivenessInfo();
+        return this;
+    }
+
+    public RowUpdateBuilder clustering(Object... clusteringValues)
+    {
+        assert rowBuilder == null;
+        rowBuilder = updateBuilder.row(clusteringValues);
+        if (noRowMarker)
+            rowBuilder.noPrimaryKeyLivenessInfo();
+        return this;
+    }
+
+    public Mutation build()
+    {
+        return new Mutation(buildUpdate());
+    }
+
+    public PartitionUpdate buildUpdate()
+    {
+        PartitionUpdate update = updateBuilder.build();
+        for (RangeTombstone rt : rts)
+            update.add(rt);
+        return update;
+    }
+
+    private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues)
+    {
+        assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty());
+
+        boolean isStatic = clusteringValues.length != update.metadata().comparator.size();
+        Row.Builder builder = BTreeRow.sortedBuilder();
+
+        if (isStatic)
+            builder.newRow(Clustering.STATIC_CLUSTERING);
+        else
+            builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
+        builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime)));
+
+        update.add(builder.build());
+    }
+
+    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
+    {
+        return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
+    }
+
+    public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
+    {
+        PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 0);
+        deleteRow(update, timestamp, localDeletionTime, clusteringValues);
+        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
+        // underneath (this class if for convenience, not performance)
+        return new Mutation(update.metadata().ksName, update.partitionKey()).add(update);
+    }
+
+    private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
+    {
+        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
+            return (DecoratedKey)partitionKey[0];
+
+        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
+        return metadata.decorateKey(key);
+    }
+
+    public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
+    {
+        rts.add(rt);
+        return this;
+    }
+
+    public RowUpdateBuilder addRangeTombstone(Object start, Object end)
+    {
+        updateBuilder.addRangeTombstone().start(start).end(end);
+        return this;
+    }
+
+    public RowUpdateBuilder add(String columnName, Object value)
+    {
+        rowBuilder().add(columnName, value);
+        return this;
+    }
+
+    public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
+    {
+        return add(columnDefinition.name.toString(), value);
+    }
+
+    public RowUpdateBuilder delete(String columnName)
+    {
+        rowBuilder().delete(columnName);
+        return this;
+    }
+
+    public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
+    {
+        return delete(columnDefinition.name.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java b/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
index b264553..3dd798d 100644
--- a/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
+++ b/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
@@ -172,7 +172,7 @@ public class TTLExpiryTest
 
         new RowUpdateBuilder(cfs.metadata, timestamp, 1, key)
             .add("col2", ByteBufferUtil.EMPTY_BYTE_BUFFER)
-            .addMapEntry("col8", "bar", "foo")
+            .add("col8", Collections.singletonMap("bar", "foo"))
             .delete("col1")
             .build()
             .applyUnsafe();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java b/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
index a069db1..bfa9796 100644
--- a/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
+++ b/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.cassandra.db.partition;
 
+import org.apache.cassandra.UpdateBuilder;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.cql3.CQLTester;
 import org.apache.cassandra.db.RowUpdateBuilder;
@@ -34,21 +35,17 @@ public class PartitionUpdateTest extends CQLTester
         createTable("CREATE TABLE %s (key text, clustering int, a int, s int static, PRIMARY KEY(key, clustering))");
         CFMetaData cfm = currentTableMetadata();
 
-        long timestamp = FBUtilities.timestampMicros();
-        PartitionUpdate update = new RowUpdateBuilder(cfm, timestamp, "key0").clustering(1).add("a", 1).buildUpdate();
-        Assert.assertEquals(1, update.operationCount());
-
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").buildUpdate();
-        Assert.assertEquals(0, update.operationCount());
+        UpdateBuilder builder = UpdateBuilder.create(cfm, "key0");
+        Assert.assertEquals(0, builder.build().operationCount());
+        Assert.assertEquals(1, builder.newRow(1).add("a", 1).build().operationCount());
 
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").add("s", 1).buildUpdate();
-        Assert.assertEquals(1, update.operationCount());
+        builder = UpdateBuilder.create(cfm, "key0");
+        Assert.assertEquals(1, builder.newRow().add("s", 1).build().operationCount());
 
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").add("s", 1).buildUpdate();
-        update = new RowUpdateBuilder(update, timestamp, cfm.params.defaultTimeToLive).clustering(1)
-                                                                                      .add("a", 1)
-                                                                                      .buildUpdate();
-        Assert.assertEquals(2, update.operationCount());
+        builder = UpdateBuilder.create(cfm, "key0");
+        builder.newRow().add("s", 1);
+        builder.newRow(1).add("a", 1);
+        Assert.assertEquals(2, builder.build().operationCount());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/hints/HintTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/hints/HintTest.java b/test/unit/org/apache/cassandra/hints/HintTest.java
index 658a41c..4cc2188 100644
--- a/test/unit/org/apache/cassandra/hints/HintTest.java
+++ b/test/unit/org/apache/cassandra/hints/HintTest.java
@@ -30,9 +30,7 @@ import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
 import org.apache.cassandra.Util;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.config.*;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.partitions.FilteredPartition;
 import org.apache.cassandra.db.partitions.PartitionIterator;
@@ -127,7 +125,7 @@ public class HintTest
 
         // assert that we can read the inserted partitions
         for (PartitionUpdate partition : mutation.getPartitionUpdates())
-            assertPartitionsEqual(partition, readPartition(key, partition.metadata().cfName));
+            assertPartitionsEqual(partition, readPartition(key, partition.metadata().cfName, partition.columns()));
     }
 
     @Test
@@ -152,8 +150,10 @@ public class HintTest
         assertNoPartitions(key, TABLE1);
 
         // TABLE0 and TABLE2 updates should have been applied successfully
-        assertPartitionsEqual(mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE0)), readPartition(key, TABLE0));
-        assertPartitionsEqual(mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE2)), readPartition(key, TABLE2));
+        PartitionUpdate upd0 = mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE0));
+        assertPartitionsEqual(upd0, readPartition(key, TABLE0, upd0.columns()));
+        PartitionUpdate upd2 = mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE2));
+        assertPartitionsEqual(upd2, readPartition(key, TABLE2, upd2.columns()));
     }
 
     @Test
@@ -296,40 +296,44 @@ public class HintTest
 
     private static Mutation createMutation(String key, long now)
     {
-        Mutation mutation = new Mutation(KEYSPACE, dk(key));
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(KEYSPACE, dk(key));
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE0), now, mutation)
-            .clustering("column0")
-            .add("val", "value0")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE0))
+               .timestamp(now)
+               .row("column0")
+               .add("val", "value0");
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE1), now + 1, mutation)
-            .clustering("column1")
-            .add("val", "value1")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE1))
+               .timestamp(now + 1)
+               .row("column1")
+               .add("val", "value1");
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE2), now + 2, mutation)
-            .clustering("column2")
-            .add("val", "value2")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE2))
+               .timestamp(now + 2)
+               .row("column2")
+               .add("val", "value2");
 
-        return mutation;
+        return builder.build();
     }
 
-    private static SinglePartitionReadCommand cmd(String key, String table)
+    private static ColumnFamilyStore cfs(String table)
     {
-        CFMetaData meta = Schema.instance.getCFMetaData(KEYSPACE, table);
-        return SinglePartitionReadCommand.fullPartitionRead(meta, FBUtilities.nowInSeconds(), bytes(key));
+        return Schema.instance.getColumnFamilyStoreInstance(Schema.instance.getCFMetaData(KEYSPACE, table).cfId);
     }
 
-    private static FilteredPartition readPartition(String key, String table)
+    private static FilteredPartition readPartition(String key, String table, PartitionColumns columns)
     {
-        return Util.getOnlyPartition(cmd(key, table));
+        String[] columnNames = new String[columns.size()];
+        int i = 0;
+        for (ColumnDefinition column : columns)
+            columnNames[i++] = column.name.toString();
+
+        return Util.getOnlyPartition(Util.cmd(cfs(table), key).columns(columnNames).build());
     }
 
     private static void assertNoPartitions(String key, String table)
     {
-        ReadCommand cmd = cmd(key, table);
+        ReadCommand cmd = Util.cmd(cfs(table), key).build();
 
         try (ReadExecutionController executionController = cmd.executionController();
              PartitionIterator iterator = cmd.executeInternal(executionController))

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java b/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
index cc97df0..87abdac 100644
--- a/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
+++ b/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
@@ -27,6 +27,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.Util;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.*;
@@ -159,7 +160,7 @@ public class LegacyHintsMigratorTest
 
                 assertEquals(timestamp, hint.creationTime);
                 assertEquals(ttl, hint.gcgs);
-                assertMutationsEqual(mutation, hint.mutation);
+                assertTrue(mutation + " != " + hint.mutation, Util.sameContent(mutation, hint.mutation));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/DefsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/DefsTest.java b/test/unit/org/apache/cassandra/schema/DefsTest.java
index e9980f6..d4ac1dc 100644
--- a/test/unit/org/apache/cassandra/schema/DefsTest.java
+++ b/test/unit/org/apache/cassandra/schema/DefsTest.java
@@ -498,7 +498,7 @@ public class DefsTest
     public void testDropIndex() throws ConfigurationException
     {
         // persist keyspace definition in the system keyspace
-        SchemaKeyspace.makeCreateKeyspaceMutation(Schema.instance.getKSMetaData(KEYSPACE6), FBUtilities.timestampMicros()).applyUnsafe();
+        SchemaKeyspace.makeCreateKeyspaceMutation(Schema.instance.getKSMetaData(KEYSPACE6), FBUtilities.timestampMicros()).build().applyUnsafe();
         ColumnFamilyStore cfs = Keyspace.open(KEYSPACE6).getColumnFamilyStore(TABLE1i);
         String indexName = "birthdate_key_index";
 


[13/14] cassandra git commit: Merge branch 'cassandra-3.8' into cassandra-3.9

Posted by sl...@apache.org.
Merge branch 'cassandra-3.8' into cassandra-3.9

* cassandra-3.8:
  Fix RTE on mixed-version cluster due to CDC schema changes.
  c* uses commons-lang3, not commons-lang
  Update build.xml and CHANGES.txt for 3.8


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b603720e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b603720e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b603720e

Branch: refs/heads/cassandra-3.9
Commit: b603720e4e0e9a9ed5f077e3a0f8819ef2d11441
Parents: 1323ad0 2683806
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Wed Aug 3 17:42:06 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Wed Aug 3 17:42:06 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 NEWS.txt                                        |  10 +-
 build.xml                                       |   2 +-
 .../cassandra/batchlog/BatchlogManager.java     |  19 +-
 .../batchlog/LegacyBatchlogMigrator.java        |   9 +-
 src/java/org/apache/cassandra/db/Mutation.java  |  66 +++
 .../apache/cassandra/db/RowUpdateBuilder.java   | 400 ----------------
 .../org/apache/cassandra/db/SimpleBuilders.java | 461 +++++++++++++++++++
 .../org/apache/cassandra/db/SystemKeyspace.java |  11 +-
 .../cassandra/db/commitlog/CommitLogReader.java |   2 +-
 .../db/partitions/AbstractBTreePartition.java   |   2 +-
 .../db/partitions/PartitionUpdate.java          | 154 +++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  99 ++++
 src/java/org/apache/cassandra/db/rows/Rows.java |  16 +
 .../apache/cassandra/db/transform/BaseRows.java |   3 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |  12 +-
 .../apache/cassandra/schema/SchemaKeyspace.java | 427 +++++++++--------
 .../cassandra/service/MigrationManager.java     |   8 +-
 .../apache/cassandra/tracing/TraceKeyspace.java |  52 ++-
 .../org/apache/cassandra/UpdateBuilder.java     |  56 +--
 test/unit/org/apache/cassandra/Util.java        |  26 +-
 .../apache/cassandra/batchlog/BatchTest.java    |  17 +-
 .../apache/cassandra/config/CFMetaDataTest.java |   2 +-
 .../apache/cassandra/cql3/CDCStatementTest.java |  10 +
 .../entities/RowUpdateBuilderTest.java          |  79 ----
 .../db/RecoveryManagerMissingHeaderTest.java    |   4 +-
 .../cassandra/db/RecoveryManagerTest.java       |   8 +-
 .../apache/cassandra/db/RowUpdateBuilder.java   | 196 ++++++++
 .../cassandra/db/compaction/TTLExpiryTest.java  |   2 +-
 .../db/partition/PartitionUpdateTest.java       |  23 +-
 .../org/apache/cassandra/hints/HintTest.java    |  56 +--
 .../hints/LegacyHintsMigratorTest.java          |   3 +-
 .../org/apache/cassandra/schema/DefsTest.java   |   2 +-
 .../schema/LegacySchemaMigratorTest.java        | 111 ++---
 .../cassandra/schema/SchemaKeyspaceTest.java    |   6 +-
 .../cassandra/service/DataResolverTest.java     |   2 +-
 .../streaming/StreamingTransferTest.java        |   2 +-
 37 files changed, 1450 insertions(+), 909 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 0d79a9f,388a290..62a6e6f
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,32 -1,5 +1,33 @@@
 +3.9
 + * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
 + * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
 + * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
 + * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
 +Merged from 3.0:
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 +Merged from 2.2:
 + * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
 + * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
 + * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
 +Merged from 2.1:
 + * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
 + * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
 +
 +
  3.8
+  * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
   * Fix hdr logging for single operation workloads (CASSANDRA-12145)
   * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
   * Increase size of flushExecutor thread pool (CASSANDRA-12071)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/transform/BaseRows.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/test/unit/org/apache/cassandra/Util.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/test/unit/org/apache/cassandra/service/DataResolverTest.java
----------------------------------------------------------------------


[12/14] cassandra git commit: Merge branch 'cassandra-3.8' into cassandra-3.9

Posted by sl...@apache.org.
Merge branch 'cassandra-3.8' into cassandra-3.9

* cassandra-3.8:
  Fix RTE on mixed-version cluster due to CDC schema changes.
  c* uses commons-lang3, not commons-lang
  Update build.xml and CHANGES.txt for 3.8


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b603720e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b603720e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b603720e

Branch: refs/heads/trunk
Commit: b603720e4e0e9a9ed5f077e3a0f8819ef2d11441
Parents: 1323ad0 2683806
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Wed Aug 3 17:42:06 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Wed Aug 3 17:42:06 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 NEWS.txt                                        |  10 +-
 build.xml                                       |   2 +-
 .../cassandra/batchlog/BatchlogManager.java     |  19 +-
 .../batchlog/LegacyBatchlogMigrator.java        |   9 +-
 src/java/org/apache/cassandra/db/Mutation.java  |  66 +++
 .../apache/cassandra/db/RowUpdateBuilder.java   | 400 ----------------
 .../org/apache/cassandra/db/SimpleBuilders.java | 461 +++++++++++++++++++
 .../org/apache/cassandra/db/SystemKeyspace.java |  11 +-
 .../cassandra/db/commitlog/CommitLogReader.java |   2 +-
 .../db/partitions/AbstractBTreePartition.java   |   2 +-
 .../db/partitions/PartitionUpdate.java          | 154 +++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  99 ++++
 src/java/org/apache/cassandra/db/rows/Rows.java |  16 +
 .../apache/cassandra/db/transform/BaseRows.java |   3 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |  12 +-
 .../apache/cassandra/schema/SchemaKeyspace.java | 427 +++++++++--------
 .../cassandra/service/MigrationManager.java     |   8 +-
 .../apache/cassandra/tracing/TraceKeyspace.java |  52 ++-
 .../org/apache/cassandra/UpdateBuilder.java     |  56 +--
 test/unit/org/apache/cassandra/Util.java        |  26 +-
 .../apache/cassandra/batchlog/BatchTest.java    |  17 +-
 .../apache/cassandra/config/CFMetaDataTest.java |   2 +-
 .../apache/cassandra/cql3/CDCStatementTest.java |  10 +
 .../entities/RowUpdateBuilderTest.java          |  79 ----
 .../db/RecoveryManagerMissingHeaderTest.java    |   4 +-
 .../cassandra/db/RecoveryManagerTest.java       |   8 +-
 .../apache/cassandra/db/RowUpdateBuilder.java   | 196 ++++++++
 .../cassandra/db/compaction/TTLExpiryTest.java  |   2 +-
 .../db/partition/PartitionUpdateTest.java       |  23 +-
 .../org/apache/cassandra/hints/HintTest.java    |  56 +--
 .../hints/LegacyHintsMigratorTest.java          |   3 +-
 .../org/apache/cassandra/schema/DefsTest.java   |   2 +-
 .../schema/LegacySchemaMigratorTest.java        | 111 ++---
 .../cassandra/schema/SchemaKeyspaceTest.java    |   6 +-
 .../cassandra/service/DataResolverTest.java     |   2 +-
 .../streaming/StreamingTransferTest.java        |   2 +-
 37 files changed, 1450 insertions(+), 909 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 0d79a9f,388a290..62a6e6f
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,32 -1,5 +1,33 @@@
 +3.9
 + * Fixed flacky SSTablesIteratedTest (CASSANDRA-12282)
 + * Fixed flacky SSTableRewriterTest: check file counts before calling validateCFS (CASSANDRA-12348)
 + * cqlsh: Fix handling of $$-escaped strings (CASSANDRA-12189)
 + * Fix SSL JMX requiring truststore containing server cert (CASSANDRA-12109)
 +Merged from 3.0:
 + * Fix upgrade of super columns on thrift (CASSANDRA-12335)
 + * Fixed flacky BlacklistingCompactionsTest, switched to fixed size types and increased corruption size (CASSANDRA-12359)
 + * Rerun ReplicationAwareTokenAllocatorTest on failure to avoid flakiness (CASSANDRA-12277)
 + * Exception when computing read-repair for range tombstones (CASSANDRA-12263)
 + * Lost counter writes in compact table and static columns (CASSANDRA-12219)
 + * AssertionError with MVs on updating a row that isn't indexed due to a null value (CASSANDRA-12247)
 + * Disable RR and speculative retry with EACH_QUORUM reads (CASSANDRA-11980)
 + * Add option to override compaction space check (CASSANDRA-12180)
 + * Faster startup by only scanning each directory for temporary files once (CASSANDRA-12114)
 + * Respond with v1/v2 protocol header when responding to driver that attempts
 +   to connect with too low of a protocol version (CASSANDRA-11464)
 + * NullPointerExpception when reading/compacting table (CASSANDRA-11988)
 + * Fix problem with undeleteable rows on upgrade to new sstable format (CASSANDRA-12144)
 +Merged from 2.2:
 + * Wait for tracing events before returning response and query at same consistency level client side (CASSANDRA-11465)
 + * cqlsh copyutil should get host metadata by connected address (CASSANDRA-11979)
 + * Fixed cqlshlib.test.remove_test_db (CASSANDRA-12214)
 +Merged from 2.1:
 + * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
 + * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
 +
 +
  3.8
+  * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
   * Fix hdr logging for single operation workloads (CASSANDRA-12145)
   * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
   * Increase size of flushExecutor thread pool (CASSANDRA-12071)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/db/transform/BaseRows.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/test/unit/org/apache/cassandra/Util.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b603720e/test/unit/org/apache/cassandra/service/DataResolverTest.java
----------------------------------------------------------------------


[06/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
index 2de671c..72441cd 100644
--- a/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
+++ b/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
@@ -34,6 +34,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.FieldIdentifier;
 import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.Row;
 import org.apache.cassandra.db.marshal.*;
 import org.apache.cassandra.index.TargetParser;
 import org.apache.cassandra.thrift.ThriftConversion;
@@ -564,35 +565,40 @@ public class LegacySchemaMigratorTest
         setLegacyIndexStatus(keyspace);
     }
 
-    private static Mutation makeLegacyCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    private static DecoratedKey decorate(CFMetaData metadata, Object value)
     {
-        // Note that because Keyspaces is a COMPACT TABLE, we're really only setting static columns internally and shouldn't set any clustering.
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyKeyspaces, timestamp, keyspace.name);
+        return metadata.decorateKey(((AbstractType)metadata.getKeyValidator()).decompose(value));
+    }
 
-        adder.add("durable_writes", keyspace.params.durableWrites)
-             .add("strategy_class", keyspace.params.replication.klass.getName())
-             .add("strategy_options", json(keyspace.params.replication.options));
+    private static Mutation makeLegacyCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    {
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(SystemKeyspace.NAME, decorate(SystemKeyspace.LegacyKeyspaces, keyspace.name))
+                                                 .timestamp(timestamp);
 
-        Mutation mutation = adder.build();
+        builder.update(SystemKeyspace.LegacyKeyspaces)
+               .row()
+               .add("durable_writes", keyspace.params.durableWrites)
+               .add("strategy_class", keyspace.params.replication.klass.getName())
+               .add("strategy_options", json(keyspace.params.replication.options));
 
-        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation));
-        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation));
-        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation));
-        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation));
+        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, true, builder));
+        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, builder));
+        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, builder));
+        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, builder));
 
-        return mutation;
+        return builder.build();
     }
 
     /*
      * Serializing tables
      */
 
-    private static void addTableToSchemaMutation(CFMetaData table, long timestamp, boolean withColumnsAndTriggers, Mutation mutation)
+    private static void addTableToSchemaMutation(CFMetaData table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder)
     {
         // For property that can be null (and can be changed), we insert tombstones, to make sure
         // we don't keep a property the user has removed
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyColumnfamilies, timestamp, mutation)
-                                 .clustering(table.cfName);
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyColumnfamilies)
+                                         .row(table.cfName);
 
         adder.add("cf_id", table.cfId)
              .add("type", table.isSuper() ? "Super" : "Standard");
@@ -625,12 +631,14 @@ public class LegacySchemaMigratorTest
              .add("read_repair_chance", table.params.readRepairChance)
              .add("speculative_retry", table.params.speculativeRetry.toString());
 
+        Map<String, Long> dropped = new HashMap<>();
         for (Map.Entry<ByteBuffer, CFMetaData.DroppedColumn> entry : table.getDroppedColumns().entrySet())
         {
             String name = UTF8Type.instance.getString(entry.getKey());
             CFMetaData.DroppedColumn column = entry.getValue();
-            adder.addMapEntry("dropped_columns", name, column.droppedTime);
+            dropped.put(name, column.droppedTime);
         }
+        adder.add("dropped_columns", dropped);
 
         adder.add("is_dense", table.isDense());
 
@@ -639,13 +647,11 @@ public class LegacySchemaMigratorTest
         if (withColumnsAndTriggers)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (TriggerMetadata trigger : table.getTriggers())
-                addTriggerToSchemaMutation(table, trigger, timestamp, mutation);
+                addTriggerToSchemaMutation(table, trigger, builder);
         }
-
-        adder.build();
     }
 
     private static String cachingToString(CachingParams caching)
@@ -655,14 +661,14 @@ public class LegacySchemaMigratorTest
                       caching.rowsPerPartitionAsString());
     }
 
-    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
         // We need to special case pk-only dense tables. See CASSANDRA-9874.
         String name = table.isDense() && column.kind == ColumnDefinition.Kind.REGULAR && column.type instanceof EmptyType
                     ? ""
                     : column.name.toString();
 
-        final RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyColumns, timestamp, mutation).clustering(table.cfName, name);
+        final Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyColumns).row(table.cfName, name);
 
         adder.add("validator", column.type.toString())
              .add("type", serializeKind(column.kind, table.isDense()))
@@ -682,8 +688,6 @@ public class LegacySchemaMigratorTest
             adder.add("index_type", null);
             adder.add("index_options", null);
         }
-
-        adder.build();
     }
 
     private static Optional<IndexMetadata> findIndexForColumn(Indexes indexes,
@@ -712,71 +716,67 @@ public class LegacySchemaMigratorTest
         return kind.toString().toLowerCase();
     }
 
-    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        new RowUpdateBuilder(SystemKeyspace.LegacyTriggers, timestamp, mutation)
-            .clustering(table.cfName, trigger.name)
-            .addMapEntry("trigger_options", "class", trigger.classOption)
-            .build();
+        builder.update(SystemKeyspace.LegacyTriggers)
+               .row(table.cfName, trigger.name)
+               .add("trigger_options", Collections.singletonMap("class", trigger.classOption));
     }
 
     /*
      * Serializing types
      */
 
-    private static void addTypeToSchemaMutation(UserType type, long timestamp, Mutation mutation)
+    private static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyUsertypes, timestamp, mutation)
-                                 .clustering(type.getNameAsString());
-
-        adder.resetCollection("field_names")
-             .resetCollection("field_types");
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyUsertypes)
+                                         .row(type.getNameAsString());
 
+        List<String> names = new ArrayList<>();
+        List<String> types = new ArrayList<>();
         for (int i = 0; i < type.size(); i++)
         {
-            adder.addListEntry("field_names", type.fieldName(i).toString())
-                 .addListEntry("field_types", type.fieldType(i).toString());
+            names.add(type.fieldName(i).toString());
+            types.add(type.fieldType(i).toString());
         }
 
-        adder.build();
+        adder.add("field_names", names)
+             .add("field_types", types);
     }
 
     /*
      * Serializing functions
      */
 
-    private static void addFunctionToSchemaMutation(UDFunction function, long timestamp, Mutation mutation)
+    private static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyFunctions, timestamp, mutation)
-                                 .clustering(function.name().name, functionSignatureWithTypes(function));
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyFunctions)
+                                         .row(function.name().name, functionSignatureWithTypes(function));
 
         adder.add("body", function.body())
              .add("language", function.language())
              .add("return_type", function.returnType().toString())
              .add("called_on_null_input", function.isCalledOnNullInput());
 
-        adder.resetCollection("argument_names")
-             .resetCollection("argument_types");
-
+        List<ByteBuffer> names = new ArrayList<>();
+        List<String> types = new ArrayList<>();
         for (int i = 0; i < function.argNames().size(); i++)
         {
-            adder.addListEntry("argument_names", function.argNames().get(i).bytes)
-                 .addListEntry("argument_types", function.argTypes().get(i).toString());
+            names.add(function.argNames().get(i).bytes);
+            types.add(function.argTypes().get(i).toString());
         }
-
-        adder.build();
+        adder.add("argument_names", names)
+             .add("argument_types", types);
     }
 
     /*
      * Serializing aggregates
      */
 
-    private static void addAggregateToSchemaMutation(UDAggregate aggregate, long timestamp, Mutation mutation)
+    private static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(SystemKeyspace.LegacyAggregates, timestamp, mutation)
-                                 .clustering(aggregate.name().name, functionSignatureWithTypes(aggregate));
-
-        adder.resetCollection("argument_types");
+        Row.SimpleBuilder adder = builder.update(SystemKeyspace.LegacyAggregates)
+                                 .row(aggregate.name().name, functionSignatureWithTypes(aggregate));
 
         adder.add("return_type", aggregate.returnType().toString())
              .add("state_func", aggregate.stateFunction().name().name);
@@ -788,10 +788,11 @@ public class LegacySchemaMigratorTest
         if (aggregate.initialCondition() != null)
             adder.add("initcond", aggregate.initialCondition());
 
+        List<String> types = new ArrayList<>();
         for (AbstractType<?> argType : aggregate.argTypes())
-            adder.addListEntry("argument_types", argType.toString());
+            types.add(argType.toString());
 
-        adder.build();
+        adder.add("argument_types", types);
     }
 
     // We allow method overloads, so a function is not uniquely identified by its name only, but

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java b/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
index b2e3535..d686fdb 100644
--- a/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
+++ b/test/unit/org/apache/cassandra/schema/SchemaKeyspaceTest.java
@@ -164,7 +164,7 @@ public class SchemaKeyspaceTest
     private static void updateTable(String keyspace, CFMetaData oldTable, CFMetaData newTable)
     {
         KeyspaceMetadata ksm = Schema.instance.getKeyspaceInstance(keyspace).getMetadata();
-        Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros());
+        Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros()).build();
         SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
     }
 
@@ -173,7 +173,7 @@ public class SchemaKeyspaceTest
         CFMetaData table = CFMetaData.compile(cql, keyspace);
 
         KeyspaceMetadata ksm = KeyspaceMetadata.create(keyspace, KeyspaceParams.simple(1), Tables.of(table));
-        Mutation mutation = SchemaKeyspace.makeCreateTableMutation(ksm, table, FBUtilities.timestampMicros());
+        Mutation mutation = SchemaKeyspace.makeCreateTableMutation(ksm, table, FBUtilities.timestampMicros()).build();
         SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
     }
 
@@ -187,7 +187,7 @@ public class SchemaKeyspaceTest
         assert before.equals(after) : String.format("%n%s%n!=%n%s", before, after);
 
         // Test schema conversion
-        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
+        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros()).build();
         PartitionUpdate serializedCf = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
         PartitionUpdate serializedCD = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/service/DataResolverTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/service/DataResolverTest.java b/test/unit/org/apache/cassandra/service/DataResolverTest.java
index b20dfc0..b48512f 100644
--- a/test/unit/org/apache/cassandra/service/DataResolverTest.java
+++ b/test/unit/org/apache/cassandra/service/DataResolverTest.java
@@ -245,7 +245,7 @@ public class DataResolverTest
 
         RangeTombstone tombstone1 = tombstone("1", "11", 1, nowInSec);
         RangeTombstone tombstone2 = tombstone("3", "31", 1, nowInSec);
-        PartitionUpdate update =new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1)
+        PartitionUpdate update = new RowUpdateBuilder(cfm, nowInSec, 1L, dk).addRangeTombstone(tombstone1)
                                                                                   .addRangeTombstone(tombstone2)
                                                                                   .buildUpdate();
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
index 6be880c..879f44c 100644
--- a/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
+++ b/test/unit/org/apache/cassandra/streaming/StreamingTransferTest.java
@@ -363,7 +363,7 @@ public class StreamingTransferTest
 
 
         updates = new RowUpdateBuilder(cfs.metadata, FBUtilities.timestampMicros() + 1, key);
-        updates.addRangeTombstone(Slice.make(comparator.make(5), comparator.make(7)))
+        updates.addRangeTombstone(5, 7)
                 .build()
                 .apply();
 


[05/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
Fix RTE on mixed-version cluster due to CDC schema changes.

Patch by jmckenzie and slebresne; reviewed by ayeschenko for CASSANDRA-12236


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/26838063
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/26838063
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/26838063

Branch: refs/heads/cassandra-3.9
Commit: 26838063de6246e3a1e18062114ca92fb81c00cf
Parents: b27e2f9
Author: Josh McKenzie <jm...@apache.org>
Authored: Thu Jul 21 12:45:13 2016 -0400
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Wed Aug 3 17:41:24 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 NEWS.txt                                        |  10 +-
 .../cassandra/batchlog/BatchlogManager.java     |  19 +-
 .../batchlog/LegacyBatchlogMigrator.java        |   9 +-
 src/java/org/apache/cassandra/db/Mutation.java  |  66 +++
 .../apache/cassandra/db/RowUpdateBuilder.java   | 400 ----------------
 .../org/apache/cassandra/db/SimpleBuilders.java | 461 +++++++++++++++++++
 .../org/apache/cassandra/db/SystemKeyspace.java |  11 +-
 .../db/partitions/AbstractBTreePartition.java   |   2 +-
 .../db/partitions/PartitionUpdate.java          | 154 +++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  99 ++++
 src/java/org/apache/cassandra/db/rows/Rows.java |  16 +
 .../apache/cassandra/db/transform/BaseRows.java |   3 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |  12 +-
 .../apache/cassandra/schema/SchemaKeyspace.java | 427 +++++++++--------
 .../cassandra/service/MigrationManager.java     |   8 +-
 .../apache/cassandra/tracing/TraceKeyspace.java |  52 ++-
 .../org/apache/cassandra/UpdateBuilder.java     |  56 +--
 test/unit/org/apache/cassandra/Util.java        |  26 +-
 .../apache/cassandra/batchlog/BatchTest.java    |  17 +-
 .../apache/cassandra/config/CFMetaDataTest.java |   2 +-
 .../apache/cassandra/cql3/CDCStatementTest.java |  10 +
 .../entities/RowUpdateBuilderTest.java          |  79 ----
 .../db/RecoveryManagerMissingHeaderTest.java    |   4 +-
 .../cassandra/db/RecoveryManagerTest.java       |   8 +-
 .../apache/cassandra/db/RowUpdateBuilder.java   | 196 ++++++++
 .../cassandra/db/compaction/TTLExpiryTest.java  |   2 +-
 .../db/partition/PartitionUpdateTest.java       |  23 +-
 .../org/apache/cassandra/hints/HintTest.java    |  56 +--
 .../hints/LegacyHintsMigratorTest.java          |   3 +-
 .../org/apache/cassandra/schema/DefsTest.java   |   2 +-
 .../schema/LegacySchemaMigratorTest.java        | 111 ++---
 .../cassandra/schema/SchemaKeyspaceTest.java    |   6 +-
 .../cassandra/service/DataResolverTest.java     |   2 +-
 .../streaming/StreamingTransferTest.java        |   2 +-
 35 files changed, 1448 insertions(+), 907 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 4330fde..388a290 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.8
+ * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
  * Fix hdr logging for single operation workloads (CASSANDRA-12145)
  * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
  * Increase size of flushExecutor thread pool (CASSANDRA-12071)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 7418f3a..d8d84f5 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -39,6 +39,9 @@ New features
      the data/cdc_raw directory until removed by the user and writes to CDC-enabled tables
      will be rejected with a WriteTimeoutException once cdc_total_space_in_mb is reached
      between unflushed CommitLogSegments and cdc_raw.
+     NOTE: CDC is disabled by default in the .yaml file. Do not enable CDC on a mixed-version
+     cluster as it will lead to exceptions which can interrupt traffic. Once all nodes
+     have been upgraded to 3.8 it is safe to enable this feature and restart the cluster.
 
 Upgrading
 ---------
@@ -48,13 +51,6 @@ Upgrading
       those under a different name, change your code to use the new names and
       drop the old versions, and this _before_ upgrade (see CASSANDRA-10783 for more
       details).
-    - Due to changes in schema migration handling and the storage format after 3.0, you will
-      see error messages such as:
-         "java.lang.RuntimeException: Unknown column cdc during deserialization"
-      in your system logs on a mixed-version cluster during upgrades. This error message
-      is harmless and due to the 3.8 nodes having cdc added to their schema tables while
-      the <3.8 nodes do not. This message should cease once all nodes are upgraded to 3.8.
-      As always, refrain from schema changes during cluster upgrades.
 
 Deprecation
 -----------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
index f5133bb..0bc9185 100644
--- a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
+++ b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
@@ -121,20 +121,15 @@ public class BatchlogManager implements BatchlogManagerMBean
 
     public static void store(Batch batch, boolean durableWrites)
     {
-        RowUpdateBuilder builder =
-            new RowUpdateBuilder(SystemKeyspace.Batches, batch.creationTime, batch.id)
-                .clustering()
-                .add("version", MessagingService.current_version);
-
-        for (ByteBuffer mutation : batch.encodedMutations)
-            builder.addListEntry("mutations", mutation);
+        List<ByteBuffer> mutations = new ArrayList<>(batch.encodedMutations.size() + batch.decodedMutations.size());
+        mutations.addAll(batch.encodedMutations);
 
         for (Mutation mutation : batch.decodedMutations)
         {
             try (DataOutputBuffer buffer = new DataOutputBuffer())
             {
                 Mutation.serializer.serialize(mutation, buffer, MessagingService.current_version);
-                builder.addListEntry("mutations", buffer.buffer());
+                mutations.add(buffer.buffer());
             }
             catch (IOException e)
             {
@@ -143,7 +138,13 @@ public class BatchlogManager implements BatchlogManagerMBean
             }
         }
 
-        builder.build().apply(durableWrites);
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(SystemKeyspace.Batches, batch.id);
+        builder.row()
+               .timestamp(batch.creationTime)
+               .add("version", MessagingService.current_version)
+               .appendAll("mutations", mutations);
+
+        builder.buildAsMutation().apply(durableWrites);
     }
 
     @VisibleForTesting

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java b/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
index dd19f19..3a8bf83 100644
--- a/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
+++ b/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
@@ -162,12 +162,13 @@ public final class LegacyBatchlogMigrator
     @SuppressWarnings("deprecation")
     static Mutation getStoreMutation(Batch batch, int version)
     {
-        return new RowUpdateBuilder(SystemKeyspace.LegacyBatchlog, batch.creationTime, batch.id)
-               .clustering()
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(SystemKeyspace.LegacyBatchlog, batch.id);
+        builder.row()
+               .timestamp(batch.creationTime)
                .add("written_at", new Date(batch.creationTime / 1000))
                .add("data", getSerializedMutations(version, batch.decodedMutations))
-               .add("version", version)
-               .build();
+               .add("version", version);
+        return builder.buildAsMutation();
     }
 
     @SuppressWarnings("deprecation")

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/Mutation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Mutation.java b/src/java/org/apache/cassandra/db/Mutation.java
index 61e5ee9..b8639a7 100644
--- a/src/java/org/apache/cassandra/db/Mutation.java
+++ b/src/java/org/apache/cassandra/db/Mutation.java
@@ -302,6 +302,72 @@ public class Mutation implements IMutation
         return buff.append("])").toString();
     }
 
+    /**
+     * Creates a new simple mutuation builder.
+     *
+     * @param keyspaceName the name of the keyspace this is a mutation for.
+     * @param partitionKey the key of partition this if a mutation for.
+     * @return a newly created builder.
+     */
+    public static SimpleBuilder simpleBuilder(String keyspaceName, DecoratedKey partitionKey)
+    {
+        return new SimpleBuilders.MutationBuilder(keyspaceName, partitionKey);
+    }
+
+    /**
+     * Interface for building mutations geared towards human.
+     * <p>
+     * This should generally not be used when performance matters too much, but provides a more convenient interface to
+     * build a mutation than using the class constructor when performance is not of the utmost importance.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * Sets the timestamp to use for the following additions to this builder or any derived (update or row) builder.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions to this builder or any derived (update or row) builder.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * ttl to be used for the row {@code LivenessInfo}.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Adds an update for table identified by the provided metadata and return a builder for that partition.
+         *
+         * @param metadata the metadata of the table for which to add an update.
+         * @return a builder for the partition identified by {@code metadata} (and the partition key for which this is a
+         * mutation of).
+         */
+        public PartitionUpdate.SimpleBuilder update(CFMetaData metadata);
+
+        /**
+         * Adds an update for table identified by the provided name and return a builder for that partition.
+         *
+         * @param tableName the name of the table for which to add an update.
+         * @return a builder for the partition identified by {@code metadata} (and the partition key for which this is a
+         * mutation of).
+         */
+        public PartitionUpdate.SimpleBuilder update(String tableName);
+
+        /**
+         * Build the mutation represented by this builder.
+         *
+         * @return the built mutation.
+         */
+        public Mutation build();
+    }
+
     public static class MutationSerializer implements IVersionedSerializer<Mutation>
     {
         public void serialize(Mutation mutation, DataOutputPlus out, int version) throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/RowUpdateBuilder.java b/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
deleted file mode 100644
index b414eba..0000000
--- a/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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.cassandra.db;
-
-import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.cassandra.cql3.ColumnIdentifier;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.db.marshal.SetType;
-import org.apache.cassandra.db.rows.*;
-import org.apache.cassandra.db.context.CounterContext;
-import org.apache.cassandra.db.partitions.*;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.ListType;
-import org.apache.cassandra.db.marshal.MapType;
-import org.apache.cassandra.utils.*;
-
-/**
- * Convenience object to create single row updates.
- *
- * This is meant for system table update, when performance is not of the utmost importance.
- */
-public class RowUpdateBuilder
-{
-    private final PartitionUpdate update;
-
-    private final long timestamp;
-    private final int ttl;
-    private final int localDeletionTime;
-
-    private final DeletionTime deletionTime;
-
-    private final Mutation mutation;
-
-    private Row.Builder regularBuilder;
-    private Row.Builder staticBuilder;
-
-    private boolean useRowMarker = true;
-
-    private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, int localDeletionTime, Mutation mutation)
-    {
-        this.update = update;
-
-        this.timestamp = timestamp;
-        this.ttl = ttl;
-        this.localDeletionTime = localDeletionTime;
-        this.deletionTime = new DeletionTime(timestamp, localDeletionTime);
-
-        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
-        // underneath (this class if for convenience, not performance)
-        this.mutation = mutation == null ? new Mutation(update.metadata().ksName, update.partitionKey()).add(update) : mutation;
-    }
-
-    private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, Mutation mutation)
-    {
-        this(update, timestamp, ttl, FBUtilities.nowInSeconds(), mutation);
-    }
-
-    private void startRow(Clustering clustering)
-    {
-        assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        assert regularBuilder == null : "Cannot add the clustering twice to the same row";
-
-        regularBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
-        regularBuilder.newRow(clustering);
-
-        // If a CQL table, add the "row marker"
-        if (update.metadata().isCQLTable() && useRowMarker)
-            regularBuilder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, localDeletionTime));
-    }
-
-    private Row.Builder builder()
-    {
-        assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        if (regularBuilder == null)
-        {
-            // we don't force people to call clustering() if the table has no clustering, so call it ourselves
-            assert update.metadata().comparator.size() == 0 : "Missing call to clustering()";
-            startRow(Clustering.EMPTY);
-        }
-        return regularBuilder;
-    }
-
-    private Row.Builder staticBuilder()
-    {
-        assert regularBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        if (staticBuilder == null)
-        {
-            staticBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
-            staticBuilder.newRow(Clustering.STATIC_CLUSTERING);
-        }
-        return staticBuilder;
-    }
-
-    private Row.Builder builder(ColumnDefinition c)
-    {
-        return c.isStatic() ? staticBuilder() : builder();
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
-    {
-        this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
-    {
-        this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
-    {
-        this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
-    {
-        this(new PartitionUpdate(metadata, makeKey(metadata, partitionKey), metadata.partitionColumns(), 1), timestamp, ttl, localDeletionTime, null);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Mutation mutation)
-    {
-        this(metadata, timestamp, LivenessInfo.NO_TTL, mutation);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Mutation mutation)
-    {
-        this(getOrAdd(metadata, mutation), timestamp, ttl, mutation);
-    }
-
-    public RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl)
-    {
-        this(update, timestamp, ttl, null);
-    }
-
-    // This must be called before any addition or deletion if used.
-    public RowUpdateBuilder noRowMarker()
-    {
-        this.useRowMarker = false;
-        return this;
-    }
-
-    public RowUpdateBuilder clustering(Object... clusteringValues)
-    {
-        assert clusteringValues.length == update.metadata().comparator.size()
-             : "Invalid clustering values length. Expected: " + update.metadata().comparator.size() + " got: " + clusteringValues.length;
-
-        startRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
-        return this;
-    }
-
-    public Mutation build()
-    {
-        Row.Builder builder = regularBuilder == null ? staticBuilder : regularBuilder;
-        if (builder != null)
-            update.add(builder.build());
-        return mutation;
-    }
-
-    public PartitionUpdate buildUpdate()
-    {
-        build();
-        return update;
-    }
-
-    private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues)
-    {
-        assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty());
-
-        boolean isStatic = clusteringValues.length != update.metadata().comparator.size();
-        Row.Builder builder = BTreeRow.sortedBuilder();
-
-        if (isStatic)
-            builder.newRow(Clustering.STATIC_CLUSTERING);
-        else
-            builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
-        builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime)));
-
-        update.add(builder.build());
-    }
-
-    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Mutation mutation, Object... clusteringValues)
-    {
-        deleteRow(getOrAdd(metadata, mutation), timestamp, FBUtilities.nowInSeconds(), clusteringValues);
-        return mutation;
-    }
-
-    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
-    {
-        return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
-    }
-
-    public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
-    {
-        PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 0);
-        deleteRow(update, timestamp, localDeletionTime, clusteringValues);
-        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
-        // underneath (this class if for convenience, not performance)
-        return new Mutation(update.metadata().ksName, update.partitionKey()).add(update);
-    }
-
-    private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
-    {
-        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
-            return (DecoratedKey)partitionKey[0];
-
-        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
-        return metadata.decorateKey(key);
-    }
-
-    private static PartitionUpdate getOrAdd(CFMetaData metadata, Mutation mutation)
-    {
-        PartitionUpdate upd = mutation.get(metadata);
-        if (upd == null)
-        {
-            upd = new PartitionUpdate(metadata, mutation.key(), metadata.partitionColumns(), 1);
-            mutation.add(upd);
-        }
-        return upd;
-    }
-
-    public RowUpdateBuilder resetCollection(String columnName)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type.isCollection() && c.type.isMultiCell();
-        builder(c).addComplexDeletion(c, new DeletionTime(timestamp - 1, localDeletionTime));
-        return this;
-    }
-
-    public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
-    {
-        update.add(rt);
-        return this;
-    }
-
-    public RowUpdateBuilder addRangeTombstone(Slice slice)
-    {
-        return addRangeTombstone(new RangeTombstone(slice, deletionTime));
-    }
-
-    public RowUpdateBuilder addRangeTombstone(Object start, Object end)
-    {
-        ClusteringComparator cmp = update.metadata().comparator;
-        Slice slice = Slice.make(cmp.make(start), cmp.make(end));
-        return addRangeTombstone(slice);
-    }
-
-    public RowUpdateBuilder add(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        return add(c, value);
-    }
-
-    private Cell makeCell(ColumnDefinition c, ByteBuffer value, CellPath path)
-    {
-        return value == null
-             ? BufferCell.tombstone(c, timestamp, localDeletionTime)
-             : (ttl == LivenessInfo.NO_TTL ? BufferCell.live(c, timestamp, value, path) : BufferCell.expiring(c, timestamp, ttl, localDeletionTime, value, path));
-    }
-
-    public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
-    {
-        assert columnDefinition.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + columnDefinition + " since no clustering hasn't been provided";
-        builder(columnDefinition).addCell(makeCell(columnDefinition, bb(value, columnDefinition.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder delete(String columnName)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        return delete(c);
-    }
-
-    public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
-    {
-        return add(columnDefinition, null);
-    }
-
-    private static ByteBuffer bb(Object value, AbstractType<?> type)
-    {
-        if (value == null)
-            return null;
-
-        if (value instanceof ByteBuffer)
-            return (ByteBuffer)value;
-
-        if (type.isCounter())
-        {
-            // See UpdateParameters.addCounter()
-            assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
-            return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
-        }
-        return ((AbstractType)type).decompose(value);
-    }
-
-    public RowUpdateBuilder map(String columnName, Map<?, ?> map)
-    {
-        resetCollection(columnName);
-        for (Map.Entry<?, ?> entry : map.entrySet())
-            addMapEntry(columnName, entry.getKey(), entry.getValue());
-        return this;
-    }
-
-    public RowUpdateBuilder set(String columnName, Set<?> set)
-    {
-        resetCollection(columnName);
-        for (Object element : set)
-            addSetEntry(columnName, element);
-        return this;
-    }
-
-    public RowUpdateBuilder frozenList(String columnName, List<?> list)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof ListType && !c.type.isMultiCell() : "Column " + c + " is not a frozen list";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(list), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder frozenSet(String columnName, Set<?> set)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof SetType && !c.type.isMultiCell() : "Column " + c + " is not a frozen set";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(set), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder frozenMap(String columnName, Map<?, ?> map)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof MapType && !c.type.isMultiCell() : "Column " + c + " is not a frozen map";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(map), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder addMapEntry(String columnName, Object key, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof MapType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen map";
-        MapType mt = (MapType)c.type;
-        builder(c).addCell(makeCell(c, bb(value, mt.getValuesType()), CellPath.create(bb(key, mt.getKeysType()))));
-        return this;
-    }
-
-    public RowUpdateBuilder addListEntry(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof ListType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen list";
-        ListType lt = (ListType)c.type;
-        builder(c).addCell(makeCell(c, bb(value, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
-        return this;
-    }
-
-    public RowUpdateBuilder addSetEntry(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof SetType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen set";
-        SetType st = (SetType)c.type;
-        builder(c).addCell(makeCell(c, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(bb(value, st.getElementsType()))));
-        return this;
-    }
-
-    private ColumnDefinition getDefinition(String name)
-    {
-        return update.metadata().getColumnDefinition(new ColumnIdentifier(name, true));
-    }
-
-    public UnfilteredRowIterator unfilteredIterator()
-    {
-        return update.unfilteredIterator();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/SimpleBuilders.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SimpleBuilders.java b/src/java/org/apache/cassandra/db/SimpleBuilders.java
new file mode 100644
index 0000000..6e65743
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/SimpleBuilders.java
@@ -0,0 +1,461 @@
+/*
+ * 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.cassandra.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.db.context.CounterContext;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
+import org.apache.cassandra.db.rows.BTreeRow;
+import org.apache.cassandra.db.rows.BufferCell;
+import org.apache.cassandra.db.rows.Cell;
+import org.apache.cassandra.db.rows.CellPath;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.CounterId;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.UUIDGen;
+
+public abstract class SimpleBuilders
+{
+    private SimpleBuilders()
+    {
+    }
+
+    private static DecoratedKey makePartitonKey(CFMetaData metadata, Object... partitionKey)
+    {
+        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
+            return (DecoratedKey)partitionKey[0];
+
+        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
+        return metadata.decorateKey(key);
+    }
+
+    private static Clustering makeClustering(CFMetaData metadata, Object... clusteringColumns)
+    {
+        if (clusteringColumns.length == 1 && clusteringColumns[0] instanceof Clustering)
+            return (Clustering)clusteringColumns[0];
+
+        if (clusteringColumns.length == 0)
+        {
+            // If the table has clustering columns, passing no values is for updating the static values, so check we
+            // do have some static columns defined.
+            assert metadata.comparator.size() == 0 || !metadata.partitionColumns().statics.isEmpty();
+            return metadata.comparator.size() == 0 ? Clustering.EMPTY : Clustering.STATIC_CLUSTERING;
+        }
+        else
+        {
+            return metadata.comparator.make(clusteringColumns);
+        }
+    }
+
+    private static class AbstractBuilder<T>
+    {
+        protected long timestamp = FBUtilities.timestampMicros();
+        protected int ttl = 0;
+        protected int nowInSec = FBUtilities.nowInSeconds();
+
+        protected void copyParams(AbstractBuilder<?> other)
+        {
+            other.timestamp = timestamp;
+            other.ttl = ttl;
+            other.nowInSec = nowInSec;
+        }
+
+        public T timestamp(long timestamp)
+        {
+            this.timestamp = timestamp;
+            return (T)this;
+        }
+
+        public T ttl(int ttl)
+        {
+            this.ttl = ttl;
+            return (T)this;
+        }
+
+        public T nowInSec(int nowInSec)
+        {
+            this.nowInSec = nowInSec;
+            return (T)this;
+        }
+    }
+
+    public static class MutationBuilder extends AbstractBuilder<Mutation.SimpleBuilder> implements Mutation.SimpleBuilder
+    {
+        private final String keyspaceName;
+        private final DecoratedKey key;
+
+        private final Map<UUID, PartitionUpdateBuilder> updateBuilders = new HashMap<>();
+
+        public MutationBuilder(String keyspaceName, DecoratedKey key)
+        {
+            this.keyspaceName = keyspaceName;
+            this.key = key;
+        }
+
+        public PartitionUpdate.SimpleBuilder update(CFMetaData metadata)
+        {
+            assert metadata.ksName.equals(keyspaceName);
+
+            PartitionUpdateBuilder builder = updateBuilders.get(metadata.cfId);
+            if (builder == null)
+            {
+                builder = new PartitionUpdateBuilder(metadata, key);
+                updateBuilders.put(metadata.cfId, builder);
+            }
+
+            copyParams(builder);
+
+            return builder;
+        }
+
+        public PartitionUpdate.SimpleBuilder update(String tableName)
+        {
+            CFMetaData metadata = Schema.instance.getCFMetaData(keyspaceName, tableName);
+            assert metadata != null : "Unknown table " + tableName + " in keyspace " + keyspaceName;
+            return update(metadata);
+        }
+
+        public Mutation build()
+        {
+            assert !updateBuilders.isEmpty() : "Cannot create empty mutation";
+
+            if (updateBuilders.size() == 1)
+                return new Mutation(updateBuilders.values().iterator().next().build());
+
+            Mutation mutation = new Mutation(keyspaceName, key);
+            for (PartitionUpdateBuilder builder : updateBuilders.values())
+                mutation.add(builder.build());
+            return mutation;
+        }
+    }
+
+    public static class PartitionUpdateBuilder extends AbstractBuilder<PartitionUpdate.SimpleBuilder> implements PartitionUpdate.SimpleBuilder
+    {
+        private final CFMetaData metadata;
+        private final DecoratedKey key;
+        private final Map<Clustering, RowBuilder> rowBuilders = new HashMap<>();
+        private List<RTBuilder> rangeBuilders = null; // We use that rarely, so create lazily
+
+        private DeletionTime partitionDeletion = DeletionTime.LIVE;
+
+        public PartitionUpdateBuilder(CFMetaData metadata, Object... partitionKeyValues)
+        {
+            this.metadata = metadata;
+            this.key = makePartitonKey(metadata, partitionKeyValues);
+        }
+
+        public CFMetaData metadata()
+        {
+            return metadata;
+        }
+
+        public Row.SimpleBuilder row(Object... clusteringValues)
+        {
+            Clustering clustering = makeClustering(metadata, clusteringValues);
+            RowBuilder builder = rowBuilders.get(clustering);
+            if (builder == null)
+            {
+                builder = new RowBuilder(metadata, clustering);
+                rowBuilders.put(clustering, builder);
+            }
+
+            copyParams(builder);
+
+            return builder;
+        }
+
+        public PartitionUpdate.SimpleBuilder delete()
+        {
+            this.partitionDeletion = new DeletionTime(timestamp, nowInSec);
+            return this;
+        }
+
+        public RangeTombstoneBuilder addRangeTombstone()
+        {
+            if (rangeBuilders == null)
+                rangeBuilders = new ArrayList<>();
+
+            RTBuilder builder = new RTBuilder(metadata.comparator, new DeletionTime(timestamp, nowInSec));
+            rangeBuilders.add(builder);
+            return builder;
+        }
+
+        public PartitionUpdate build()
+        {
+            // Collect all updated columns
+            PartitionColumns.Builder columns = PartitionColumns.builder();
+            for (RowBuilder builder : rowBuilders.values())
+                columns.addAll(builder.columns());
+
+            // Note that rowBuilders.size() could include the static column so could be 1 off the really need capacity
+            // of the final PartitionUpdate, but as that's just a sizing hint, we'll live.
+            PartitionUpdate update = new PartitionUpdate(metadata, key, columns.build(), rowBuilders.size());
+
+            update.addPartitionDeletion(partitionDeletion);
+            if (rangeBuilders != null)
+            {
+                for (RTBuilder builder : rangeBuilders)
+                    update.add(builder.build());
+            }
+
+            for (RowBuilder builder : rowBuilders.values())
+                update.add(builder.build());
+
+            return update;
+        }
+
+        public Mutation buildAsMutation()
+        {
+            return new Mutation(build());
+        }
+
+        private static class RTBuilder implements RangeTombstoneBuilder
+        {
+            private final ClusteringComparator comparator;
+            private final DeletionTime deletionTime;
+
+            private Object[] start;
+            private Object[] end;
+
+            private boolean startInclusive = true;
+            private boolean endInclusive = true;
+
+            private RTBuilder(ClusteringComparator comparator, DeletionTime deletionTime)
+            {
+                this.comparator = comparator;
+                this.deletionTime = deletionTime;
+            }
+
+            public RangeTombstoneBuilder start(Object... values)
+            {
+                this.start = values;
+                return this;
+            }
+
+            public RangeTombstoneBuilder end(Object... values)
+            {
+                this.end = values;
+                return this;
+            }
+
+            public RangeTombstoneBuilder inclStart()
+            {
+                this.startInclusive = true;
+                return this;
+            }
+
+            public RangeTombstoneBuilder exclStart()
+            {
+                this.startInclusive = false;
+                return this;
+            }
+
+            public RangeTombstoneBuilder inclEnd()
+            {
+                this.endInclusive = true;
+                return this;
+            }
+
+            public RangeTombstoneBuilder exclEnd()
+            {
+                this.endInclusive = false;
+                return this;
+            }
+
+            private RangeTombstone build()
+            {
+                ClusteringBound startBound = ClusteringBound.create(comparator, true, startInclusive, start);
+                ClusteringBound endBound = ClusteringBound.create(comparator, false, endInclusive, end);
+                return new RangeTombstone(Slice.make(startBound, endBound), deletionTime);
+            }
+        }
+    }
+
+    public static class RowBuilder extends AbstractBuilder<Row.SimpleBuilder> implements Row.SimpleBuilder
+    {
+        private final CFMetaData metadata;
+
+        private final Set<ColumnDefinition> columns = new HashSet<>();
+        private final Row.Builder builder;
+
+        private boolean initiated;
+        private boolean noPrimaryKeyLivenessInfo;
+
+        public RowBuilder(CFMetaData metadata, Object... clusteringColumns)
+        {
+            this.metadata = metadata;
+            this.builder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
+
+            this.builder.newRow(makeClustering(metadata, clusteringColumns));
+        }
+
+        Set<ColumnDefinition> columns()
+        {
+            return columns;
+        }
+
+        private void maybeInit()
+        {
+            // We're working around the fact that Row.Builder requires that addPrimaryKeyLivenessInfo() and
+            // addRowDeletion() are called before any cell addition (which is done so the builder can more easily skip
+            // shadowed cells).
+            if (initiated)
+                return;
+
+            // If a CQL table, add the "row marker"
+            if (metadata.isCQLTable() && !noPrimaryKeyLivenessInfo)
+                builder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, nowInSec));
+
+            initiated = true;
+        }
+
+        public Row.SimpleBuilder add(String columnName, Object value)
+        {
+            return add(columnName, value, true);
+        }
+
+        public Row.SimpleBuilder appendAll(String columnName, Object value)
+        {
+            return add(columnName, value, false);
+        }
+
+        private Row.SimpleBuilder add(String columnName, Object value, boolean overwriteForCollection)
+        {
+            maybeInit();
+            ColumnDefinition column = getColumn(columnName);
+
+            if (!overwriteForCollection && !(column.type.isMultiCell() && column.type.isCollection()))
+                throw new IllegalArgumentException("appendAll() can only be called on non-frozen colletions");
+
+            columns.add(column);
+
+            if (!column.type.isMultiCell())
+            {
+                builder.addCell(cell(column, toByteBuffer(value, column.type), null));
+                return this;
+            }
+
+            assert column.type instanceof CollectionType : "Collection are the only multi-cell types supported so far";
+
+            if (value == null)
+            {
+                builder.addComplexDeletion(column, new DeletionTime(timestamp, nowInSec));
+                return this;
+            }
+
+            // Erase previous entry if any.
+            if (overwriteForCollection)
+                builder.addComplexDeletion(column, new DeletionTime(timestamp - 1, nowInSec));
+            switch (((CollectionType)column.type).kind)
+            {
+                case LIST:
+                    ListType lt = (ListType)column.type;
+                    assert value instanceof List;
+                    for (Object elt : (List)value)
+                        builder.addCell(cell(column, toByteBuffer(elt, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
+                    break;
+                case SET:
+                    SetType st = (SetType)column.type;
+                    assert value instanceof Set;
+                    for (Object elt : (Set)value)
+                        builder.addCell(cell(column, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(toByteBuffer(elt, st.getElementsType()))));
+                    break;
+                case MAP:
+                    MapType mt = (MapType)column.type;
+                    assert value instanceof Map;
+                    for (Map.Entry entry : ((Map<?, ?>)value).entrySet())
+                        builder.addCell(cell(column,
+                                             toByteBuffer(entry.getValue(), mt.getValuesType()),
+                                             CellPath.create(toByteBuffer(entry.getKey(), mt.getKeysType()))));
+                    break;
+                default:
+                    throw new AssertionError();
+            }
+            return this;
+        }
+
+        public Row.SimpleBuilder delete()
+        {
+            assert !initiated : "If called, delete() should be called before any other column value addition";
+            builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, nowInSec)));
+            return this;
+        }
+
+        public Row.SimpleBuilder delete(String columnName)
+        {
+            return add(columnName, null);
+        }
+
+        public Row.SimpleBuilder noPrimaryKeyLivenessInfo()
+        {
+            this.noPrimaryKeyLivenessInfo = true;
+            return this;
+        }
+
+        public Row build()
+        {
+            maybeInit();
+            return builder.build();
+        }
+
+        private ColumnDefinition getColumn(String columnName)
+        {
+            ColumnDefinition column = metadata.getColumnDefinition(new ColumnIdentifier(columnName, true));
+            assert column != null : "Cannot find column " + columnName;
+            assert !column.isPrimaryKeyColumn();
+            assert !column.isStatic() || builder.clustering() == Clustering.STATIC_CLUSTERING : "Cannot add non-static column to static-row";
+            return column;
+        }
+
+        private Cell cell(ColumnDefinition column, ByteBuffer value, CellPath path)
+        {
+            if (value == null)
+                return BufferCell.tombstone(column, timestamp, nowInSec, path);
+
+            return ttl == LivenessInfo.NO_TTL
+                 ? BufferCell.live(column, timestamp, value, path)
+                 : BufferCell.expiring(column, timestamp, ttl, nowInSec, value, path);
+        }
+
+        private ByteBuffer toByteBuffer(Object value, AbstractType<?> type)
+        {
+            if (value == null)
+                return null;
+
+            if (value instanceof ByteBuffer)
+                return (ByteBuffer)value;
+
+            if (type.isCounter())
+            {
+                // See UpdateParameters.addCounter()
+                assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
+                return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
+            }
+
+            return ((AbstractType)type).decompose(value);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java
index 584279d..36629a1 100644
--- a/src/java/org/apache/cassandra/db/SystemKeyspace.java
+++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java
@@ -44,6 +44,7 @@ import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.commitlog.CommitLogPosition;
 import org.apache.cassandra.db.compaction.CompactionHistoryTabularData;
 import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.db.rows.Rows;
 import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.dht.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
@@ -1233,11 +1234,11 @@ public final class SystemKeyspace
         {
             Range<Token> range = entry.getKey();
             Pair<Long, Long> values = entry.getValue();
-            new RowUpdateBuilder(SizeEstimates, timestamp, mutation)
-                .clustering(table, range.left.toString(), range.right.toString())
-                .add("partitions_count", values.left)
-                .add("mean_partition_size", values.right)
-                .build();
+            update.add(Rows.simpleBuilder(SizeEstimates, table, range.left.toString(), range.right.toString())
+                           .timestamp(timestamp)
+                           .add("partitions_count", values.left)
+                           .add("mean_partition_size", values.right)
+                           .build());
         }
 
         mutation.apply();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 1c05f3c..954168d 100644
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@ -169,7 +169,7 @@ public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
 
     public UnfilteredRowIterator unfilteredIterator()
     {
-        return unfilteredIterator(ColumnFilter.all(metadata()), Slices.ALL, false);
+        return unfilteredIterator(ColumnFilter.selection(columns()), Slices.ALL, false);
     }
 
     public UnfilteredRowIterator unfilteredIterator(ColumnFilter selection, Slices slices, boolean reversed)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java b/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
index d18392c..7796fd9 100644
--- a/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
+++ b/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
@@ -614,6 +614,160 @@ public class PartitionUpdate extends AbstractBTreePartition
         return sb.toString();
     }
 
+    /**
+     * Creates a new simple partition update builder.
+     *
+     * @param metadata the metadata for the table this is a partition of.
+     * @param partitionKeyValues the values for partition key columns identifying this partition. The values for each
+     * partition key column can be passed either directly as {@code ByteBuffer} or using a "native" value (int for
+     * Int32Type, string for UTF8Type, ...). It is also allowed to pass a single {@code DecoratedKey} value directly.
+     * @return a newly created builder.
+     */
+    public static SimpleBuilder simpleBuilder(CFMetaData metadata, Object... partitionKeyValues)
+    {
+        return new SimpleBuilders.PartitionUpdateBuilder(metadata, partitionKeyValues);
+    }
+
+    /**
+     * Interface for building partition updates geared towards human.
+     * <p>
+     * This should generally not be used when performance matters too much, but provides a more convenient interface to
+     * build an update than using the class constructor when performance is not of the utmost importance.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * The metadata of the table this is a builder on.
+         */
+        public CFMetaData metadata();
+
+        /**
+         * Sets the timestamp to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Sets the current time to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param nowInSec the current time to use for following additions. If the current time hasn't been set, the current
+         * time in seconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder nowInSec(int nowInSec);
+
+        /**
+         * Adds the row identifier by the provided clustering and return a builder for that row.
+         *
+         * @param clusteringValues the value for the clustering columns of the row to add to this build. There may be no
+         * values if either the table has no clustering column, or if you want to edit the static row. Note that as a
+         * shortcut it is also allowed to pass a {@code Clustering} object directly, in which case that should be the
+         * only argument.
+         * @return a builder for the row identified by {@code clusteringValues}.
+         */
+        public Row.SimpleBuilder row(Object... clusteringValues);
+
+        /**
+         * Deletes the partition identified by this builder (using a partition level deletion).
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder delete();
+
+        /**
+         * Adds a new range tombstone to this update, returning a builder for that range.
+         *
+         * @return the range tombstone builder for the newly added range.
+         */
+        public RangeTombstoneBuilder addRangeTombstone();
+
+        /**
+         * Build the update represented by this builder.
+         *
+         * @return the built update.
+         */
+        public PartitionUpdate build();
+
+        /**
+         * As shortcut for {@code new Mutation(build())}.
+         *
+         * @return the built update, wrapped in a {@code Mutation}.
+         */
+        public Mutation buildAsMutation();
+
+        /**
+         * Interface to build range tombstone.
+         *
+         * By default, if no other methods are called, the represented range is inclusive of both start and end and
+         * includes everything (its start is {@code BOTTOM} and it's end is {@code TOP}).
+         */
+        public interface RangeTombstoneBuilder
+        {
+            /**
+             * Sets the start for the built range using the provided values.
+             *
+             * @param values the value for the start of the range. They act like the {@code clusteringValues} argument
+             * of the {@link PartitionUpdate.SimpleBuilder#row()} method, except that it doesn't have to be a full
+             * clustering, it can only be a prefix.
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder start(Object... values);
+
+            /**
+             * Sets the end for the built range using the provided values.
+             *
+             * @param values the value for the end of the range. They act like the {@code clusteringValues} argument
+             * of the {@link PartitionUpdate.SimpleBuilder#row()} method, except that it doesn't have to be a full
+             * clustering, it can only be a prefix.
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder end(Object... values);
+
+            /**
+             * Sets the start of this range as inclusive.
+             * <p>
+             * This is the default and don't need to be called, but can for explicitness.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder inclStart();
+
+            /**
+             * Sets the start of this range as exclusive.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder exclStart();
+
+            /**
+             * Sets the end of this range as inclusive.
+             * <p>
+             * This is the default and don't need to be called, but can for explicitness.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder inclEnd();
+
+            /**
+             * Sets the end of this range as exclusive.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder exclEnd();
+        }
+    }
+
     public static class PartitionUpdateSerializer
     {
         public void serialize(PartitionUpdate update, DataOutputPlus out, int version) throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Row.java b/src/java/org/apache/cassandra/db/rows/Row.java
index 4fc3e22..7e6d141 100644
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@ -468,6 +468,105 @@ public interface Row extends Unfiltered, Collection<ColumnData>
     }
 
     /**
+     * Row builder interface geared towards human.
+     * <p>
+     * Where the {@link Builder} deals with building rows efficiently from internal objects ({@code Cell}, {@code
+     * LivenessInfo}, ...), the {@code SimpleBuilder} is geared towards building rows from string column name and
+     * 'native' values (string for text, ints for numbers, et...). In particular, it is meant to be convenient, not
+     * efficient, and should be used only in place where performance is not of the utmost importance (it is used to
+     * build schema mutation for instance).
+     * <p>
+     * Also note that contrarily to {@link Builder}, the {@code SimpleBuilder} API has no {@code newRow()} method: it is
+     * expected that the clustering of the row built is provided by the constructor of the builder.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * Sets the timestamp to use for the following additions.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * timestamp to be used for the row {@code LivenessInfo}.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * ttl to be used for the row {@code LivenessInfo}.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Adds a value to a given column.
+         *
+         * @param columnName the name of the column for which to add a new value.
+         * @param value the value to add, which must be of the proper type for {@code columnName}. This can be {@code
+         * null} in which case the this is equivalent to {@code delete(columnName)}.
+         * @return this builder.
+         */
+        public SimpleBuilder add(String columnName, Object value);
+
+        /**
+         * Appends new values to a given non-frozen collection column.
+         * <p>
+         * This method is similar to {@code add()} but the collection elements added through this method are "appended"
+         * to any pre-exising elements. In other words, this is like {@code add()} except that it doesn't delete the
+         * previous value of the collection. This can only be called on non-frozen collection columns.
+         * <p>
+         * Note that this method can be used in replacement of {@code add()} if you know that there can't be any
+         * pre-existing value for that column, in which case this is slightly less expensive as it avoid the collection
+         * tombstone inherent to {@code add()}.
+         *
+         * @param columnName the name of the column for which to add a new value, which must be a non-frozen collection.
+         * @param value the value to add, which must be of the proper type for {@code columnName} (in other words, it
+         * <b>must</b> be a collection).
+         * @return this builder.
+         *
+         * @throws IllegalArgumentException if columnName is not a non-frozen collection column.
+         */
+        public SimpleBuilder appendAll(String columnName, Object value);
+
+        /**
+         * Deletes the whole row.
+         * <p>
+         * If called, this is generally the only method called on the builder (outside of {@code timestamp()}.
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder delete();
+
+        /**
+         * Removes the value for a given column (creating a tombstone).
+         *
+         * @param columnName the name of the column to delete.
+         * @return this builder.
+         */
+        public SimpleBuilder delete(String columnName);
+
+        /**
+         * Don't include any primary key {@code LivenessInfo} in the built row.
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder noPrimaryKeyLivenessInfo();
+
+        /**
+         * Returns the built row.
+         *
+         * @return the built row.
+         */
+        public Row build();
+    }
+
+    /**
      * Utility class to help merging rows from multiple inputs (UnfilteredRowIterators).
      */
     public static class Merger

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Rows.java b/src/java/org/apache/cassandra/db/rows/Rows.java
index e325091..976d37e 100644
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@ -22,6 +22,7 @@ import java.util.*;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.PeekingIterator;
 
+import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.partitions.PartitionStatisticsCollector;
@@ -59,6 +60,21 @@ public abstract class Rows
     }
 
     /**
+     * Creates a new simple row builder.
+     *
+     * @param metadata the metadata of the table this is a row of.
+     * @param clusteringValues the value for the clustering columns of the row to add to this build. There may be no
+     * values if either the table has no clustering column, or if you want to edit the static row. Note that as a
+     * shortcut it is also allowed to pass a {@code Clustering} object directly, in which case that should be the
+     * only argument.
+     * @return a newly created builder.
+     */
+    public static Row.SimpleBuilder simpleBuilder(CFMetaData metadata, Object... clusteringValues)
+    {
+        return new SimpleBuilders.RowBuilder(metadata, clusteringValues);
+    }
+
+    /**
      * Collect statistics on a given row.
      *
      * @param row the row for which to collect stats.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/transform/BaseRows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/transform/BaseRows.java b/src/java/org/apache/cassandra/db/transform/BaseRows.java
index fb3b9f9..ce4e458 100644
--- a/src/java/org/apache/cassandra/db/transform/BaseRows.java
+++ b/src/java/org/apache/cassandra/db/transform/BaseRows.java
@@ -105,7 +105,8 @@ implements BaseRowIterator<R>
         super.add(transformation);
 
         // transform any existing data
-        staticRow = transformation.applyToStatic(staticRow);
+        if (staticRow != null)
+            staticRow = transformation.applyToStatic(staticRow);
         next = applyOne(next, transformation);
         partitionKey = transformation.applyToPartitionKey(partitionKey);
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
index 93591f0..7cc822f 100644
--- a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
+++ b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
@@ -148,20 +148,20 @@ public final class LegacySchemaMigrator
     {
         logger.info("Migrating keyspace {}", keyspace);
 
-        Mutation mutation = SchemaKeyspace.makeCreateKeyspaceMutation(keyspace.name, keyspace.params, keyspace.timestamp);
+        Mutation.SimpleBuilder builder = SchemaKeyspace.makeCreateKeyspaceMutation(keyspace.name, keyspace.params, keyspace.timestamp);
         for (Table table : keyspace.tables)
-            SchemaKeyspace.addTableToSchemaMutation(table.metadata, table.timestamp, true, mutation);
+            SchemaKeyspace.addTableToSchemaMutation(table.metadata, true, builder.timestamp(table.timestamp));
 
         for (Type type : keyspace.types)
-            SchemaKeyspace.addTypeToSchemaMutation(type.metadata, type.timestamp, mutation);
+            SchemaKeyspace.addTypeToSchemaMutation(type.metadata, builder.timestamp(type.timestamp));
 
         for (Function function : keyspace.functions)
-            SchemaKeyspace.addFunctionToSchemaMutation(function.metadata, function.timestamp, mutation);
+            SchemaKeyspace.addFunctionToSchemaMutation(function.metadata, builder.timestamp(function.timestamp));
 
         for (Aggregate aggregate : keyspace.aggregates)
-            SchemaKeyspace.addAggregateToSchemaMutation(aggregate.metadata, aggregate.timestamp, mutation);
+            SchemaKeyspace.addAggregateToSchemaMutation(aggregate.metadata, builder.timestamp(aggregate.timestamp));
 
-        mutation.apply();
+        builder.build().apply();
     }
 
     /*


[10/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index 8e3961e..7a90dab 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -266,8 +266,8 @@ public final class SchemaKeyspace
         }
 
         // (+1 to timestamp to make sure we don't get shadowed by the tombstones we just added)
-        makeCreateKeyspaceMutation(system, timestamp + 1).apply();
-        makeCreateKeyspaceMutation(schema, timestamp + 1).apply();
+        makeCreateKeyspaceMutation(system, timestamp + 1).build().apply();
+        makeCreateKeyspaceMutation(schema, timestamp + 1).build().apply();
     }
 
     public static void truncate()
@@ -377,11 +377,6 @@ public final class SchemaKeyspace
         }
     }
 
-    private static ByteBuffer getSchemaKSKey(String ksName)
-    {
-        return AsciiType.instance.fromString(ksName);
-    }
-
     private static boolean isSystemKeyspaceSchemaPartition(DecoratedKey partitionKey)
     {
         return Schema.isSystemKeyspace(UTF8Type.instance.compose(partitionKey.getKey()));
@@ -391,152 +386,152 @@ public final class SchemaKeyspace
      * Schema entities to mutations
      */
 
-    public static Mutation makeCreateKeyspaceMutation(String name, KeyspaceParams params, long timestamp)
+    private static DecoratedKey decorate(CFMetaData metadata, Object value)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Keyspaces, timestamp, name).clustering();
-        return adder.add(KeyspaceParams.Option.DURABLE_WRITES.toString(), params.durableWrites)
-                    .frozenMap(KeyspaceParams.Option.REPLICATION.toString(), params.replication.asMap())
-                    .build();
+        return metadata.decorateKey(((AbstractType)metadata.getKeyValidator()).decompose(value));
     }
 
-    public static Mutation makeCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateKeyspaceMutation(String name, KeyspaceParams params, long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(Keyspaces.ksName, decorate(Keyspaces, name))
+                                                 .timestamp(timestamp);
 
-        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, timestamp, true, mutation));
-        keyspace.views.forEach(view -> addViewToSchemaMutation(view, timestamp, true, mutation));
-        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, timestamp, mutation));
-        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, timestamp, mutation));
-        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, timestamp, mutation));
+        builder.update(Keyspaces)
+               .row()
+               .add(KeyspaceParams.Option.DURABLE_WRITES.toString(), params.durableWrites)
+               .add(KeyspaceParams.Option.REPLICATION.toString(), params.replication.asMap());
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeDropKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
     {
-        int nowInSec = FBUtilities.nowInSeconds();
-        Mutation mutation = new Mutation(NAME, Keyspaces.decorateKey(getSchemaKSKey(keyspace.name)));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        for (CFMetaData schemaTable : ALL_TABLE_METADATA)
-            mutation.add(PartitionUpdate.fullPartitionDelete(schemaTable, mutation.key(), timestamp, nowInSec));
+        keyspace.tables.forEach(table -> addTableToSchemaMutation(table, true, builder));
+        keyspace.views.forEach(view -> addViewToSchemaMutation(view, true, builder));
+        keyspace.types.forEach(type -> addTypeToSchemaMutation(type, builder));
+        keyspace.functions.udfs().forEach(udf -> addFunctionToSchemaMutation(udf, builder));
+        keyspace.functions.udas().forEach(uda -> addAggregateToSchemaMutation(uda, builder));
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
+    public static Mutation.SimpleBuilder makeDropKeyspaceMutation(KeyspaceMetadata keyspace, long timestamp)
     {
-        // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addTypeToSchemaMutation(type, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(NAME, decorate(Keyspaces, keyspace.name))
+                                                 .timestamp(timestamp);
+
+        for (CFMetaData schemaTable : ALL_TABLE_METADATA)
+            builder.update(schemaTable).delete();
+
+        return builder;
     }
 
-    static void addTypeToSchemaMutation(UserType type, long timestamp, Mutation mutation)
+    public static Mutation.SimpleBuilder makeCreateTypeMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Types, timestamp, mutation)
-                                 .clustering(type.getNameAsString())
-                                 .frozenList("field_names", type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList()))
-                                 .frozenList("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList()));
-
-        adder.build();
+        // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addTypeToSchemaMutation(type, builder);
+        return builder;
     }
 
-    private static String bbToString(ByteBuffer bb)
+    static void addTypeToSchemaMutation(UserType type, Mutation.SimpleBuilder mutation)
     {
-        try
-        {
-            return ByteBufferUtil.string(bb);
-        }
-        catch (CharacterCodingException e)
-        {
-            throw new RuntimeException(e);
-        }
+        mutation.update(Types)
+                .row(type.getNameAsString())
+                .add("field_names", type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList()))
+                .add("field_types", type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList()));
     }
 
-    public static Mutation dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
+    public static Mutation.SimpleBuilder dropTypeFromSchemaMutation(KeyspaceMetadata keyspace, UserType type, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Types, timestamp, mutation, type.name);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Types).row(type.name).delete();
+        return builder;
     }
 
-    public static Mutation makeCreateTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addTableToSchemaMutation(table, timestamp, true, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addTableToSchemaMutation(table, true, builder);
+        return builder;
     }
 
-    static void addTableToSchemaMutation(CFMetaData table, long timestamp, boolean withColumnsAndTriggers, Mutation mutation)
+    static void addTableToSchemaMutation(CFMetaData table, boolean withColumnsAndTriggers, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Tables, timestamp, mutation).clustering(table.cfName);
-
-        addTableParamsToSchemaMutation(table.params, adder);
+        Row.SimpleBuilder rowBuilder = builder.update(Tables)
+                                              .row(table.cfName)
+                                              .add("id", table.cfId)
+                                              .add("flags", CFMetaData.flagsToStrings(table.flags()));
 
-        adder.add("id", table.cfId)
-             .frozenSet("flags", CFMetaData.flagsToStrings(table.flags()))
-             .build();
+        addTableParamsToRowBuilder(table.params, rowBuilder);
 
         if (withColumnsAndTriggers)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values())
-                addDroppedColumnToSchemaMutation(table, column, timestamp, mutation);
+                addDroppedColumnToSchemaMutation(table, column, builder);
 
             for (TriggerMetadata trigger : table.getTriggers())
-                addTriggerToSchemaMutation(table, trigger, timestamp, mutation);
+                addTriggerToSchemaMutation(table, trigger, builder);
 
             for (IndexMetadata index : table.getIndexes())
-                addIndexToSchemaMutation(table, index, timestamp, mutation);
+                addIndexToSchemaMutation(table, index, builder);
         }
     }
 
-    private static void addTableParamsToSchemaMutation(TableParams params, RowUpdateBuilder adder)
+    private static void addTableParamsToRowBuilder(TableParams params, Row.SimpleBuilder builder)
     {
-        adder.add("bloom_filter_fp_chance", params.bloomFilterFpChance)
-             .add("comment", params.comment)
-             .add("dclocal_read_repair_chance", params.dcLocalReadRepairChance)
-             .add("default_time_to_live", params.defaultTimeToLive)
-             .add("gc_grace_seconds", params.gcGraceSeconds)
-             .add("max_index_interval", params.maxIndexInterval)
-             .add("memtable_flush_period_in_ms", params.memtableFlushPeriodInMs)
-             .add("min_index_interval", params.minIndexInterval)
-             .add("read_repair_chance", params.readRepairChance)
-             .add("speculative_retry", params.speculativeRetry.toString())
-             .add("crc_check_chance", params.crcCheckChance)
-             .frozenMap("caching", params.caching.asMap())
-             .frozenMap("compaction", params.compaction.asMap())
-             .frozenMap("compression", params.compression.asMap())
-             .frozenMap("extensions", params.extensions)
-             .add("cdc", params.cdc);
+        builder.add("bloom_filter_fp_chance", params.bloomFilterFpChance)
+               .add("comment", params.comment)
+               .add("dclocal_read_repair_chance", params.dcLocalReadRepairChance)
+               .add("default_time_to_live", params.defaultTimeToLive)
+               .add("gc_grace_seconds", params.gcGraceSeconds)
+               .add("max_index_interval", params.maxIndexInterval)
+               .add("memtable_flush_period_in_ms", params.memtableFlushPeriodInMs)
+               .add("min_index_interval", params.minIndexInterval)
+               .add("read_repair_chance", params.readRepairChance)
+               .add("speculative_retry", params.speculativeRetry.toString())
+               .add("crc_check_chance", params.crcCheckChance)
+               .add("caching", params.caching.asMap())
+               .add("compaction", params.compaction.asMap())
+               .add("compression", params.compression.asMap())
+               .add("extensions", params.extensions);
+
+        // Only add CDC-enabled flag to schema if it's enabled on the node. This is to work around RTE's post-8099 if a 3.8+
+        // node sends table schema to a < 3.8 versioned node with an unknown column.
+        if (DatabaseDescriptor.isCDCEnabled())
+            builder.add("cdc", params.cdc);
     }
 
-    public static Mutation makeUpdateTableMutation(KeyspaceMetadata keyspace,
-                                                   CFMetaData oldTable,
-                                                   CFMetaData newTable,
-                                                   long timestamp)
+    public static Mutation.SimpleBuilder makeUpdateTableMutation(KeyspaceMetadata keyspace,
+                                                                 CFMetaData oldTable,
+                                                                 CFMetaData newTable,
+                                                                 long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        addTableToSchemaMutation(newTable, timestamp, false, mutation);
+        addTableToSchemaMutation(newTable, false, builder);
 
         MapDifference<ByteBuffer, ColumnDefinition> columnDiff = Maps.difference(oldTable.getColumnMetadata(),
                                                                                  newTable.getColumnMetadata());
 
         // columns that are no longer needed
         for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values())
-            dropColumnFromSchemaMutation(oldTable, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(oldTable, column, builder);
 
         // newly added columns
         for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values())
-            addColumnToSchemaMutation(newTable, column, timestamp, mutation);
+            addColumnToSchemaMutation(newTable, column, builder);
 
         // old columns with updated attributes
         for (ByteBuffer name : columnDiff.entriesDiffering().keySet())
-            addColumnToSchemaMutation(newTable, newTable.getColumnDefinition(name), timestamp, mutation);
+            addColumnToSchemaMutation(newTable, newTable.getColumnDefinition(name), builder);
 
         // dropped columns
         MapDifference<ByteBuffer, CFMetaData.DroppedColumn> droppedColumnDiff =
@@ -544,38 +539,38 @@ public final class SchemaKeyspace
 
         // newly dropped columns
         for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values())
-            addDroppedColumnToSchemaMutation(newTable, column, timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newTable, column, builder);
 
         // columns added then dropped again
         for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet())
-            addDroppedColumnToSchemaMutation(newTable, newTable.getDroppedColumns().get(name), timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newTable, newTable.getDroppedColumns().get(name), builder);
 
         MapDifference<String, TriggerMetadata> triggerDiff = triggersDiff(oldTable.getTriggers(), newTable.getTriggers());
 
         // dropped triggers
         for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnLeft().values())
-            dropTriggerFromSchemaMutation(oldTable, trigger, timestamp, mutation);
+            dropTriggerFromSchemaMutation(oldTable, trigger, builder);
 
         // newly created triggers
         for (TriggerMetadata trigger : triggerDiff.entriesOnlyOnRight().values())
-            addTriggerToSchemaMutation(newTable, trigger, timestamp, mutation);
+            addTriggerToSchemaMutation(newTable, trigger, builder);
 
         MapDifference<String, IndexMetadata> indexesDiff = indexesDiff(oldTable.getIndexes(),
                                                                        newTable.getIndexes());
 
         // dropped indexes
         for (IndexMetadata index : indexesDiff.entriesOnlyOnLeft().values())
-            dropIndexFromSchemaMutation(oldTable, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(oldTable, index, builder);
 
         // newly created indexes
         for (IndexMetadata index : indexesDiff.entriesOnlyOnRight().values())
-            addIndexToSchemaMutation(newTable, index, timestamp, mutation);
+            addIndexToSchemaMutation(newTable, index, builder);
 
         // updated indexes need to be updated
         for (MapDifference.ValueDifference<IndexMetadata> diff : indexesDiff.entriesDiffering().values())
-            addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), timestamp, mutation);
+            addUpdatedIndexToSchemaMutation(newTable, diff.rightValue(), builder);
 
-        return mutation;
+        return builder;
     }
 
     private static MapDifference<String, IndexMetadata> indexesDiff(Indexes before, Indexes after)
@@ -600,144 +595,137 @@ public final class SchemaKeyspace
         return Maps.difference(beforeMap, afterMap);
     }
 
-    public static Mutation makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
+    public static Mutation.SimpleBuilder makeDropTableMutation(KeyspaceMetadata keyspace, CFMetaData table, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        RowUpdateBuilder.deleteRow(Tables, timestamp, mutation, table.cfName);
+        builder.update(Tables).row(table.cfName).delete();
 
         for (ColumnDefinition column : table.allColumns())
-            dropColumnFromSchemaMutation(table, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(table, column, builder);
 
         for (TriggerMetadata trigger : table.getTriggers())
-            dropTriggerFromSchemaMutation(table, trigger, timestamp, mutation);
+            dropTriggerFromSchemaMutation(table, trigger, builder);
 
         for (IndexMetadata index : table.getIndexes())
-            dropIndexFromSchemaMutation(table, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(table, index, builder);
 
-        return mutation;
+        return builder;
     }
 
-    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void addColumnToSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Columns, timestamp, mutation).clustering(table.cfName, column.name.toString());
-
         AbstractType<?> type = column.type;
         if (type instanceof ReversedType)
             type = ((ReversedType) type).baseType;
 
-        adder.add("column_name_bytes", column.name.bytes)
-             .add("kind", column.kind.toString().toLowerCase())
-             .add("position", column.position())
-             .add("clustering_order", column.clusteringOrder().toString().toLowerCase())
-             .add("type", type.asCQL3Type().toString())
-             .build();
+        builder.update(Columns)
+               .row(table.cfName, column.name.toString())
+               .add("column_name_bytes", column.name.bytes)
+               .add("kind", column.kind.toString().toLowerCase())
+               .add("position", column.position())
+               .add("clustering_order", column.clusteringOrder().toString().toLowerCase())
+               .add("type", type.asCQL3Type().toString());
     }
 
-    private static void dropColumnFromSchemaMutation(CFMetaData table, ColumnDefinition column, long timestamp, Mutation mutation)
+    private static void dropColumnFromSchemaMutation(CFMetaData table, ColumnDefinition column, Mutation.SimpleBuilder builder)
     {
         // Note: we do want to use name.toString(), not name.bytes directly for backward compatibility (For CQL3, this won't make a difference).
-        RowUpdateBuilder.deleteRow(Columns, timestamp, mutation, table.cfName, column.name.toString());
+        builder.update(Columns).row(table.cfName, column.name.toString()).delete();
     }
 
-    private static void addDroppedColumnToSchemaMutation(CFMetaData table, CFMetaData.DroppedColumn column, long timestamp, Mutation mutation)
+    private static void addDroppedColumnToSchemaMutation(CFMetaData table, CFMetaData.DroppedColumn column, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(DroppedColumns, timestamp, mutation).clustering(table.cfName, column.name);
-
-        adder.add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime)))
-             .add("type", expandUserTypes(column.type).asCQL3Type().toString())
-             .build();
+        builder.update(DroppedColumns)
+               .row(table.cfName, column.name)
+               .add("dropped_time", new Date(TimeUnit.MICROSECONDS.toMillis(column.droppedTime)))
+               .add("type", expandUserTypes(column.type).asCQL3Type().toString());
     }
 
-    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void addTriggerToSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        new RowUpdateBuilder(Triggers, timestamp, mutation)
-            .clustering(table.cfName, trigger.name)
-            .frozenMap("options", Collections.singletonMap("class", trigger.classOption))
-            .build();
+        builder.update(Triggers)
+               .row(table.cfName, trigger.name)
+               .add("options", Collections.singletonMap("class", trigger.classOption));
     }
 
-    private static void dropTriggerFromSchemaMutation(CFMetaData table, TriggerMetadata trigger, long timestamp, Mutation mutation)
+    private static void dropTriggerFromSchemaMutation(CFMetaData table, TriggerMetadata trigger, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder.deleteRow(Triggers, timestamp, mutation, table.cfName, trigger.name);
+        builder.update(Triggers).row(table.cfName, trigger.name).delete();
     }
 
-    public static Mutation makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addViewToSchemaMutation(view, timestamp, true, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addViewToSchemaMutation(view, true, builder);
+        return builder;
     }
 
-    private static void addViewToSchemaMutation(ViewDefinition view, long timestamp, boolean includeColumns, Mutation mutation)
+    private static void addViewToSchemaMutation(ViewDefinition view, boolean includeColumns, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder builder = new RowUpdateBuilder(Views, timestamp, mutation)
-            .clustering(view.viewName);
-
         CFMetaData table = view.metadata;
+        Row.SimpleBuilder rowBuilder = builder.update(Views)
+                                              .row(view.viewName)
+                                              .add("include_all_columns", view.includeAllColumns)
+                                              .add("base_table_id", view.baseTableId)
+                                              .add("base_table_name", view.baseTableMetadata().cfName)
+                                              .add("where_clause", view.whereClause)
+                                              .add("id", table.cfId);
 
-        builder.add("include_all_columns", view.includeAllColumns)
-               .add("base_table_id", view.baseTableId)
-               .add("base_table_name", view.baseTableMetadata().cfName)
-               .add("where_clause", view.whereClause)
-               .add("id", table.cfId);
-
-        addTableParamsToSchemaMutation(table.params, builder);
+        addTableParamsToRowBuilder(table.params, rowBuilder);
 
         if (includeColumns)
         {
             for (ColumnDefinition column : table.allColumns())
-                addColumnToSchemaMutation(table, column, timestamp, mutation);
+                addColumnToSchemaMutation(table, column, builder);
 
             for (CFMetaData.DroppedColumn column : table.getDroppedColumns().values())
-                addDroppedColumnToSchemaMutation(table, column, timestamp, mutation);
+                addDroppedColumnToSchemaMutation(table, column, builder);
         }
-
-        builder.build();
     }
 
-    public static Mutation makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
+    public static Mutation.SimpleBuilder makeDropViewMutation(KeyspaceMetadata keyspace, ViewDefinition view, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        RowUpdateBuilder.deleteRow(Views, timestamp, mutation, view.viewName);
+        builder.update(Views).row(view.viewName).delete();
 
         CFMetaData table = view.metadata;
         for (ColumnDefinition column : table.allColumns())
-            dropColumnFromSchemaMutation(table, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(table, column, builder);
 
         for (IndexMetadata index : table.getIndexes())
-            dropIndexFromSchemaMutation(table, index, timestamp, mutation);
+            dropIndexFromSchemaMutation(table, index, builder);
 
-        return mutation;
+        return builder;
     }
 
-    public static Mutation makeUpdateViewMutation(KeyspaceMetadata keyspace,
-                                                  ViewDefinition oldView,
-                                                  ViewDefinition newView,
-                                                  long timestamp)
+    public static Mutation.SimpleBuilder makeUpdateViewMutation(KeyspaceMetadata keyspace,
+                                                                ViewDefinition oldView,
+                                                                ViewDefinition newView,
+                                                                long timestamp)
     {
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
 
-        addViewToSchemaMutation(newView, timestamp, false, mutation);
+        addViewToSchemaMutation(newView, false, builder);
 
         MapDifference<ByteBuffer, ColumnDefinition> columnDiff = Maps.difference(oldView.metadata.getColumnMetadata(),
                                                                                  newView.metadata.getColumnMetadata());
 
         // columns that are no longer needed
         for (ColumnDefinition column : columnDiff.entriesOnlyOnLeft().values())
-            dropColumnFromSchemaMutation(oldView.metadata, column, timestamp, mutation);
+            dropColumnFromSchemaMutation(oldView.metadata, column, builder);
 
         // newly added columns
         for (ColumnDefinition column : columnDiff.entriesOnlyOnRight().values())
-            addColumnToSchemaMutation(newView.metadata, column, timestamp, mutation);
+            addColumnToSchemaMutation(newView.metadata, column, builder);
 
         // old columns with updated attributes
         for (ByteBuffer name : columnDiff.entriesDiffering().keySet())
-            addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), timestamp, mutation);
+            addColumnToSchemaMutation(newView.metadata, newView.metadata.getColumnDefinition(name), builder);
 
         // dropped columns
         MapDifference<ByteBuffer, CFMetaData.DroppedColumn> droppedColumnDiff =
@@ -745,63 +733,68 @@ public final class SchemaKeyspace
 
         // newly dropped columns
         for (CFMetaData.DroppedColumn column : droppedColumnDiff.entriesOnlyOnRight().values())
-            addDroppedColumnToSchemaMutation(oldView.metadata, column, timestamp, mutation);
+            addDroppedColumnToSchemaMutation(oldView.metadata, column, builder);
 
         // columns added then dropped again
         for (ByteBuffer name : droppedColumnDiff.entriesDiffering().keySet())
-            addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), timestamp, mutation);
+            addDroppedColumnToSchemaMutation(newView.metadata, newView.metadata.getDroppedColumns().get(name), builder);
 
-        return mutation;
+        return builder;
     }
 
     private static void addIndexToSchemaMutation(CFMetaData table,
                                                  IndexMetadata index,
-                                                 long timestamp,
-                                                 Mutation mutation)
+                                                 Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder builder = new RowUpdateBuilder(Indexes, timestamp, mutation).clustering(table.cfName, index.name);
-
-        builder.add("kind", index.kind.toString());
-        builder.frozenMap("options", index.options);
-        builder.build();
+        builder.update(Indexes)
+               .row(table.cfName, index.name)
+               .add("kind", index.kind.toString())
+               .add("options", index.options);
     }
 
     private static void dropIndexFromSchemaMutation(CFMetaData table,
                                                     IndexMetadata index,
-                                                    long timestamp,
-                                                    Mutation mutation)
+                                                    Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder.deleteRow(Indexes, timestamp, mutation, table.cfName, index.name);
+        builder.update(Indexes).row(table.cfName, index.name).delete();
     }
 
     private static void addUpdatedIndexToSchemaMutation(CFMetaData table,
                                                         IndexMetadata index,
-                                                        long timestamp,
-                                                        Mutation mutation)
+                                                        Mutation.SimpleBuilder builder)
     {
-        addIndexToSchemaMutation(table, index, timestamp, mutation);
+        addIndexToSchemaMutation(table, index, builder);
     }
 
-    public static Mutation makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addFunctionToSchemaMutation(function, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addFunctionToSchemaMutation(function, builder);
+        return builder;
     }
 
-    static void addFunctionToSchemaMutation(UDFunction function, long timestamp, Mutation mutation)
+    static void addFunctionToSchemaMutation(UDFunction function, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder =
-            new RowUpdateBuilder(Functions, timestamp, mutation).clustering(function.name().name, functionArgumentsList(function));
-
-        adder.add("body", function.body())
-             .add("language", function.language())
-             .add("return_type", function.returnType().asCQL3Type().toString())
-             .add("called_on_null_input", function.isCalledOnNullInput())
-             .frozenList("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
+        builder.update(Functions)
+               .row(function.name().name, functionArgumentsList(function))
+               .add("body", function.body())
+               .add("language", function.language())
+               .add("return_type", function.returnType().asCQL3Type().toString())
+               .add("called_on_null_input", function.isCalledOnNullInput())
+               .add("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
+    }
 
-        adder.build();
+    private static String bbToString(ByteBuffer bb)
+    {
+        try
+        {
+            return ByteBufferUtil.string(bb);
+        }
+        catch (CharacterCodingException e)
+        {
+            throw new RuntimeException(e);
+        }
     }
 
     private static List<String> functionArgumentsList(AbstractFunction fun)
@@ -813,42 +806,42 @@ public final class SchemaKeyspace
                   .collect(toList());
     }
 
-    public static Mutation makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
+    public static Mutation.SimpleBuilder makeDropFunctionMutation(KeyspaceMetadata keyspace, UDFunction function, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Functions, timestamp, mutation, function.name().name, functionArgumentsList(function));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Functions).row(function.name().name, functionArgumentsList(function)).delete();
+        return builder;
     }
 
-    public static Mutation makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
+    public static Mutation.SimpleBuilder makeCreateAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        addAggregateToSchemaMutation(aggregate, timestamp, mutation);
-        return mutation;
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        addAggregateToSchemaMutation(aggregate, builder);
+        return builder;
     }
 
-    static void addAggregateToSchemaMutation(UDAggregate aggregate, long timestamp, Mutation mutation)
+    static void addAggregateToSchemaMutation(UDAggregate aggregate, Mutation.SimpleBuilder builder)
     {
-        RowUpdateBuilder adder =
-            new RowUpdateBuilder(Aggregates, timestamp, mutation) .clustering(aggregate.name().name, functionArgumentsList(aggregate));
-
-        adder.add("return_type", aggregate.returnType().asCQL3Type().toString())
-             .add("state_func", aggregate.stateFunction().name().name)
-             .add("state_type", aggregate.stateType().asCQL3Type().toString())
-             .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
-             .add("initcond", aggregate.initialCondition() != null
-                              // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
-                              ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
-                              : null)
-             .build();
+        builder.update(Aggregates)
+               .row(aggregate.name().name, functionArgumentsList(aggregate))
+               .add("return_type", aggregate.returnType().asCQL3Type().toString())
+               .add("state_func", aggregate.stateFunction().name().name)
+               .add("state_type", aggregate.stateType().asCQL3Type().toString())
+               .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
+               .add("initcond", aggregate.initialCondition() != null
+                                // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
+                                ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
+                                : null);
     }
 
-    public static Mutation makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
+    public static Mutation.SimpleBuilder makeDropAggregateMutation(KeyspaceMetadata keyspace, UDAggregate aggregate, long timestamp)
     {
         // Include the serialized keyspace in case the target node missed a CREATE KEYSPACE migration (see CASSANDRA-5631).
-        Mutation mutation = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
-        return RowUpdateBuilder.deleteRow(Aggregates, timestamp, mutation, aggregate.name().name, functionArgumentsList(aggregate));
+        Mutation.SimpleBuilder builder = makeCreateKeyspaceMutation(keyspace.name, keyspace.params, timestamp);
+        builder.update(Aggregates).row(aggregate.name().name, functionArgumentsList(aggregate)).delete();
+        return builder;
     }
 
     /*

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/service/MigrationManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/MigrationManager.java b/src/java/org/apache/cassandra/service/MigrationManager.java
index ba239b3..7eac678 100644
--- a/src/java/org/apache/cassandra/service/MigrationManager.java
+++ b/src/java/org/apache/cassandra/service/MigrationManager.java
@@ -506,12 +506,14 @@ public class MigrationManager
      * actively announce a new version to active hosts via rpc
      * @param schema The schema mutation to be applied
      */
-    private static void announce(Mutation schema, boolean announceLocally)
+    private static void announce(Mutation.SimpleBuilder schema, boolean announceLocally)
     {
+        List<Mutation> mutations = Collections.singletonList(schema.build());
+
         if (announceLocally)
-            SchemaKeyspace.mergeSchema(Collections.singletonList(schema));
+            SchemaKeyspace.mergeSchema(mutations);
         else
-            FBUtilities.waitOnFuture(announce(Collections.singletonList(schema)));
+            FBUtilities.waitOnFuture(announce(mutations));
     }
 
     private static void pushSchemaMutation(InetAddress endpoint, Collection<Mutation> schema)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
index d7b804a..5c400a9 100644
--- a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
+++ b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java
@@ -23,7 +23,8 @@ import java.util.*;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.db.RowUpdateBuilder;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.schema.KeyspaceMetadata;
 import org.apache.cassandra.schema.KeyspaceParams;
 import org.apache.cassandra.schema.Tables;
@@ -86,36 +87,41 @@ public final class TraceKeyspace
                                              String command,
                                              int ttl)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Sessions, FBUtilities.timestampMicros(), ttl, sessionId)
-                                 .clustering()
-                                 .add("client", client)
-                                 .add("coordinator", FBUtilities.getBroadcastAddress())
-                                 .add("request", request)
-                                 .add("started_at", new Date(startedAt))
-                                 .add("command", command);
-
-        for (Map.Entry<String, String> entry : parameters.entrySet())
-            adder.addMapEntry("parameters", entry.getKey(), entry.getValue());
-        return adder.build();
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Sessions, sessionId);
+        builder.row()
+               .ttl(ttl)
+               .add("client", client)
+               .add("coordinator", FBUtilities.getBroadcastAddress())
+               .add("request", request)
+               .add("started_at", new Date(startedAt))
+               .add("command", command)
+               .appendAll("parameters", parameters);
+
+        return builder.buildAsMutation();
     }
 
     static Mutation makeStopSessionMutation(ByteBuffer sessionId, int elapsed, int ttl)
     {
-        return new RowUpdateBuilder(Sessions, FBUtilities.timestampMicros(), ttl, sessionId)
-               .clustering()
-               .add("duration", elapsed)
-               .build();
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Sessions, sessionId);
+        builder.row()
+               .ttl(ttl)
+               .add("duration", elapsed);
+        return builder.buildAsMutation();
     }
 
     static Mutation makeEventMutation(ByteBuffer sessionId, String message, int elapsed, String threadName, int ttl)
     {
-        RowUpdateBuilder adder = new RowUpdateBuilder(Events, FBUtilities.timestampMicros(), ttl, sessionId)
-                                 .clustering(UUIDGen.getTimeUUID());
-        adder.add("activity", message);
-        adder.add("source", FBUtilities.getBroadcastAddress());
-        adder.add("thread", threadName);
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(Events, sessionId);
+        Row.SimpleBuilder rowBuilder = builder.row(UUIDGen.getTimeUUID())
+                                              .ttl(ttl);
+
+        rowBuilder.add("activity", message)
+                  .add("source", FBUtilities.getBroadcastAddress())
+                  .add("thread", threadName);
+
         if (elapsed >= 0)
-            adder.add("source_elapsed", elapsed);
-        return adder.build();
+            rowBuilder.add("source_elapsed", elapsed);
+
+        return builder.buildAsMutation();
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/UpdateBuilder.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/UpdateBuilder.java b/test/unit/org/apache/cassandra/UpdateBuilder.java
index 3a5fbe6..19e48f2 100644
--- a/test/unit/org/apache/cassandra/UpdateBuilder.java
+++ b/test/unit/org/apache/cassandra/UpdateBuilder.java
@@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.rows.*;
 import org.apache.cassandra.db.partitions.*;
 import org.apache.cassandra.utils.FBUtilities;
 import org.apache.cassandra.service.StorageService;
@@ -34,32 +35,30 @@ import org.apache.cassandra.service.StorageService;
  */
 public class UpdateBuilder
 {
-    private final PartitionUpdate update;
-    private RowUpdateBuilder currentRow;
-    private long timestamp = FBUtilities.timestampMicros();
+    private final PartitionUpdate.SimpleBuilder updateBuilder;
+    private Row.SimpleBuilder currentRow;
 
-    private UpdateBuilder(CFMetaData metadata, DecoratedKey partitionKey)
+    private UpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder)
     {
-        this.update = new PartitionUpdate(metadata, partitionKey, metadata.partitionColumns(), 4);
+        this.updateBuilder = updateBuilder;
     }
 
     public static UpdateBuilder create(CFMetaData metadata, Object... partitionKey)
     {
-        return new UpdateBuilder(metadata, makeKey(metadata, partitionKey));
+        return new UpdateBuilder(PartitionUpdate.simpleBuilder(metadata, partitionKey));
     }
 
     public UpdateBuilder withTimestamp(long timestamp)
     {
-        this.timestamp = timestamp;
+        updateBuilder.timestamp(timestamp);
+        if (currentRow != null)
+            currentRow.timestamp(timestamp);
         return this;
     }
 
     public UpdateBuilder newRow(Object... clustering)
     {
-        maybeBuildCurrentRow();
-        currentRow = new RowUpdateBuilder(update, timestamp, 0);
-        if (clustering.length > 0)
-            currentRow.clustering(clustering);
+        currentRow = updateBuilder.row(clustering);
         return this;
     }
 
@@ -72,48 +71,25 @@ public class UpdateBuilder
 
     public PartitionUpdate build()
     {
-        maybeBuildCurrentRow();
-        return update;
+        return updateBuilder.build();
     }
 
     public IMutation makeMutation()
     {
-        Mutation m = new Mutation(build());
-        return update.metadata().isCounter()
+        Mutation m = updateBuilder.buildAsMutation();
+        return updateBuilder.metadata().isCounter()
              ? new CounterMutation(m, ConsistencyLevel.ONE)
              : m;
     }
 
     public void apply()
     {
-        Mutation m = new Mutation(build());
-        if (update.metadata().isCounter())
-            new CounterMutation(m, ConsistencyLevel.ONE).apply();
-        else
-            m.apply();
+        makeMutation().apply();
     }
 
     public void applyUnsafe()
     {
-        assert !update.metadata().isCounter() : "Counters have currently no applyUnsafe() option";
-        new Mutation(build()).applyUnsafe();
-    }
-
-    private void maybeBuildCurrentRow()
-    {
-        if (currentRow != null)
-        {
-            currentRow.build();
-            currentRow = null;
-        }
-    }
-
-    private static DecoratedKey makeKey(CFMetaData metadata, Object[] partitionKey)
-    {
-        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
-            return (DecoratedKey)partitionKey[0];
-
-        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
-        return metadata.decorateKey(key);
+        assert !updateBuilder.metadata().isCounter() : "Counters have currently no applyUnsafe() option";
+        updateBuilder.buildAsMutation().applyUnsafe();
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/Util.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/Util.java b/test/unit/org/apache/cassandra/Util.java
index 9689628..151d3d4 100644
--- a/test/unit/org/apache/cassandra/Util.java
+++ b/test/unit/org/apache/cassandra/Util.java
@@ -459,15 +459,37 @@ public class Util
     public static boolean equal(UnfilteredRowIterator a, UnfilteredRowIterator b)
     {
         return Objects.equals(a.columns(), b.columns())
-            && Objects.equals(a.metadata(), b.metadata())
+            && Objects.equals(a.stats(), b.stats())
+            && sameContent(a, b);
+    }
+
+    // Test equality of the iterators, but without caring too much about the "metadata" of said iterator. This is often
+    // what we want in tests. In particular, the columns() reported by the iterators will sometimes differ because they
+    // are a superset of what the iterator actually contains, and depending on the method used to get each iterator
+    // tested, one may include a defined column the other don't while there is not actual content for that column.
+    public static boolean sameContent(UnfilteredRowIterator a, UnfilteredRowIterator b)
+    {
+        return Objects.equals(a.metadata(), b.metadata())
             && Objects.equals(a.isReverseOrder(), b.isReverseOrder())
             && Objects.equals(a.partitionKey(), b.partitionKey())
             && Objects.equals(a.partitionLevelDeletion(), b.partitionLevelDeletion())
             && Objects.equals(a.staticRow(), b.staticRow())
-            && Objects.equals(a.stats(), b.stats())
             && Iterators.elementsEqual(a, b);
     }
 
+    public static boolean sameContent(Mutation a, Mutation b)
+    {
+        if (!a.key().equals(b.key()) || !a.getColumnFamilyIds().equals(b.getColumnFamilyIds()))
+            return false;
+
+        for (UUID cfId : a.getColumnFamilyIds())
+        {
+            if (!sameContent(a.getPartitionUpdate(cfId).unfilteredIterator(), b.getPartitionUpdate(cfId).unfilteredIterator()))
+                return false;
+        }
+        return true;
+    }
+
     // moved & refactored from KeyspaceTest in < 3.0
     public static void assertColumns(Row row, String... expectedColumnNames)
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/batchlog/BatchTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/batchlog/BatchTest.java b/test/unit/org/apache/cassandra/batchlog/BatchTest.java
index b7a4100..4e64ec6 100644
--- a/test/unit/org/apache/cassandra/batchlog/BatchTest.java
+++ b/test/unit/org/apache/cassandra/batchlog/BatchTest.java
@@ -28,6 +28,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.Util;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.Keyspace;
 import org.apache.cassandra.db.Mutation;
@@ -44,6 +45,7 @@ import org.apache.cassandra.utils.UUIDGen;
 
 import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class BatchTest
 {
@@ -148,6 +150,19 @@ public class BatchTest
         Iterator<Mutation> it1 = batch1.decodedMutations.iterator();
         Iterator<Mutation> it2 = batch2.decodedMutations.iterator();
         while (it1.hasNext())
-            assertEquals(it1.next().toString(), it2.next().toString());
+        {
+            // We can't simply test the equality of both mutation string representation, that is do:
+            //   assertEquals(it1.next().toString(), it2.next().toString());
+            // because when deserializing from the old format, the returned iterator will always have it's 'columns()'
+            // method return all the table columns (no matter what's the actual content), and the table contains a
+            // 'val0' column we're not setting in that test.
+            //
+            // And it's actually not easy to fix legacy deserialization as we'd need to know which columns are actually
+            // set upfront, which would require use to iterate over the whole content first, which would be costly. And
+            // as the result of 'columns()' is only meant as a superset of the columns in the iterator, we don't bother.
+            Mutation mut1 = it1.next();
+            Mutation mut2 = it2.next();
+            assertTrue(mut1 + " != " + mut2, Util.sameContent(mut1, mut2));
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/config/CFMetaDataTest.java b/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
index 6bfe5c0..8616987 100644
--- a/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
+++ b/test/unit/org/apache/cassandra/config/CFMetaDataTest.java
@@ -150,7 +150,7 @@ public class CFMetaDataTest
         assert before.equals(after) : String.format("%n%s%n!=%n%s", before, after);
 
         // Test schema conversion
-        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
+        Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros()).build();
         PartitionUpdate cfU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
         PartitionUpdate cdU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java b/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
index 632c290..0b18eec 100644
--- a/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
+++ b/test/unit/org/apache/cassandra/cql3/CDCStatementTest.java
@@ -19,10 +19,20 @@
 package org.apache.cassandra.cql3;
 
 import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
+import org.apache.cassandra.config.DatabaseDescriptor;
+
 public class CDCStatementTest extends CQLTester
 {
+    @BeforeClass
+    public static void checkConfig()
+    {
+        Assume.assumeTrue(DatabaseDescriptor.isCDCEnabled());
+    }
+
     @Test
     public void testEnableOnCreate() throws Throwable
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
deleted file mode 100644
index afe2455..0000000
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/RowUpdateBuilderTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.cassandra.cql3.validation.entities;
-
-import org.junit.Test;
-
-import org.apache.cassandra.Util;
-import org.apache.cassandra.cql3.CQLTester;
-import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.db.RowUpdateBuilder;
-import org.apache.cassandra.utils.FBUtilities;
-
-// see CASSANDRA-9743, CASSANDRA-9746
-public class RowUpdateBuilderTest extends CQLTester
-{
-    @Test
-    public void testAddListEntryDurable() throws Throwable
-    {
-        testAddListEntry(false);
-    }
-
-    @Test
-    public void testAddListEntryTransient() throws Throwable
-    {
-        testAddListEntry(true);
-    }
-
-    public void testAddListEntry(boolean skipCommitLog) throws Throwable
-    {
-        createTable("CREATE TABLE %s ("
-                    + "pk text,"
-                    + "ck text,"
-                    + "l1 list<int>,"
-                    + "l2 list<int>,"
-                    + "PRIMARY KEY ((pk), ck))");
-
-        long timestamp = FBUtilities.timestampMicros();
-
-        Mutation mutation = new Mutation(keyspace(), Util.dk("test"));
-        addToMutation("row1", timestamp, mutation);
-        addToMutation("row2", timestamp, mutation);
-
-        if (skipCommitLog)
-            mutation.applyUnsafe();
-        else
-            mutation.apply();
-
-        assertRowCount(execute("SELECT ck FROM %s"), 2);
-    }
-
-    private void addToMutation(String typeName, long timestamp, Mutation mutation)
-    {
-        RowUpdateBuilder adder = new RowUpdateBuilder(getCurrentColumnFamilyStore().metadata, timestamp, mutation)
-                                 .clustering(typeName);
-
-        for (int i = 0; i < 2; i++)
-        {
-            adder.addListEntry("l1", i)
-                 .addListEntry("l2", i);
-        }
-
-        adder.build();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java b/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
index a67e9e5..8897700 100644
--- a/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
+++ b/test/unit/org/apache/cassandra/db/RecoveryManagerMissingHeaderTest.java
@@ -118,7 +118,7 @@ public class RecoveryManagerMissingHeaderTest
 
         CommitLog.instance.resetUnsafe(false);
 
-        Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-        Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java b/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
index 37d719e..cbc412d 100644
--- a/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
+++ b/test/unit/org/apache/cassandra/db/RecoveryManagerTest.java
@@ -188,8 +188,8 @@ public class RecoveryManagerTest
             }
             Assert.assertFalse(t.isAlive());
 
-            Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-            Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+            Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+            Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
         }
         finally
         {
@@ -220,8 +220,8 @@ public class RecoveryManagerTest
         CommitLog.instance.resetUnsafe(false);
 
         DecoratedKey dk = Util.dk("keymulti");
-        Assert.assertTrue(Util.equal(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
-        Assert.assertTrue(Util.equal(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd1, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace1.getColumnFamilyStore(CF_STANDARD1), dk).build()).unfilteredIterator()));
+        Assert.assertTrue(Util.sameContent(upd2, Util.getOnlyPartitionUnfiltered(Util.cmd(keyspace2.getColumnFamilyStore(CF_STANDARD3), dk).build()).unfilteredIterator()));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java b/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
new file mode 100644
index 0000000..8e71d64
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/RowUpdateBuilder.java
@@ -0,0 +1,196 @@
+/*
+ * 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.cassandra.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.db.marshal.SetType;
+import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.context.CounterContext;
+import org.apache.cassandra.db.partitions.*;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.ListType;
+import org.apache.cassandra.db.marshal.MapType;
+import org.apache.cassandra.utils.*;
+
+/**
+ * Convenience object to create single row updates for tests.
+ *
+ * This is a thin wrapper over the builders in SimpleBuilders for historical reasons.
+ * We could modify all the tests using this class to use the simple builders directly
+ * instead, but there is a fair amount of use so the value of such effort is unclear.
+ */
+public class RowUpdateBuilder
+{
+    private final PartitionUpdate.SimpleBuilder updateBuilder;
+    private Row.SimpleBuilder rowBuilder;
+    private boolean noRowMarker;
+
+    private List<RangeTombstone> rts = new ArrayList<>();
+
+    private RowUpdateBuilder(PartitionUpdate.SimpleBuilder updateBuilder)
+    {
+        this.updateBuilder = updateBuilder;
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
+    {
+        this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
+    {
+        this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
+    {
+        this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
+    }
+
+    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
+    {
+        this(PartitionUpdate.simpleBuilder(metadata, partitionKey));
+
+        this.updateBuilder.timestamp(timestamp);
+        this.updateBuilder.ttl(ttl);
+        this.updateBuilder.nowInSec(localDeletionTime);
+    }
+
+    private Row.SimpleBuilder rowBuilder()
+    {
+        // Normally, rowBuilder is created by the call to clustering(), but we allow skipping that call for an empty
+        // clustering.
+        if (rowBuilder == null)
+        {
+            rowBuilder = updateBuilder.row();
+            if (noRowMarker)
+                rowBuilder.noPrimaryKeyLivenessInfo();
+        }
+
+        return rowBuilder;
+    }
+
+    // This must be called before any addition or deletion if used.
+    public RowUpdateBuilder noRowMarker()
+    {
+        this.noRowMarker = true;
+        if (rowBuilder != null)
+            rowBuilder.noPrimaryKeyLivenessInfo();
+        return this;
+    }
+
+    public RowUpdateBuilder clustering(Object... clusteringValues)
+    {
+        assert rowBuilder == null;
+        rowBuilder = updateBuilder.row(clusteringValues);
+        if (noRowMarker)
+            rowBuilder.noPrimaryKeyLivenessInfo();
+        return this;
+    }
+
+    public Mutation build()
+    {
+        return new Mutation(buildUpdate());
+    }
+
+    public PartitionUpdate buildUpdate()
+    {
+        PartitionUpdate update = updateBuilder.build();
+        for (RangeTombstone rt : rts)
+            update.add(rt);
+        return update;
+    }
+
+    private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues)
+    {
+        assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty());
+
+        boolean isStatic = clusteringValues.length != update.metadata().comparator.size();
+        Row.Builder builder = BTreeRow.sortedBuilder();
+
+        if (isStatic)
+            builder.newRow(Clustering.STATIC_CLUSTERING);
+        else
+            builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
+        builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime)));
+
+        update.add(builder.build());
+    }
+
+    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
+    {
+        return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
+    }
+
+    public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
+    {
+        PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 0);
+        deleteRow(update, timestamp, localDeletionTime, clusteringValues);
+        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
+        // underneath (this class if for convenience, not performance)
+        return new Mutation(update.metadata().ksName, update.partitionKey()).add(update);
+    }
+
+    private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
+    {
+        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
+            return (DecoratedKey)partitionKey[0];
+
+        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
+        return metadata.decorateKey(key);
+    }
+
+    public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
+    {
+        rts.add(rt);
+        return this;
+    }
+
+    public RowUpdateBuilder addRangeTombstone(Object start, Object end)
+    {
+        updateBuilder.addRangeTombstone().start(start).end(end);
+        return this;
+    }
+
+    public RowUpdateBuilder add(String columnName, Object value)
+    {
+        rowBuilder().add(columnName, value);
+        return this;
+    }
+
+    public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
+    {
+        return add(columnDefinition.name.toString(), value);
+    }
+
+    public RowUpdateBuilder delete(String columnName)
+    {
+        rowBuilder().delete(columnName);
+        return this;
+    }
+
+    public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
+    {
+        return delete(columnDefinition.name.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java b/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
index b264553..3dd798d 100644
--- a/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
+++ b/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
@@ -172,7 +172,7 @@ public class TTLExpiryTest
 
         new RowUpdateBuilder(cfs.metadata, timestamp, 1, key)
             .add("col2", ByteBufferUtil.EMPTY_BYTE_BUFFER)
-            .addMapEntry("col8", "bar", "foo")
+            .add("col8", Collections.singletonMap("bar", "foo"))
             .delete("col1")
             .build()
             .applyUnsafe();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java b/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
index a069db1..bfa9796 100644
--- a/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
+++ b/test/unit/org/apache/cassandra/db/partition/PartitionUpdateTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.cassandra.db.partition;
 
+import org.apache.cassandra.UpdateBuilder;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.cql3.CQLTester;
 import org.apache.cassandra.db.RowUpdateBuilder;
@@ -34,21 +35,17 @@ public class PartitionUpdateTest extends CQLTester
         createTable("CREATE TABLE %s (key text, clustering int, a int, s int static, PRIMARY KEY(key, clustering))");
         CFMetaData cfm = currentTableMetadata();
 
-        long timestamp = FBUtilities.timestampMicros();
-        PartitionUpdate update = new RowUpdateBuilder(cfm, timestamp, "key0").clustering(1).add("a", 1).buildUpdate();
-        Assert.assertEquals(1, update.operationCount());
-
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").buildUpdate();
-        Assert.assertEquals(0, update.operationCount());
+        UpdateBuilder builder = UpdateBuilder.create(cfm, "key0");
+        Assert.assertEquals(0, builder.build().operationCount());
+        Assert.assertEquals(1, builder.newRow(1).add("a", 1).build().operationCount());
 
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").add("s", 1).buildUpdate();
-        Assert.assertEquals(1, update.operationCount());
+        builder = UpdateBuilder.create(cfm, "key0");
+        Assert.assertEquals(1, builder.newRow().add("s", 1).build().operationCount());
 
-        update = new RowUpdateBuilder(cfm, timestamp, "key0").add("s", 1).buildUpdate();
-        update = new RowUpdateBuilder(update, timestamp, cfm.params.defaultTimeToLive).clustering(1)
-                                                                                      .add("a", 1)
-                                                                                      .buildUpdate();
-        Assert.assertEquals(2, update.operationCount());
+        builder = UpdateBuilder.create(cfm, "key0");
+        builder.newRow().add("s", 1);
+        builder.newRow(1).add("a", 1);
+        Assert.assertEquals(2, builder.build().operationCount());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/hints/HintTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/hints/HintTest.java b/test/unit/org/apache/cassandra/hints/HintTest.java
index 658a41c..4cc2188 100644
--- a/test/unit/org/apache/cassandra/hints/HintTest.java
+++ b/test/unit/org/apache/cassandra/hints/HintTest.java
@@ -30,9 +30,7 @@ import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
 import org.apache.cassandra.Util;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.config.*;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.partitions.FilteredPartition;
 import org.apache.cassandra.db.partitions.PartitionIterator;
@@ -127,7 +125,7 @@ public class HintTest
 
         // assert that we can read the inserted partitions
         for (PartitionUpdate partition : mutation.getPartitionUpdates())
-            assertPartitionsEqual(partition, readPartition(key, partition.metadata().cfName));
+            assertPartitionsEqual(partition, readPartition(key, partition.metadata().cfName, partition.columns()));
     }
 
     @Test
@@ -152,8 +150,10 @@ public class HintTest
         assertNoPartitions(key, TABLE1);
 
         // TABLE0 and TABLE2 updates should have been applied successfully
-        assertPartitionsEqual(mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE0)), readPartition(key, TABLE0));
-        assertPartitionsEqual(mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE2)), readPartition(key, TABLE2));
+        PartitionUpdate upd0 = mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE0));
+        assertPartitionsEqual(upd0, readPartition(key, TABLE0, upd0.columns()));
+        PartitionUpdate upd2 = mutation.getPartitionUpdate(Schema.instance.getId(KEYSPACE, TABLE2));
+        assertPartitionsEqual(upd2, readPartition(key, TABLE2, upd2.columns()));
     }
 
     @Test
@@ -296,40 +296,44 @@ public class HintTest
 
     private static Mutation createMutation(String key, long now)
     {
-        Mutation mutation = new Mutation(KEYSPACE, dk(key));
+        Mutation.SimpleBuilder builder = Mutation.simpleBuilder(KEYSPACE, dk(key));
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE0), now, mutation)
-            .clustering("column0")
-            .add("val", "value0")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE0))
+               .timestamp(now)
+               .row("column0")
+               .add("val", "value0");
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE1), now + 1, mutation)
-            .clustering("column1")
-            .add("val", "value1")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE1))
+               .timestamp(now + 1)
+               .row("column1")
+               .add("val", "value1");
 
-        new RowUpdateBuilder(Schema.instance.getCFMetaData(KEYSPACE, TABLE2), now + 2, mutation)
-            .clustering("column2")
-            .add("val", "value2")
-            .build();
+        builder.update(Schema.instance.getCFMetaData(KEYSPACE, TABLE2))
+               .timestamp(now + 2)
+               .row("column2")
+               .add("val", "value2");
 
-        return mutation;
+        return builder.build();
     }
 
-    private static SinglePartitionReadCommand cmd(String key, String table)
+    private static ColumnFamilyStore cfs(String table)
     {
-        CFMetaData meta = Schema.instance.getCFMetaData(KEYSPACE, table);
-        return SinglePartitionReadCommand.fullPartitionRead(meta, FBUtilities.nowInSeconds(), bytes(key));
+        return Schema.instance.getColumnFamilyStoreInstance(Schema.instance.getCFMetaData(KEYSPACE, table).cfId);
     }
 
-    private static FilteredPartition readPartition(String key, String table)
+    private static FilteredPartition readPartition(String key, String table, PartitionColumns columns)
     {
-        return Util.getOnlyPartition(cmd(key, table));
+        String[] columnNames = new String[columns.size()];
+        int i = 0;
+        for (ColumnDefinition column : columns)
+            columnNames[i++] = column.name.toString();
+
+        return Util.getOnlyPartition(Util.cmd(cfs(table), key).columns(columnNames).build());
     }
 
     private static void assertNoPartitions(String key, String table)
     {
-        ReadCommand cmd = cmd(key, table);
+        ReadCommand cmd = Util.cmd(cfs(table), key).build();
 
         try (ReadExecutionController executionController = cmd.executionController();
              PartitionIterator iterator = cmd.executeInternal(executionController))

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java b/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
index cc97df0..87abdac 100644
--- a/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
+++ b/test/unit/org/apache/cassandra/hints/LegacyHintsMigratorTest.java
@@ -27,6 +27,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.Util;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.*;
@@ -159,7 +160,7 @@ public class LegacyHintsMigratorTest
 
                 assertEquals(timestamp, hint.creationTime);
                 assertEquals(ttl, hint.gcgs);
-                assertMutationsEqual(mutation, hint.mutation);
+                assertTrue(mutation + " != " + hint.mutation, Util.sameContent(mutation, hint.mutation));
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/test/unit/org/apache/cassandra/schema/DefsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/schema/DefsTest.java b/test/unit/org/apache/cassandra/schema/DefsTest.java
index e9980f6..d4ac1dc 100644
--- a/test/unit/org/apache/cassandra/schema/DefsTest.java
+++ b/test/unit/org/apache/cassandra/schema/DefsTest.java
@@ -498,7 +498,7 @@ public class DefsTest
     public void testDropIndex() throws ConfigurationException
     {
         // persist keyspace definition in the system keyspace
-        SchemaKeyspace.makeCreateKeyspaceMutation(Schema.instance.getKSMetaData(KEYSPACE6), FBUtilities.timestampMicros()).applyUnsafe();
+        SchemaKeyspace.makeCreateKeyspaceMutation(Schema.instance.getKSMetaData(KEYSPACE6), FBUtilities.timestampMicros()).build().applyUnsafe();
         ColumnFamilyStore cfs = Keyspace.open(KEYSPACE6).getColumnFamilyStore(TABLE1i);
         String indexName = "birthdate_key_index";
 


[14/14] cassandra git commit: Merge branch 'cassandra-3.9' into trunk

Posted by sl...@apache.org.
Merge branch 'cassandra-3.9' into trunk

* cassandra-3.9:
  Fix RTE on mixed-version cluster due to CDC schema changes.


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4e21f726
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4e21f726
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4e21f726

Branch: refs/heads/trunk
Commit: 4e21f7267dc051fdd8214c67859e639ee5a9824f
Parents: 5cc0d15 b603720
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Wed Aug 3 17:44:44 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Wed Aug 3 17:44:44 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   5 +
 NEWS.txt                                        |  10 +-
 .../cassandra/batchlog/BatchlogManager.java     |  19 +-
 .../batchlog/LegacyBatchlogMigrator.java        |   9 +-
 src/java/org/apache/cassandra/db/Mutation.java  |  66 +++
 .../apache/cassandra/db/RowUpdateBuilder.java   | 400 ----------------
 .../org/apache/cassandra/db/SimpleBuilders.java | 461 +++++++++++++++++++
 .../org/apache/cassandra/db/SystemKeyspace.java |  11 +-
 .../db/partitions/AbstractBTreePartition.java   |   2 +-
 .../db/partitions/PartitionUpdate.java          | 154 +++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  99 ++++
 src/java/org/apache/cassandra/db/rows/Rows.java |  16 +
 .../apache/cassandra/db/transform/BaseRows.java |   3 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |  12 +-
 .../apache/cassandra/schema/SchemaKeyspace.java | 427 +++++++++--------
 .../cassandra/service/MigrationManager.java     |   8 +-
 .../apache/cassandra/tracing/TraceKeyspace.java |  52 ++-
 .../org/apache/cassandra/UpdateBuilder.java     |  56 +--
 test/unit/org/apache/cassandra/Util.java        |  26 +-
 .../apache/cassandra/batchlog/BatchTest.java    |  17 +-
 .../apache/cassandra/config/CFMetaDataTest.java |   2 +-
 .../apache/cassandra/cql3/CDCStatementTest.java |  10 +
 .../entities/RowUpdateBuilderTest.java          |  79 ----
 .../db/RecoveryManagerMissingHeaderTest.java    |   4 +-
 .../cassandra/db/RecoveryManagerTest.java       |   8 +-
 .../apache/cassandra/db/RowUpdateBuilder.java   | 196 ++++++++
 .../cassandra/db/compaction/TTLExpiryTest.java  |   2 +-
 .../db/partition/PartitionUpdateTest.java       |  23 +-
 .../org/apache/cassandra/hints/HintTest.java    |  56 +--
 .../hints/LegacyHintsMigratorTest.java          |   3 +-
 .../org/apache/cassandra/schema/DefsTest.java   |   2 +-
 .../schema/LegacySchemaMigratorTest.java        | 111 ++---
 .../cassandra/schema/SchemaKeyspaceTest.java    |   6 +-
 .../cassandra/service/DataResolverTest.java     |   2 +-
 .../streaming/StreamingTransferTest.java        |   2 +-
 35 files changed, 1452 insertions(+), 907 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 6234687,62a6e6f..debdd74
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -53,7 -25,9 +53,12 @@@ Merged from 2.1
   * cannot use cql since upgrading python to 2.7.11+ (CASSANDRA-11850)
   * Allow STCS-in-L0 compactions to reduce scope with LCS (CASSANDRA-12040)
  
++<<<<<<< HEAD
++=======
+ 
++>>>>>>> cassandra-3.9
  3.8
+  * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
   * Fix hdr logging for single operation workloads (CASSANDRA-12145)
   * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
   * Increase size of flushExecutor thread pool (CASSANDRA-12071)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/NEWS.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/test/unit/org/apache/cassandra/UpdateBuilder.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/UpdateBuilder.java
index 047baaa,19e48f2..9fcda15
--- a/test/unit/org/apache/cassandra/UpdateBuilder.java
+++ b/test/unit/org/apache/cassandra/UpdateBuilder.java
@@@ -21,8 -21,10 +21,9 @@@ import java.nio.ByteBuffer
  
  import org.apache.cassandra.config.CFMetaData;
  import org.apache.cassandra.db.*;
+ import org.apache.cassandra.db.rows.*;
  import org.apache.cassandra.db.partitions.*;
  import org.apache.cassandra.utils.FBUtilities;
 -import org.apache.cassandra.service.StorageService;
  
  
  /**

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/test/unit/org/apache/cassandra/Util.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/test/unit/org/apache/cassandra/db/compaction/TTLExpiryTest.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4e21f726/test/unit/org/apache/cassandra/schema/LegacySchemaMigratorTest.java
----------------------------------------------------------------------


[11/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
Fix RTE on mixed-version cluster due to CDC schema changes.

Patch by jmckenzie and slebresne; reviewed by ayeschenko for CASSANDRA-12236


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/26838063
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/26838063
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/26838063

Branch: refs/heads/trunk
Commit: 26838063de6246e3a1e18062114ca92fb81c00cf
Parents: b27e2f9
Author: Josh McKenzie <jm...@apache.org>
Authored: Thu Jul 21 12:45:13 2016 -0400
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Wed Aug 3 17:41:24 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 NEWS.txt                                        |  10 +-
 .../cassandra/batchlog/BatchlogManager.java     |  19 +-
 .../batchlog/LegacyBatchlogMigrator.java        |   9 +-
 src/java/org/apache/cassandra/db/Mutation.java  |  66 +++
 .../apache/cassandra/db/RowUpdateBuilder.java   | 400 ----------------
 .../org/apache/cassandra/db/SimpleBuilders.java | 461 +++++++++++++++++++
 .../org/apache/cassandra/db/SystemKeyspace.java |  11 +-
 .../db/partitions/AbstractBTreePartition.java   |   2 +-
 .../db/partitions/PartitionUpdate.java          | 154 +++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  99 ++++
 src/java/org/apache/cassandra/db/rows/Rows.java |  16 +
 .../apache/cassandra/db/transform/BaseRows.java |   3 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |  12 +-
 .../apache/cassandra/schema/SchemaKeyspace.java | 427 +++++++++--------
 .../cassandra/service/MigrationManager.java     |   8 +-
 .../apache/cassandra/tracing/TraceKeyspace.java |  52 ++-
 .../org/apache/cassandra/UpdateBuilder.java     |  56 +--
 test/unit/org/apache/cassandra/Util.java        |  26 +-
 .../apache/cassandra/batchlog/BatchTest.java    |  17 +-
 .../apache/cassandra/config/CFMetaDataTest.java |   2 +-
 .../apache/cassandra/cql3/CDCStatementTest.java |  10 +
 .../entities/RowUpdateBuilderTest.java          |  79 ----
 .../db/RecoveryManagerMissingHeaderTest.java    |   4 +-
 .../cassandra/db/RecoveryManagerTest.java       |   8 +-
 .../apache/cassandra/db/RowUpdateBuilder.java   | 196 ++++++++
 .../cassandra/db/compaction/TTLExpiryTest.java  |   2 +-
 .../db/partition/PartitionUpdateTest.java       |  23 +-
 .../org/apache/cassandra/hints/HintTest.java    |  56 +--
 .../hints/LegacyHintsMigratorTest.java          |   3 +-
 .../org/apache/cassandra/schema/DefsTest.java   |   2 +-
 .../schema/LegacySchemaMigratorTest.java        | 111 ++---
 .../cassandra/schema/SchemaKeyspaceTest.java    |   6 +-
 .../cassandra/service/DataResolverTest.java     |   2 +-
 .../streaming/StreamingTransferTest.java        |   2 +-
 35 files changed, 1448 insertions(+), 907 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 4330fde..388a290 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.8
+ * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
  * Fix hdr logging for single operation workloads (CASSANDRA-12145)
  * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
  * Increase size of flushExecutor thread pool (CASSANDRA-12071)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 7418f3a..d8d84f5 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -39,6 +39,9 @@ New features
      the data/cdc_raw directory until removed by the user and writes to CDC-enabled tables
      will be rejected with a WriteTimeoutException once cdc_total_space_in_mb is reached
      between unflushed CommitLogSegments and cdc_raw.
+     NOTE: CDC is disabled by default in the .yaml file. Do not enable CDC on a mixed-version
+     cluster as it will lead to exceptions which can interrupt traffic. Once all nodes
+     have been upgraded to 3.8 it is safe to enable this feature and restart the cluster.
 
 Upgrading
 ---------
@@ -48,13 +51,6 @@ Upgrading
       those under a different name, change your code to use the new names and
       drop the old versions, and this _before_ upgrade (see CASSANDRA-10783 for more
       details).
-    - Due to changes in schema migration handling and the storage format after 3.0, you will
-      see error messages such as:
-         "java.lang.RuntimeException: Unknown column cdc during deserialization"
-      in your system logs on a mixed-version cluster during upgrades. This error message
-      is harmless and due to the 3.8 nodes having cdc added to their schema tables while
-      the <3.8 nodes do not. This message should cease once all nodes are upgraded to 3.8.
-      As always, refrain from schema changes during cluster upgrades.
 
 Deprecation
 -----------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
index f5133bb..0bc9185 100644
--- a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
+++ b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
@@ -121,20 +121,15 @@ public class BatchlogManager implements BatchlogManagerMBean
 
     public static void store(Batch batch, boolean durableWrites)
     {
-        RowUpdateBuilder builder =
-            new RowUpdateBuilder(SystemKeyspace.Batches, batch.creationTime, batch.id)
-                .clustering()
-                .add("version", MessagingService.current_version);
-
-        for (ByteBuffer mutation : batch.encodedMutations)
-            builder.addListEntry("mutations", mutation);
+        List<ByteBuffer> mutations = new ArrayList<>(batch.encodedMutations.size() + batch.decodedMutations.size());
+        mutations.addAll(batch.encodedMutations);
 
         for (Mutation mutation : batch.decodedMutations)
         {
             try (DataOutputBuffer buffer = new DataOutputBuffer())
             {
                 Mutation.serializer.serialize(mutation, buffer, MessagingService.current_version);
-                builder.addListEntry("mutations", buffer.buffer());
+                mutations.add(buffer.buffer());
             }
             catch (IOException e)
             {
@@ -143,7 +138,13 @@ public class BatchlogManager implements BatchlogManagerMBean
             }
         }
 
-        builder.build().apply(durableWrites);
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(SystemKeyspace.Batches, batch.id);
+        builder.row()
+               .timestamp(batch.creationTime)
+               .add("version", MessagingService.current_version)
+               .appendAll("mutations", mutations);
+
+        builder.buildAsMutation().apply(durableWrites);
     }
 
     @VisibleForTesting

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java b/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
index dd19f19..3a8bf83 100644
--- a/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
+++ b/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
@@ -162,12 +162,13 @@ public final class LegacyBatchlogMigrator
     @SuppressWarnings("deprecation")
     static Mutation getStoreMutation(Batch batch, int version)
     {
-        return new RowUpdateBuilder(SystemKeyspace.LegacyBatchlog, batch.creationTime, batch.id)
-               .clustering()
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(SystemKeyspace.LegacyBatchlog, batch.id);
+        builder.row()
+               .timestamp(batch.creationTime)
                .add("written_at", new Date(batch.creationTime / 1000))
                .add("data", getSerializedMutations(version, batch.decodedMutations))
-               .add("version", version)
-               .build();
+               .add("version", version);
+        return builder.buildAsMutation();
     }
 
     @SuppressWarnings("deprecation")

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/Mutation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Mutation.java b/src/java/org/apache/cassandra/db/Mutation.java
index 61e5ee9..b8639a7 100644
--- a/src/java/org/apache/cassandra/db/Mutation.java
+++ b/src/java/org/apache/cassandra/db/Mutation.java
@@ -302,6 +302,72 @@ public class Mutation implements IMutation
         return buff.append("])").toString();
     }
 
+    /**
+     * Creates a new simple mutuation builder.
+     *
+     * @param keyspaceName the name of the keyspace this is a mutation for.
+     * @param partitionKey the key of partition this if a mutation for.
+     * @return a newly created builder.
+     */
+    public static SimpleBuilder simpleBuilder(String keyspaceName, DecoratedKey partitionKey)
+    {
+        return new SimpleBuilders.MutationBuilder(keyspaceName, partitionKey);
+    }
+
+    /**
+     * Interface for building mutations geared towards human.
+     * <p>
+     * This should generally not be used when performance matters too much, but provides a more convenient interface to
+     * build a mutation than using the class constructor when performance is not of the utmost importance.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * Sets the timestamp to use for the following additions to this builder or any derived (update or row) builder.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions to this builder or any derived (update or row) builder.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * ttl to be used for the row {@code LivenessInfo}.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Adds an update for table identified by the provided metadata and return a builder for that partition.
+         *
+         * @param metadata the metadata of the table for which to add an update.
+         * @return a builder for the partition identified by {@code metadata} (and the partition key for which this is a
+         * mutation of).
+         */
+        public PartitionUpdate.SimpleBuilder update(CFMetaData metadata);
+
+        /**
+         * Adds an update for table identified by the provided name and return a builder for that partition.
+         *
+         * @param tableName the name of the table for which to add an update.
+         * @return a builder for the partition identified by {@code metadata} (and the partition key for which this is a
+         * mutation of).
+         */
+        public PartitionUpdate.SimpleBuilder update(String tableName);
+
+        /**
+         * Build the mutation represented by this builder.
+         *
+         * @return the built mutation.
+         */
+        public Mutation build();
+    }
+
     public static class MutationSerializer implements IVersionedSerializer<Mutation>
     {
         public void serialize(Mutation mutation, DataOutputPlus out, int version) throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/RowUpdateBuilder.java b/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
deleted file mode 100644
index b414eba..0000000
--- a/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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.cassandra.db;
-
-import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.cassandra.cql3.ColumnIdentifier;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.db.marshal.SetType;
-import org.apache.cassandra.db.rows.*;
-import org.apache.cassandra.db.context.CounterContext;
-import org.apache.cassandra.db.partitions.*;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.ListType;
-import org.apache.cassandra.db.marshal.MapType;
-import org.apache.cassandra.utils.*;
-
-/**
- * Convenience object to create single row updates.
- *
- * This is meant for system table update, when performance is not of the utmost importance.
- */
-public class RowUpdateBuilder
-{
-    private final PartitionUpdate update;
-
-    private final long timestamp;
-    private final int ttl;
-    private final int localDeletionTime;
-
-    private final DeletionTime deletionTime;
-
-    private final Mutation mutation;
-
-    private Row.Builder regularBuilder;
-    private Row.Builder staticBuilder;
-
-    private boolean useRowMarker = true;
-
-    private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, int localDeletionTime, Mutation mutation)
-    {
-        this.update = update;
-
-        this.timestamp = timestamp;
-        this.ttl = ttl;
-        this.localDeletionTime = localDeletionTime;
-        this.deletionTime = new DeletionTime(timestamp, localDeletionTime);
-
-        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
-        // underneath (this class if for convenience, not performance)
-        this.mutation = mutation == null ? new Mutation(update.metadata().ksName, update.partitionKey()).add(update) : mutation;
-    }
-
-    private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, Mutation mutation)
-    {
-        this(update, timestamp, ttl, FBUtilities.nowInSeconds(), mutation);
-    }
-
-    private void startRow(Clustering clustering)
-    {
-        assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        assert regularBuilder == null : "Cannot add the clustering twice to the same row";
-
-        regularBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
-        regularBuilder.newRow(clustering);
-
-        // If a CQL table, add the "row marker"
-        if (update.metadata().isCQLTable() && useRowMarker)
-            regularBuilder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, localDeletionTime));
-    }
-
-    private Row.Builder builder()
-    {
-        assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        if (regularBuilder == null)
-        {
-            // we don't force people to call clustering() if the table has no clustering, so call it ourselves
-            assert update.metadata().comparator.size() == 0 : "Missing call to clustering()";
-            startRow(Clustering.EMPTY);
-        }
-        return regularBuilder;
-    }
-
-    private Row.Builder staticBuilder()
-    {
-        assert regularBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        if (staticBuilder == null)
-        {
-            staticBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
-            staticBuilder.newRow(Clustering.STATIC_CLUSTERING);
-        }
-        return staticBuilder;
-    }
-
-    private Row.Builder builder(ColumnDefinition c)
-    {
-        return c.isStatic() ? staticBuilder() : builder();
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
-    {
-        this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
-    {
-        this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
-    {
-        this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
-    {
-        this(new PartitionUpdate(metadata, makeKey(metadata, partitionKey), metadata.partitionColumns(), 1), timestamp, ttl, localDeletionTime, null);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Mutation mutation)
-    {
-        this(metadata, timestamp, LivenessInfo.NO_TTL, mutation);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Mutation mutation)
-    {
-        this(getOrAdd(metadata, mutation), timestamp, ttl, mutation);
-    }
-
-    public RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl)
-    {
-        this(update, timestamp, ttl, null);
-    }
-
-    // This must be called before any addition or deletion if used.
-    public RowUpdateBuilder noRowMarker()
-    {
-        this.useRowMarker = false;
-        return this;
-    }
-
-    public RowUpdateBuilder clustering(Object... clusteringValues)
-    {
-        assert clusteringValues.length == update.metadata().comparator.size()
-             : "Invalid clustering values length. Expected: " + update.metadata().comparator.size() + " got: " + clusteringValues.length;
-
-        startRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
-        return this;
-    }
-
-    public Mutation build()
-    {
-        Row.Builder builder = regularBuilder == null ? staticBuilder : regularBuilder;
-        if (builder != null)
-            update.add(builder.build());
-        return mutation;
-    }
-
-    public PartitionUpdate buildUpdate()
-    {
-        build();
-        return update;
-    }
-
-    private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues)
-    {
-        assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty());
-
-        boolean isStatic = clusteringValues.length != update.metadata().comparator.size();
-        Row.Builder builder = BTreeRow.sortedBuilder();
-
-        if (isStatic)
-            builder.newRow(Clustering.STATIC_CLUSTERING);
-        else
-            builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
-        builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime)));
-
-        update.add(builder.build());
-    }
-
-    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Mutation mutation, Object... clusteringValues)
-    {
-        deleteRow(getOrAdd(metadata, mutation), timestamp, FBUtilities.nowInSeconds(), clusteringValues);
-        return mutation;
-    }
-
-    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
-    {
-        return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
-    }
-
-    public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
-    {
-        PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 0);
-        deleteRow(update, timestamp, localDeletionTime, clusteringValues);
-        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
-        // underneath (this class if for convenience, not performance)
-        return new Mutation(update.metadata().ksName, update.partitionKey()).add(update);
-    }
-
-    private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
-    {
-        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
-            return (DecoratedKey)partitionKey[0];
-
-        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
-        return metadata.decorateKey(key);
-    }
-
-    private static PartitionUpdate getOrAdd(CFMetaData metadata, Mutation mutation)
-    {
-        PartitionUpdate upd = mutation.get(metadata);
-        if (upd == null)
-        {
-            upd = new PartitionUpdate(metadata, mutation.key(), metadata.partitionColumns(), 1);
-            mutation.add(upd);
-        }
-        return upd;
-    }
-
-    public RowUpdateBuilder resetCollection(String columnName)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type.isCollection() && c.type.isMultiCell();
-        builder(c).addComplexDeletion(c, new DeletionTime(timestamp - 1, localDeletionTime));
-        return this;
-    }
-
-    public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
-    {
-        update.add(rt);
-        return this;
-    }
-
-    public RowUpdateBuilder addRangeTombstone(Slice slice)
-    {
-        return addRangeTombstone(new RangeTombstone(slice, deletionTime));
-    }
-
-    public RowUpdateBuilder addRangeTombstone(Object start, Object end)
-    {
-        ClusteringComparator cmp = update.metadata().comparator;
-        Slice slice = Slice.make(cmp.make(start), cmp.make(end));
-        return addRangeTombstone(slice);
-    }
-
-    public RowUpdateBuilder add(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        return add(c, value);
-    }
-
-    private Cell makeCell(ColumnDefinition c, ByteBuffer value, CellPath path)
-    {
-        return value == null
-             ? BufferCell.tombstone(c, timestamp, localDeletionTime)
-             : (ttl == LivenessInfo.NO_TTL ? BufferCell.live(c, timestamp, value, path) : BufferCell.expiring(c, timestamp, ttl, localDeletionTime, value, path));
-    }
-
-    public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
-    {
-        assert columnDefinition.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + columnDefinition + " since no clustering hasn't been provided";
-        builder(columnDefinition).addCell(makeCell(columnDefinition, bb(value, columnDefinition.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder delete(String columnName)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        return delete(c);
-    }
-
-    public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
-    {
-        return add(columnDefinition, null);
-    }
-
-    private static ByteBuffer bb(Object value, AbstractType<?> type)
-    {
-        if (value == null)
-            return null;
-
-        if (value instanceof ByteBuffer)
-            return (ByteBuffer)value;
-
-        if (type.isCounter())
-        {
-            // See UpdateParameters.addCounter()
-            assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
-            return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
-        }
-        return ((AbstractType)type).decompose(value);
-    }
-
-    public RowUpdateBuilder map(String columnName, Map<?, ?> map)
-    {
-        resetCollection(columnName);
-        for (Map.Entry<?, ?> entry : map.entrySet())
-            addMapEntry(columnName, entry.getKey(), entry.getValue());
-        return this;
-    }
-
-    public RowUpdateBuilder set(String columnName, Set<?> set)
-    {
-        resetCollection(columnName);
-        for (Object element : set)
-            addSetEntry(columnName, element);
-        return this;
-    }
-
-    public RowUpdateBuilder frozenList(String columnName, List<?> list)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof ListType && !c.type.isMultiCell() : "Column " + c + " is not a frozen list";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(list), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder frozenSet(String columnName, Set<?> set)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof SetType && !c.type.isMultiCell() : "Column " + c + " is not a frozen set";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(set), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder frozenMap(String columnName, Map<?, ?> map)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof MapType && !c.type.isMultiCell() : "Column " + c + " is not a frozen map";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(map), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder addMapEntry(String columnName, Object key, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof MapType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen map";
-        MapType mt = (MapType)c.type;
-        builder(c).addCell(makeCell(c, bb(value, mt.getValuesType()), CellPath.create(bb(key, mt.getKeysType()))));
-        return this;
-    }
-
-    public RowUpdateBuilder addListEntry(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof ListType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen list";
-        ListType lt = (ListType)c.type;
-        builder(c).addCell(makeCell(c, bb(value, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
-        return this;
-    }
-
-    public RowUpdateBuilder addSetEntry(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof SetType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen set";
-        SetType st = (SetType)c.type;
-        builder(c).addCell(makeCell(c, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(bb(value, st.getElementsType()))));
-        return this;
-    }
-
-    private ColumnDefinition getDefinition(String name)
-    {
-        return update.metadata().getColumnDefinition(new ColumnIdentifier(name, true));
-    }
-
-    public UnfilteredRowIterator unfilteredIterator()
-    {
-        return update.unfilteredIterator();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/SimpleBuilders.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SimpleBuilders.java b/src/java/org/apache/cassandra/db/SimpleBuilders.java
new file mode 100644
index 0000000..6e65743
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/SimpleBuilders.java
@@ -0,0 +1,461 @@
+/*
+ * 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.cassandra.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.db.context.CounterContext;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
+import org.apache.cassandra.db.rows.BTreeRow;
+import org.apache.cassandra.db.rows.BufferCell;
+import org.apache.cassandra.db.rows.Cell;
+import org.apache.cassandra.db.rows.CellPath;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.CounterId;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.UUIDGen;
+
+public abstract class SimpleBuilders
+{
+    private SimpleBuilders()
+    {
+    }
+
+    private static DecoratedKey makePartitonKey(CFMetaData metadata, Object... partitionKey)
+    {
+        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
+            return (DecoratedKey)partitionKey[0];
+
+        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
+        return metadata.decorateKey(key);
+    }
+
+    private static Clustering makeClustering(CFMetaData metadata, Object... clusteringColumns)
+    {
+        if (clusteringColumns.length == 1 && clusteringColumns[0] instanceof Clustering)
+            return (Clustering)clusteringColumns[0];
+
+        if (clusteringColumns.length == 0)
+        {
+            // If the table has clustering columns, passing no values is for updating the static values, so check we
+            // do have some static columns defined.
+            assert metadata.comparator.size() == 0 || !metadata.partitionColumns().statics.isEmpty();
+            return metadata.comparator.size() == 0 ? Clustering.EMPTY : Clustering.STATIC_CLUSTERING;
+        }
+        else
+        {
+            return metadata.comparator.make(clusteringColumns);
+        }
+    }
+
+    private static class AbstractBuilder<T>
+    {
+        protected long timestamp = FBUtilities.timestampMicros();
+        protected int ttl = 0;
+        protected int nowInSec = FBUtilities.nowInSeconds();
+
+        protected void copyParams(AbstractBuilder<?> other)
+        {
+            other.timestamp = timestamp;
+            other.ttl = ttl;
+            other.nowInSec = nowInSec;
+        }
+
+        public T timestamp(long timestamp)
+        {
+            this.timestamp = timestamp;
+            return (T)this;
+        }
+
+        public T ttl(int ttl)
+        {
+            this.ttl = ttl;
+            return (T)this;
+        }
+
+        public T nowInSec(int nowInSec)
+        {
+            this.nowInSec = nowInSec;
+            return (T)this;
+        }
+    }
+
+    public static class MutationBuilder extends AbstractBuilder<Mutation.SimpleBuilder> implements Mutation.SimpleBuilder
+    {
+        private final String keyspaceName;
+        private final DecoratedKey key;
+
+        private final Map<UUID, PartitionUpdateBuilder> updateBuilders = new HashMap<>();
+
+        public MutationBuilder(String keyspaceName, DecoratedKey key)
+        {
+            this.keyspaceName = keyspaceName;
+            this.key = key;
+        }
+
+        public PartitionUpdate.SimpleBuilder update(CFMetaData metadata)
+        {
+            assert metadata.ksName.equals(keyspaceName);
+
+            PartitionUpdateBuilder builder = updateBuilders.get(metadata.cfId);
+            if (builder == null)
+            {
+                builder = new PartitionUpdateBuilder(metadata, key);
+                updateBuilders.put(metadata.cfId, builder);
+            }
+
+            copyParams(builder);
+
+            return builder;
+        }
+
+        public PartitionUpdate.SimpleBuilder update(String tableName)
+        {
+            CFMetaData metadata = Schema.instance.getCFMetaData(keyspaceName, tableName);
+            assert metadata != null : "Unknown table " + tableName + " in keyspace " + keyspaceName;
+            return update(metadata);
+        }
+
+        public Mutation build()
+        {
+            assert !updateBuilders.isEmpty() : "Cannot create empty mutation";
+
+            if (updateBuilders.size() == 1)
+                return new Mutation(updateBuilders.values().iterator().next().build());
+
+            Mutation mutation = new Mutation(keyspaceName, key);
+            for (PartitionUpdateBuilder builder : updateBuilders.values())
+                mutation.add(builder.build());
+            return mutation;
+        }
+    }
+
+    public static class PartitionUpdateBuilder extends AbstractBuilder<PartitionUpdate.SimpleBuilder> implements PartitionUpdate.SimpleBuilder
+    {
+        private final CFMetaData metadata;
+        private final DecoratedKey key;
+        private final Map<Clustering, RowBuilder> rowBuilders = new HashMap<>();
+        private List<RTBuilder> rangeBuilders = null; // We use that rarely, so create lazily
+
+        private DeletionTime partitionDeletion = DeletionTime.LIVE;
+
+        public PartitionUpdateBuilder(CFMetaData metadata, Object... partitionKeyValues)
+        {
+            this.metadata = metadata;
+            this.key = makePartitonKey(metadata, partitionKeyValues);
+        }
+
+        public CFMetaData metadata()
+        {
+            return metadata;
+        }
+
+        public Row.SimpleBuilder row(Object... clusteringValues)
+        {
+            Clustering clustering = makeClustering(metadata, clusteringValues);
+            RowBuilder builder = rowBuilders.get(clustering);
+            if (builder == null)
+            {
+                builder = new RowBuilder(metadata, clustering);
+                rowBuilders.put(clustering, builder);
+            }
+
+            copyParams(builder);
+
+            return builder;
+        }
+
+        public PartitionUpdate.SimpleBuilder delete()
+        {
+            this.partitionDeletion = new DeletionTime(timestamp, nowInSec);
+            return this;
+        }
+
+        public RangeTombstoneBuilder addRangeTombstone()
+        {
+            if (rangeBuilders == null)
+                rangeBuilders = new ArrayList<>();
+
+            RTBuilder builder = new RTBuilder(metadata.comparator, new DeletionTime(timestamp, nowInSec));
+            rangeBuilders.add(builder);
+            return builder;
+        }
+
+        public PartitionUpdate build()
+        {
+            // Collect all updated columns
+            PartitionColumns.Builder columns = PartitionColumns.builder();
+            for (RowBuilder builder : rowBuilders.values())
+                columns.addAll(builder.columns());
+
+            // Note that rowBuilders.size() could include the static column so could be 1 off the really need capacity
+            // of the final PartitionUpdate, but as that's just a sizing hint, we'll live.
+            PartitionUpdate update = new PartitionUpdate(metadata, key, columns.build(), rowBuilders.size());
+
+            update.addPartitionDeletion(partitionDeletion);
+            if (rangeBuilders != null)
+            {
+                for (RTBuilder builder : rangeBuilders)
+                    update.add(builder.build());
+            }
+
+            for (RowBuilder builder : rowBuilders.values())
+                update.add(builder.build());
+
+            return update;
+        }
+
+        public Mutation buildAsMutation()
+        {
+            return new Mutation(build());
+        }
+
+        private static class RTBuilder implements RangeTombstoneBuilder
+        {
+            private final ClusteringComparator comparator;
+            private final DeletionTime deletionTime;
+
+            private Object[] start;
+            private Object[] end;
+
+            private boolean startInclusive = true;
+            private boolean endInclusive = true;
+
+            private RTBuilder(ClusteringComparator comparator, DeletionTime deletionTime)
+            {
+                this.comparator = comparator;
+                this.deletionTime = deletionTime;
+            }
+
+            public RangeTombstoneBuilder start(Object... values)
+            {
+                this.start = values;
+                return this;
+            }
+
+            public RangeTombstoneBuilder end(Object... values)
+            {
+                this.end = values;
+                return this;
+            }
+
+            public RangeTombstoneBuilder inclStart()
+            {
+                this.startInclusive = true;
+                return this;
+            }
+
+            public RangeTombstoneBuilder exclStart()
+            {
+                this.startInclusive = false;
+                return this;
+            }
+
+            public RangeTombstoneBuilder inclEnd()
+            {
+                this.endInclusive = true;
+                return this;
+            }
+
+            public RangeTombstoneBuilder exclEnd()
+            {
+                this.endInclusive = false;
+                return this;
+            }
+
+            private RangeTombstone build()
+            {
+                ClusteringBound startBound = ClusteringBound.create(comparator, true, startInclusive, start);
+                ClusteringBound endBound = ClusteringBound.create(comparator, false, endInclusive, end);
+                return new RangeTombstone(Slice.make(startBound, endBound), deletionTime);
+            }
+        }
+    }
+
+    public static class RowBuilder extends AbstractBuilder<Row.SimpleBuilder> implements Row.SimpleBuilder
+    {
+        private final CFMetaData metadata;
+
+        private final Set<ColumnDefinition> columns = new HashSet<>();
+        private final Row.Builder builder;
+
+        private boolean initiated;
+        private boolean noPrimaryKeyLivenessInfo;
+
+        public RowBuilder(CFMetaData metadata, Object... clusteringColumns)
+        {
+            this.metadata = metadata;
+            this.builder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
+
+            this.builder.newRow(makeClustering(metadata, clusteringColumns));
+        }
+
+        Set<ColumnDefinition> columns()
+        {
+            return columns;
+        }
+
+        private void maybeInit()
+        {
+            // We're working around the fact that Row.Builder requires that addPrimaryKeyLivenessInfo() and
+            // addRowDeletion() are called before any cell addition (which is done so the builder can more easily skip
+            // shadowed cells).
+            if (initiated)
+                return;
+
+            // If a CQL table, add the "row marker"
+            if (metadata.isCQLTable() && !noPrimaryKeyLivenessInfo)
+                builder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, nowInSec));
+
+            initiated = true;
+        }
+
+        public Row.SimpleBuilder add(String columnName, Object value)
+        {
+            return add(columnName, value, true);
+        }
+
+        public Row.SimpleBuilder appendAll(String columnName, Object value)
+        {
+            return add(columnName, value, false);
+        }
+
+        private Row.SimpleBuilder add(String columnName, Object value, boolean overwriteForCollection)
+        {
+            maybeInit();
+            ColumnDefinition column = getColumn(columnName);
+
+            if (!overwriteForCollection && !(column.type.isMultiCell() && column.type.isCollection()))
+                throw new IllegalArgumentException("appendAll() can only be called on non-frozen colletions");
+
+            columns.add(column);
+
+            if (!column.type.isMultiCell())
+            {
+                builder.addCell(cell(column, toByteBuffer(value, column.type), null));
+                return this;
+            }
+
+            assert column.type instanceof CollectionType : "Collection are the only multi-cell types supported so far";
+
+            if (value == null)
+            {
+                builder.addComplexDeletion(column, new DeletionTime(timestamp, nowInSec));
+                return this;
+            }
+
+            // Erase previous entry if any.
+            if (overwriteForCollection)
+                builder.addComplexDeletion(column, new DeletionTime(timestamp - 1, nowInSec));
+            switch (((CollectionType)column.type).kind)
+            {
+                case LIST:
+                    ListType lt = (ListType)column.type;
+                    assert value instanceof List;
+                    for (Object elt : (List)value)
+                        builder.addCell(cell(column, toByteBuffer(elt, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
+                    break;
+                case SET:
+                    SetType st = (SetType)column.type;
+                    assert value instanceof Set;
+                    for (Object elt : (Set)value)
+                        builder.addCell(cell(column, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(toByteBuffer(elt, st.getElementsType()))));
+                    break;
+                case MAP:
+                    MapType mt = (MapType)column.type;
+                    assert value instanceof Map;
+                    for (Map.Entry entry : ((Map<?, ?>)value).entrySet())
+                        builder.addCell(cell(column,
+                                             toByteBuffer(entry.getValue(), mt.getValuesType()),
+                                             CellPath.create(toByteBuffer(entry.getKey(), mt.getKeysType()))));
+                    break;
+                default:
+                    throw new AssertionError();
+            }
+            return this;
+        }
+
+        public Row.SimpleBuilder delete()
+        {
+            assert !initiated : "If called, delete() should be called before any other column value addition";
+            builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, nowInSec)));
+            return this;
+        }
+
+        public Row.SimpleBuilder delete(String columnName)
+        {
+            return add(columnName, null);
+        }
+
+        public Row.SimpleBuilder noPrimaryKeyLivenessInfo()
+        {
+            this.noPrimaryKeyLivenessInfo = true;
+            return this;
+        }
+
+        public Row build()
+        {
+            maybeInit();
+            return builder.build();
+        }
+
+        private ColumnDefinition getColumn(String columnName)
+        {
+            ColumnDefinition column = metadata.getColumnDefinition(new ColumnIdentifier(columnName, true));
+            assert column != null : "Cannot find column " + columnName;
+            assert !column.isPrimaryKeyColumn();
+            assert !column.isStatic() || builder.clustering() == Clustering.STATIC_CLUSTERING : "Cannot add non-static column to static-row";
+            return column;
+        }
+
+        private Cell cell(ColumnDefinition column, ByteBuffer value, CellPath path)
+        {
+            if (value == null)
+                return BufferCell.tombstone(column, timestamp, nowInSec, path);
+
+            return ttl == LivenessInfo.NO_TTL
+                 ? BufferCell.live(column, timestamp, value, path)
+                 : BufferCell.expiring(column, timestamp, ttl, nowInSec, value, path);
+        }
+
+        private ByteBuffer toByteBuffer(Object value, AbstractType<?> type)
+        {
+            if (value == null)
+                return null;
+
+            if (value instanceof ByteBuffer)
+                return (ByteBuffer)value;
+
+            if (type.isCounter())
+            {
+                // See UpdateParameters.addCounter()
+                assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
+                return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
+            }
+
+            return ((AbstractType)type).decompose(value);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java
index 584279d..36629a1 100644
--- a/src/java/org/apache/cassandra/db/SystemKeyspace.java
+++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java
@@ -44,6 +44,7 @@ import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.commitlog.CommitLogPosition;
 import org.apache.cassandra.db.compaction.CompactionHistoryTabularData;
 import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.db.rows.Rows;
 import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.dht.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
@@ -1233,11 +1234,11 @@ public final class SystemKeyspace
         {
             Range<Token> range = entry.getKey();
             Pair<Long, Long> values = entry.getValue();
-            new RowUpdateBuilder(SizeEstimates, timestamp, mutation)
-                .clustering(table, range.left.toString(), range.right.toString())
-                .add("partitions_count", values.left)
-                .add("mean_partition_size", values.right)
-                .build();
+            update.add(Rows.simpleBuilder(SizeEstimates, table, range.left.toString(), range.right.toString())
+                           .timestamp(timestamp)
+                           .add("partitions_count", values.left)
+                           .add("mean_partition_size", values.right)
+                           .build());
         }
 
         mutation.apply();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 1c05f3c..954168d 100644
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@ -169,7 +169,7 @@ public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
 
     public UnfilteredRowIterator unfilteredIterator()
     {
-        return unfilteredIterator(ColumnFilter.all(metadata()), Slices.ALL, false);
+        return unfilteredIterator(ColumnFilter.selection(columns()), Slices.ALL, false);
     }
 
     public UnfilteredRowIterator unfilteredIterator(ColumnFilter selection, Slices slices, boolean reversed)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java b/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
index d18392c..7796fd9 100644
--- a/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
+++ b/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
@@ -614,6 +614,160 @@ public class PartitionUpdate extends AbstractBTreePartition
         return sb.toString();
     }
 
+    /**
+     * Creates a new simple partition update builder.
+     *
+     * @param metadata the metadata for the table this is a partition of.
+     * @param partitionKeyValues the values for partition key columns identifying this partition. The values for each
+     * partition key column can be passed either directly as {@code ByteBuffer} or using a "native" value (int for
+     * Int32Type, string for UTF8Type, ...). It is also allowed to pass a single {@code DecoratedKey} value directly.
+     * @return a newly created builder.
+     */
+    public static SimpleBuilder simpleBuilder(CFMetaData metadata, Object... partitionKeyValues)
+    {
+        return new SimpleBuilders.PartitionUpdateBuilder(metadata, partitionKeyValues);
+    }
+
+    /**
+     * Interface for building partition updates geared towards human.
+     * <p>
+     * This should generally not be used when performance matters too much, but provides a more convenient interface to
+     * build an update than using the class constructor when performance is not of the utmost importance.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * The metadata of the table this is a builder on.
+         */
+        public CFMetaData metadata();
+
+        /**
+         * Sets the timestamp to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Sets the current time to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param nowInSec the current time to use for following additions. If the current time hasn't been set, the current
+         * time in seconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder nowInSec(int nowInSec);
+
+        /**
+         * Adds the row identifier by the provided clustering and return a builder for that row.
+         *
+         * @param clusteringValues the value for the clustering columns of the row to add to this build. There may be no
+         * values if either the table has no clustering column, or if you want to edit the static row. Note that as a
+         * shortcut it is also allowed to pass a {@code Clustering} object directly, in which case that should be the
+         * only argument.
+         * @return a builder for the row identified by {@code clusteringValues}.
+         */
+        public Row.SimpleBuilder row(Object... clusteringValues);
+
+        /**
+         * Deletes the partition identified by this builder (using a partition level deletion).
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder delete();
+
+        /**
+         * Adds a new range tombstone to this update, returning a builder for that range.
+         *
+         * @return the range tombstone builder for the newly added range.
+         */
+        public RangeTombstoneBuilder addRangeTombstone();
+
+        /**
+         * Build the update represented by this builder.
+         *
+         * @return the built update.
+         */
+        public PartitionUpdate build();
+
+        /**
+         * As shortcut for {@code new Mutation(build())}.
+         *
+         * @return the built update, wrapped in a {@code Mutation}.
+         */
+        public Mutation buildAsMutation();
+
+        /**
+         * Interface to build range tombstone.
+         *
+         * By default, if no other methods are called, the represented range is inclusive of both start and end and
+         * includes everything (its start is {@code BOTTOM} and it's end is {@code TOP}).
+         */
+        public interface RangeTombstoneBuilder
+        {
+            /**
+             * Sets the start for the built range using the provided values.
+             *
+             * @param values the value for the start of the range. They act like the {@code clusteringValues} argument
+             * of the {@link PartitionUpdate.SimpleBuilder#row()} method, except that it doesn't have to be a full
+             * clustering, it can only be a prefix.
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder start(Object... values);
+
+            /**
+             * Sets the end for the built range using the provided values.
+             *
+             * @param values the value for the end of the range. They act like the {@code clusteringValues} argument
+             * of the {@link PartitionUpdate.SimpleBuilder#row()} method, except that it doesn't have to be a full
+             * clustering, it can only be a prefix.
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder end(Object... values);
+
+            /**
+             * Sets the start of this range as inclusive.
+             * <p>
+             * This is the default and don't need to be called, but can for explicitness.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder inclStart();
+
+            /**
+             * Sets the start of this range as exclusive.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder exclStart();
+
+            /**
+             * Sets the end of this range as inclusive.
+             * <p>
+             * This is the default and don't need to be called, but can for explicitness.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder inclEnd();
+
+            /**
+             * Sets the end of this range as exclusive.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder exclEnd();
+        }
+    }
+
     public static class PartitionUpdateSerializer
     {
         public void serialize(PartitionUpdate update, DataOutputPlus out, int version) throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Row.java b/src/java/org/apache/cassandra/db/rows/Row.java
index 4fc3e22..7e6d141 100644
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@ -468,6 +468,105 @@ public interface Row extends Unfiltered, Collection<ColumnData>
     }
 
     /**
+     * Row builder interface geared towards human.
+     * <p>
+     * Where the {@link Builder} deals with building rows efficiently from internal objects ({@code Cell}, {@code
+     * LivenessInfo}, ...), the {@code SimpleBuilder} is geared towards building rows from string column name and
+     * 'native' values (string for text, ints for numbers, et...). In particular, it is meant to be convenient, not
+     * efficient, and should be used only in place where performance is not of the utmost importance (it is used to
+     * build schema mutation for instance).
+     * <p>
+     * Also note that contrarily to {@link Builder}, the {@code SimpleBuilder} API has no {@code newRow()} method: it is
+     * expected that the clustering of the row built is provided by the constructor of the builder.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * Sets the timestamp to use for the following additions.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * timestamp to be used for the row {@code LivenessInfo}.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * ttl to be used for the row {@code LivenessInfo}.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Adds a value to a given column.
+         *
+         * @param columnName the name of the column for which to add a new value.
+         * @param value the value to add, which must be of the proper type for {@code columnName}. This can be {@code
+         * null} in which case the this is equivalent to {@code delete(columnName)}.
+         * @return this builder.
+         */
+        public SimpleBuilder add(String columnName, Object value);
+
+        /**
+         * Appends new values to a given non-frozen collection column.
+         * <p>
+         * This method is similar to {@code add()} but the collection elements added through this method are "appended"
+         * to any pre-exising elements. In other words, this is like {@code add()} except that it doesn't delete the
+         * previous value of the collection. This can only be called on non-frozen collection columns.
+         * <p>
+         * Note that this method can be used in replacement of {@code add()} if you know that there can't be any
+         * pre-existing value for that column, in which case this is slightly less expensive as it avoid the collection
+         * tombstone inherent to {@code add()}.
+         *
+         * @param columnName the name of the column for which to add a new value, which must be a non-frozen collection.
+         * @param value the value to add, which must be of the proper type for {@code columnName} (in other words, it
+         * <b>must</b> be a collection).
+         * @return this builder.
+         *
+         * @throws IllegalArgumentException if columnName is not a non-frozen collection column.
+         */
+        public SimpleBuilder appendAll(String columnName, Object value);
+
+        /**
+         * Deletes the whole row.
+         * <p>
+         * If called, this is generally the only method called on the builder (outside of {@code timestamp()}.
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder delete();
+
+        /**
+         * Removes the value for a given column (creating a tombstone).
+         *
+         * @param columnName the name of the column to delete.
+         * @return this builder.
+         */
+        public SimpleBuilder delete(String columnName);
+
+        /**
+         * Don't include any primary key {@code LivenessInfo} in the built row.
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder noPrimaryKeyLivenessInfo();
+
+        /**
+         * Returns the built row.
+         *
+         * @return the built row.
+         */
+        public Row build();
+    }
+
+    /**
      * Utility class to help merging rows from multiple inputs (UnfilteredRowIterators).
      */
     public static class Merger

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Rows.java b/src/java/org/apache/cassandra/db/rows/Rows.java
index e325091..976d37e 100644
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@ -22,6 +22,7 @@ import java.util.*;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.PeekingIterator;
 
+import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.partitions.PartitionStatisticsCollector;
@@ -59,6 +60,21 @@ public abstract class Rows
     }
 
     /**
+     * Creates a new simple row builder.
+     *
+     * @param metadata the metadata of the table this is a row of.
+     * @param clusteringValues the value for the clustering columns of the row to add to this build. There may be no
+     * values if either the table has no clustering column, or if you want to edit the static row. Note that as a
+     * shortcut it is also allowed to pass a {@code Clustering} object directly, in which case that should be the
+     * only argument.
+     * @return a newly created builder.
+     */
+    public static Row.SimpleBuilder simpleBuilder(CFMetaData metadata, Object... clusteringValues)
+    {
+        return new SimpleBuilders.RowBuilder(metadata, clusteringValues);
+    }
+
+    /**
      * Collect statistics on a given row.
      *
      * @param row the row for which to collect stats.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/transform/BaseRows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/transform/BaseRows.java b/src/java/org/apache/cassandra/db/transform/BaseRows.java
index fb3b9f9..ce4e458 100644
--- a/src/java/org/apache/cassandra/db/transform/BaseRows.java
+++ b/src/java/org/apache/cassandra/db/transform/BaseRows.java
@@ -105,7 +105,8 @@ implements BaseRowIterator<R>
         super.add(transformation);
 
         // transform any existing data
-        staticRow = transformation.applyToStatic(staticRow);
+        if (staticRow != null)
+            staticRow = transformation.applyToStatic(staticRow);
         next = applyOne(next, transformation);
         partitionKey = transformation.applyToPartitionKey(partitionKey);
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
index 93591f0..7cc822f 100644
--- a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
+++ b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
@@ -148,20 +148,20 @@ public final class LegacySchemaMigrator
     {
         logger.info("Migrating keyspace {}", keyspace);
 
-        Mutation mutation = SchemaKeyspace.makeCreateKeyspaceMutation(keyspace.name, keyspace.params, keyspace.timestamp);
+        Mutation.SimpleBuilder builder = SchemaKeyspace.makeCreateKeyspaceMutation(keyspace.name, keyspace.params, keyspace.timestamp);
         for (Table table : keyspace.tables)
-            SchemaKeyspace.addTableToSchemaMutation(table.metadata, table.timestamp, true, mutation);
+            SchemaKeyspace.addTableToSchemaMutation(table.metadata, true, builder.timestamp(table.timestamp));
 
         for (Type type : keyspace.types)
-            SchemaKeyspace.addTypeToSchemaMutation(type.metadata, type.timestamp, mutation);
+            SchemaKeyspace.addTypeToSchemaMutation(type.metadata, builder.timestamp(type.timestamp));
 
         for (Function function : keyspace.functions)
-            SchemaKeyspace.addFunctionToSchemaMutation(function.metadata, function.timestamp, mutation);
+            SchemaKeyspace.addFunctionToSchemaMutation(function.metadata, builder.timestamp(function.timestamp));
 
         for (Aggregate aggregate : keyspace.aggregates)
-            SchemaKeyspace.addAggregateToSchemaMutation(aggregate.metadata, aggregate.timestamp, mutation);
+            SchemaKeyspace.addAggregateToSchemaMutation(aggregate.metadata, builder.timestamp(aggregate.timestamp));
 
-        mutation.apply();
+        builder.build().apply();
     }
 
     /*


[08/14] cassandra git commit: Fix RTE on mixed-version cluster due to CDC schema changes.

Posted by sl...@apache.org.
Fix RTE on mixed-version cluster due to CDC schema changes.

Patch by jmckenzie and slebresne; reviewed by ayeschenko for CASSANDRA-12236


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/26838063
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/26838063
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/26838063

Branch: refs/heads/cassandra-3.8
Commit: 26838063de6246e3a1e18062114ca92fb81c00cf
Parents: b27e2f9
Author: Josh McKenzie <jm...@apache.org>
Authored: Thu Jul 21 12:45:13 2016 -0400
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Wed Aug 3 17:41:24 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 NEWS.txt                                        |  10 +-
 .../cassandra/batchlog/BatchlogManager.java     |  19 +-
 .../batchlog/LegacyBatchlogMigrator.java        |   9 +-
 src/java/org/apache/cassandra/db/Mutation.java  |  66 +++
 .../apache/cassandra/db/RowUpdateBuilder.java   | 400 ----------------
 .../org/apache/cassandra/db/SimpleBuilders.java | 461 +++++++++++++++++++
 .../org/apache/cassandra/db/SystemKeyspace.java |  11 +-
 .../db/partitions/AbstractBTreePartition.java   |   2 +-
 .../db/partitions/PartitionUpdate.java          | 154 +++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  99 ++++
 src/java/org/apache/cassandra/db/rows/Rows.java |  16 +
 .../apache/cassandra/db/transform/BaseRows.java |   3 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |  12 +-
 .../apache/cassandra/schema/SchemaKeyspace.java | 427 +++++++++--------
 .../cassandra/service/MigrationManager.java     |   8 +-
 .../apache/cassandra/tracing/TraceKeyspace.java |  52 ++-
 .../org/apache/cassandra/UpdateBuilder.java     |  56 +--
 test/unit/org/apache/cassandra/Util.java        |  26 +-
 .../apache/cassandra/batchlog/BatchTest.java    |  17 +-
 .../apache/cassandra/config/CFMetaDataTest.java |   2 +-
 .../apache/cassandra/cql3/CDCStatementTest.java |  10 +
 .../entities/RowUpdateBuilderTest.java          |  79 ----
 .../db/RecoveryManagerMissingHeaderTest.java    |   4 +-
 .../cassandra/db/RecoveryManagerTest.java       |   8 +-
 .../apache/cassandra/db/RowUpdateBuilder.java   | 196 ++++++++
 .../cassandra/db/compaction/TTLExpiryTest.java  |   2 +-
 .../db/partition/PartitionUpdateTest.java       |  23 +-
 .../org/apache/cassandra/hints/HintTest.java    |  56 +--
 .../hints/LegacyHintsMigratorTest.java          |   3 +-
 .../org/apache/cassandra/schema/DefsTest.java   |   2 +-
 .../schema/LegacySchemaMigratorTest.java        | 111 ++---
 .../cassandra/schema/SchemaKeyspaceTest.java    |   6 +-
 .../cassandra/service/DataResolverTest.java     |   2 +-
 .../streaming/StreamingTransferTest.java        |   2 +-
 35 files changed, 1448 insertions(+), 907 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 4330fde..388a290 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.8
+ * RTE from new CDC column breaks in flight queries (CASSANDRA-12236)
  * Fix hdr logging for single operation workloads (CASSANDRA-12145)
  * Fix SASI PREFIX search in CONTAINS mode with partial terms (CASSANDRA-12073)
  * Increase size of flushExecutor thread pool (CASSANDRA-12071)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index 7418f3a..d8d84f5 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -39,6 +39,9 @@ New features
      the data/cdc_raw directory until removed by the user and writes to CDC-enabled tables
      will be rejected with a WriteTimeoutException once cdc_total_space_in_mb is reached
      between unflushed CommitLogSegments and cdc_raw.
+     NOTE: CDC is disabled by default in the .yaml file. Do not enable CDC on a mixed-version
+     cluster as it will lead to exceptions which can interrupt traffic. Once all nodes
+     have been upgraded to 3.8 it is safe to enable this feature and restart the cluster.
 
 Upgrading
 ---------
@@ -48,13 +51,6 @@ Upgrading
       those under a different name, change your code to use the new names and
       drop the old versions, and this _before_ upgrade (see CASSANDRA-10783 for more
       details).
-    - Due to changes in schema migration handling and the storage format after 3.0, you will
-      see error messages such as:
-         "java.lang.RuntimeException: Unknown column cdc during deserialization"
-      in your system logs on a mixed-version cluster during upgrades. This error message
-      is harmless and due to the 3.8 nodes having cdc added to their schema tables while
-      the <3.8 nodes do not. This message should cease once all nodes are upgraded to 3.8.
-      As always, refrain from schema changes during cluster upgrades.
 
 Deprecation
 -----------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
index f5133bb..0bc9185 100644
--- a/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
+++ b/src/java/org/apache/cassandra/batchlog/BatchlogManager.java
@@ -121,20 +121,15 @@ public class BatchlogManager implements BatchlogManagerMBean
 
     public static void store(Batch batch, boolean durableWrites)
     {
-        RowUpdateBuilder builder =
-            new RowUpdateBuilder(SystemKeyspace.Batches, batch.creationTime, batch.id)
-                .clustering()
-                .add("version", MessagingService.current_version);
-
-        for (ByteBuffer mutation : batch.encodedMutations)
-            builder.addListEntry("mutations", mutation);
+        List<ByteBuffer> mutations = new ArrayList<>(batch.encodedMutations.size() + batch.decodedMutations.size());
+        mutations.addAll(batch.encodedMutations);
 
         for (Mutation mutation : batch.decodedMutations)
         {
             try (DataOutputBuffer buffer = new DataOutputBuffer())
             {
                 Mutation.serializer.serialize(mutation, buffer, MessagingService.current_version);
-                builder.addListEntry("mutations", buffer.buffer());
+                mutations.add(buffer.buffer());
             }
             catch (IOException e)
             {
@@ -143,7 +138,13 @@ public class BatchlogManager implements BatchlogManagerMBean
             }
         }
 
-        builder.build().apply(durableWrites);
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(SystemKeyspace.Batches, batch.id);
+        builder.row()
+               .timestamp(batch.creationTime)
+               .add("version", MessagingService.current_version)
+               .appendAll("mutations", mutations);
+
+        builder.buildAsMutation().apply(durableWrites);
     }
 
     @VisibleForTesting

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java b/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
index dd19f19..3a8bf83 100644
--- a/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
+++ b/src/java/org/apache/cassandra/batchlog/LegacyBatchlogMigrator.java
@@ -162,12 +162,13 @@ public final class LegacyBatchlogMigrator
     @SuppressWarnings("deprecation")
     static Mutation getStoreMutation(Batch batch, int version)
     {
-        return new RowUpdateBuilder(SystemKeyspace.LegacyBatchlog, batch.creationTime, batch.id)
-               .clustering()
+        PartitionUpdate.SimpleBuilder builder = PartitionUpdate.simpleBuilder(SystemKeyspace.LegacyBatchlog, batch.id);
+        builder.row()
+               .timestamp(batch.creationTime)
                .add("written_at", new Date(batch.creationTime / 1000))
                .add("data", getSerializedMutations(version, batch.decodedMutations))
-               .add("version", version)
-               .build();
+               .add("version", version);
+        return builder.buildAsMutation();
     }
 
     @SuppressWarnings("deprecation")

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/Mutation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Mutation.java b/src/java/org/apache/cassandra/db/Mutation.java
index 61e5ee9..b8639a7 100644
--- a/src/java/org/apache/cassandra/db/Mutation.java
+++ b/src/java/org/apache/cassandra/db/Mutation.java
@@ -302,6 +302,72 @@ public class Mutation implements IMutation
         return buff.append("])").toString();
     }
 
+    /**
+     * Creates a new simple mutuation builder.
+     *
+     * @param keyspaceName the name of the keyspace this is a mutation for.
+     * @param partitionKey the key of partition this if a mutation for.
+     * @return a newly created builder.
+     */
+    public static SimpleBuilder simpleBuilder(String keyspaceName, DecoratedKey partitionKey)
+    {
+        return new SimpleBuilders.MutationBuilder(keyspaceName, partitionKey);
+    }
+
+    /**
+     * Interface for building mutations geared towards human.
+     * <p>
+     * This should generally not be used when performance matters too much, but provides a more convenient interface to
+     * build a mutation than using the class constructor when performance is not of the utmost importance.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * Sets the timestamp to use for the following additions to this builder or any derived (update or row) builder.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions to this builder or any derived (update or row) builder.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * ttl to be used for the row {@code LivenessInfo}.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Adds an update for table identified by the provided metadata and return a builder for that partition.
+         *
+         * @param metadata the metadata of the table for which to add an update.
+         * @return a builder for the partition identified by {@code metadata} (and the partition key for which this is a
+         * mutation of).
+         */
+        public PartitionUpdate.SimpleBuilder update(CFMetaData metadata);
+
+        /**
+         * Adds an update for table identified by the provided name and return a builder for that partition.
+         *
+         * @param tableName the name of the table for which to add an update.
+         * @return a builder for the partition identified by {@code metadata} (and the partition key for which this is a
+         * mutation of).
+         */
+        public PartitionUpdate.SimpleBuilder update(String tableName);
+
+        /**
+         * Build the mutation represented by this builder.
+         *
+         * @return the built mutation.
+         */
+        public Mutation build();
+    }
+
     public static class MutationSerializer implements IVersionedSerializer<Mutation>
     {
         public void serialize(Mutation mutation, DataOutputPlus out, int version) throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/RowUpdateBuilder.java b/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
deleted file mode 100644
index b414eba..0000000
--- a/src/java/org/apache/cassandra/db/RowUpdateBuilder.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * 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.cassandra.db;
-
-import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.cassandra.cql3.ColumnIdentifier;
-import org.apache.cassandra.config.CFMetaData;
-import org.apache.cassandra.config.ColumnDefinition;
-import org.apache.cassandra.db.marshal.SetType;
-import org.apache.cassandra.db.rows.*;
-import org.apache.cassandra.db.context.CounterContext;
-import org.apache.cassandra.db.partitions.*;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.ListType;
-import org.apache.cassandra.db.marshal.MapType;
-import org.apache.cassandra.utils.*;
-
-/**
- * Convenience object to create single row updates.
- *
- * This is meant for system table update, when performance is not of the utmost importance.
- */
-public class RowUpdateBuilder
-{
-    private final PartitionUpdate update;
-
-    private final long timestamp;
-    private final int ttl;
-    private final int localDeletionTime;
-
-    private final DeletionTime deletionTime;
-
-    private final Mutation mutation;
-
-    private Row.Builder regularBuilder;
-    private Row.Builder staticBuilder;
-
-    private boolean useRowMarker = true;
-
-    private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, int localDeletionTime, Mutation mutation)
-    {
-        this.update = update;
-
-        this.timestamp = timestamp;
-        this.ttl = ttl;
-        this.localDeletionTime = localDeletionTime;
-        this.deletionTime = new DeletionTime(timestamp, localDeletionTime);
-
-        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
-        // underneath (this class if for convenience, not performance)
-        this.mutation = mutation == null ? new Mutation(update.metadata().ksName, update.partitionKey()).add(update) : mutation;
-    }
-
-    private RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl, Mutation mutation)
-    {
-        this(update, timestamp, ttl, FBUtilities.nowInSeconds(), mutation);
-    }
-
-    private void startRow(Clustering clustering)
-    {
-        assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        assert regularBuilder == null : "Cannot add the clustering twice to the same row";
-
-        regularBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
-        regularBuilder.newRow(clustering);
-
-        // If a CQL table, add the "row marker"
-        if (update.metadata().isCQLTable() && useRowMarker)
-            regularBuilder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, localDeletionTime));
-    }
-
-    private Row.Builder builder()
-    {
-        assert staticBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        if (regularBuilder == null)
-        {
-            // we don't force people to call clustering() if the table has no clustering, so call it ourselves
-            assert update.metadata().comparator.size() == 0 : "Missing call to clustering()";
-            startRow(Clustering.EMPTY);
-        }
-        return regularBuilder;
-    }
-
-    private Row.Builder staticBuilder()
-    {
-        assert regularBuilder == null : "Cannot update both static and non-static columns with the same RowUpdateBuilder object";
-        if (staticBuilder == null)
-        {
-            staticBuilder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
-            staticBuilder.newRow(Clustering.STATIC_CLUSTERING);
-        }
-        return staticBuilder;
-    }
-
-    private Row.Builder builder(ColumnDefinition c)
-    {
-        return c.isStatic() ? staticBuilder() : builder();
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Object partitionKey)
-    {
-        this(metadata, FBUtilities.nowInSeconds(), timestamp, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, Object partitionKey)
-    {
-        this(metadata, localDeletionTime, timestamp, metadata.params.defaultTimeToLive, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Object partitionKey)
-    {
-        this(metadata, FBUtilities.nowInSeconds(), timestamp, ttl, partitionKey);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, int localDeletionTime, long timestamp, int ttl, Object partitionKey)
-    {
-        this(new PartitionUpdate(metadata, makeKey(metadata, partitionKey), metadata.partitionColumns(), 1), timestamp, ttl, localDeletionTime, null);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, Mutation mutation)
-    {
-        this(metadata, timestamp, LivenessInfo.NO_TTL, mutation);
-    }
-
-    public RowUpdateBuilder(CFMetaData metadata, long timestamp, int ttl, Mutation mutation)
-    {
-        this(getOrAdd(metadata, mutation), timestamp, ttl, mutation);
-    }
-
-    public RowUpdateBuilder(PartitionUpdate update, long timestamp, int ttl)
-    {
-        this(update, timestamp, ttl, null);
-    }
-
-    // This must be called before any addition or deletion if used.
-    public RowUpdateBuilder noRowMarker()
-    {
-        this.useRowMarker = false;
-        return this;
-    }
-
-    public RowUpdateBuilder clustering(Object... clusteringValues)
-    {
-        assert clusteringValues.length == update.metadata().comparator.size()
-             : "Invalid clustering values length. Expected: " + update.metadata().comparator.size() + " got: " + clusteringValues.length;
-
-        startRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
-        return this;
-    }
-
-    public Mutation build()
-    {
-        Row.Builder builder = regularBuilder == null ? staticBuilder : regularBuilder;
-        if (builder != null)
-            update.add(builder.build());
-        return mutation;
-    }
-
-    public PartitionUpdate buildUpdate()
-    {
-        build();
-        return update;
-    }
-
-    private static void deleteRow(PartitionUpdate update, long timestamp, int localDeletionTime, Object... clusteringValues)
-    {
-        assert clusteringValues.length == update.metadata().comparator.size() || (clusteringValues.length == 0 && !update.columns().statics.isEmpty());
-
-        boolean isStatic = clusteringValues.length != update.metadata().comparator.size();
-        Row.Builder builder = BTreeRow.sortedBuilder();
-
-        if (isStatic)
-            builder.newRow(Clustering.STATIC_CLUSTERING);
-        else
-            builder.newRow(clusteringValues.length == 0 ? Clustering.EMPTY : update.metadata().comparator.make(clusteringValues));
-        builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, localDeletionTime)));
-
-        update.add(builder.build());
-    }
-
-    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Mutation mutation, Object... clusteringValues)
-    {
-        deleteRow(getOrAdd(metadata, mutation), timestamp, FBUtilities.nowInSeconds(), clusteringValues);
-        return mutation;
-    }
-
-    public static Mutation deleteRow(CFMetaData metadata, long timestamp, Object key, Object... clusteringValues)
-    {
-        return deleteRowAt(metadata, timestamp, FBUtilities.nowInSeconds(), key, clusteringValues);
-    }
-
-    public static Mutation deleteRowAt(CFMetaData metadata, long timestamp, int localDeletionTime, Object key, Object... clusteringValues)
-    {
-        PartitionUpdate update = new PartitionUpdate(metadata, makeKey(metadata, key), metadata.partitionColumns(), 0);
-        deleteRow(update, timestamp, localDeletionTime, clusteringValues);
-        // note that the created mutation may get further update later on, so we don't use the ctor that create a singletonMap
-        // underneath (this class if for convenience, not performance)
-        return new Mutation(update.metadata().ksName, update.partitionKey()).add(update);
-    }
-
-    private static DecoratedKey makeKey(CFMetaData metadata, Object... partitionKey)
-    {
-        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
-            return (DecoratedKey)partitionKey[0];
-
-        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
-        return metadata.decorateKey(key);
-    }
-
-    private static PartitionUpdate getOrAdd(CFMetaData metadata, Mutation mutation)
-    {
-        PartitionUpdate upd = mutation.get(metadata);
-        if (upd == null)
-        {
-            upd = new PartitionUpdate(metadata, mutation.key(), metadata.partitionColumns(), 1);
-            mutation.add(upd);
-        }
-        return upd;
-    }
-
-    public RowUpdateBuilder resetCollection(String columnName)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type.isCollection() && c.type.isMultiCell();
-        builder(c).addComplexDeletion(c, new DeletionTime(timestamp - 1, localDeletionTime));
-        return this;
-    }
-
-    public RowUpdateBuilder addRangeTombstone(RangeTombstone rt)
-    {
-        update.add(rt);
-        return this;
-    }
-
-    public RowUpdateBuilder addRangeTombstone(Slice slice)
-    {
-        return addRangeTombstone(new RangeTombstone(slice, deletionTime));
-    }
-
-    public RowUpdateBuilder addRangeTombstone(Object start, Object end)
-    {
-        ClusteringComparator cmp = update.metadata().comparator;
-        Slice slice = Slice.make(cmp.make(start), cmp.make(end));
-        return addRangeTombstone(slice);
-    }
-
-    public RowUpdateBuilder add(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        return add(c, value);
-    }
-
-    private Cell makeCell(ColumnDefinition c, ByteBuffer value, CellPath path)
-    {
-        return value == null
-             ? BufferCell.tombstone(c, timestamp, localDeletionTime)
-             : (ttl == LivenessInfo.NO_TTL ? BufferCell.live(c, timestamp, value, path) : BufferCell.expiring(c, timestamp, ttl, localDeletionTime, value, path));
-    }
-
-    public RowUpdateBuilder add(ColumnDefinition columnDefinition, Object value)
-    {
-        assert columnDefinition.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + columnDefinition + " since no clustering hasn't been provided";
-        builder(columnDefinition).addCell(makeCell(columnDefinition, bb(value, columnDefinition.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder delete(String columnName)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c != null : "Cannot find column " + columnName;
-        return delete(c);
-    }
-
-    public RowUpdateBuilder delete(ColumnDefinition columnDefinition)
-    {
-        return add(columnDefinition, null);
-    }
-
-    private static ByteBuffer bb(Object value, AbstractType<?> type)
-    {
-        if (value == null)
-            return null;
-
-        if (value instanceof ByteBuffer)
-            return (ByteBuffer)value;
-
-        if (type.isCounter())
-        {
-            // See UpdateParameters.addCounter()
-            assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
-            return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
-        }
-        return ((AbstractType)type).decompose(value);
-    }
-
-    public RowUpdateBuilder map(String columnName, Map<?, ?> map)
-    {
-        resetCollection(columnName);
-        for (Map.Entry<?, ?> entry : map.entrySet())
-            addMapEntry(columnName, entry.getKey(), entry.getValue());
-        return this;
-    }
-
-    public RowUpdateBuilder set(String columnName, Set<?> set)
-    {
-        resetCollection(columnName);
-        for (Object element : set)
-            addSetEntry(columnName, element);
-        return this;
-    }
-
-    public RowUpdateBuilder frozenList(String columnName, List<?> list)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof ListType && !c.type.isMultiCell() : "Column " + c + " is not a frozen list";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(list), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder frozenSet(String columnName, Set<?> set)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof SetType && !c.type.isMultiCell() : "Column " + c + " is not a frozen set";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(set), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder frozenMap(String columnName, Map<?, ?> map)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof MapType && !c.type.isMultiCell() : "Column " + c + " is not a frozen map";
-        builder(c).addCell(makeCell(c, bb(((AbstractType)c.type).decompose(map), c.type), null));
-        return this;
-    }
-
-    public RowUpdateBuilder addMapEntry(String columnName, Object key, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || update.metadata().comparator.size() == 0 || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof MapType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen map";
-        MapType mt = (MapType)c.type;
-        builder(c).addCell(makeCell(c, bb(value, mt.getValuesType()), CellPath.create(bb(key, mt.getKeysType()))));
-        return this;
-    }
-
-    public RowUpdateBuilder addListEntry(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof ListType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen list";
-        ListType lt = (ListType)c.type;
-        builder(c).addCell(makeCell(c, bb(value, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
-        return this;
-    }
-
-    public RowUpdateBuilder addSetEntry(String columnName, Object value)
-    {
-        ColumnDefinition c = getDefinition(columnName);
-        assert c.isStatic() || regularBuilder != null : "Cannot set non static column " + c + " since no clustering has been provided";
-        assert c.type instanceof SetType && c.type.isMultiCell() : "Column " + c + " is not a non-frozen set";
-        SetType st = (SetType)c.type;
-        builder(c).addCell(makeCell(c, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(bb(value, st.getElementsType()))));
-        return this;
-    }
-
-    private ColumnDefinition getDefinition(String name)
-    {
-        return update.metadata().getColumnDefinition(new ColumnIdentifier(name, true));
-    }
-
-    public UnfilteredRowIterator unfilteredIterator()
-    {
-        return update.unfilteredIterator();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/SimpleBuilders.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SimpleBuilders.java b/src/java/org/apache/cassandra/db/SimpleBuilders.java
new file mode 100644
index 0000000..6e65743
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/SimpleBuilders.java
@@ -0,0 +1,461 @@
+/*
+ * 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.cassandra.db;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.db.context.CounterContext;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
+import org.apache.cassandra.db.rows.BTreeRow;
+import org.apache.cassandra.db.rows.BufferCell;
+import org.apache.cassandra.db.rows.Cell;
+import org.apache.cassandra.db.rows.CellPath;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.CounterId;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.UUIDGen;
+
+public abstract class SimpleBuilders
+{
+    private SimpleBuilders()
+    {
+    }
+
+    private static DecoratedKey makePartitonKey(CFMetaData metadata, Object... partitionKey)
+    {
+        if (partitionKey.length == 1 && partitionKey[0] instanceof DecoratedKey)
+            return (DecoratedKey)partitionKey[0];
+
+        ByteBuffer key = CFMetaData.serializePartitionKey(metadata.getKeyValidatorAsClusteringComparator().make(partitionKey));
+        return metadata.decorateKey(key);
+    }
+
+    private static Clustering makeClustering(CFMetaData metadata, Object... clusteringColumns)
+    {
+        if (clusteringColumns.length == 1 && clusteringColumns[0] instanceof Clustering)
+            return (Clustering)clusteringColumns[0];
+
+        if (clusteringColumns.length == 0)
+        {
+            // If the table has clustering columns, passing no values is for updating the static values, so check we
+            // do have some static columns defined.
+            assert metadata.comparator.size() == 0 || !metadata.partitionColumns().statics.isEmpty();
+            return metadata.comparator.size() == 0 ? Clustering.EMPTY : Clustering.STATIC_CLUSTERING;
+        }
+        else
+        {
+            return metadata.comparator.make(clusteringColumns);
+        }
+    }
+
+    private static class AbstractBuilder<T>
+    {
+        protected long timestamp = FBUtilities.timestampMicros();
+        protected int ttl = 0;
+        protected int nowInSec = FBUtilities.nowInSeconds();
+
+        protected void copyParams(AbstractBuilder<?> other)
+        {
+            other.timestamp = timestamp;
+            other.ttl = ttl;
+            other.nowInSec = nowInSec;
+        }
+
+        public T timestamp(long timestamp)
+        {
+            this.timestamp = timestamp;
+            return (T)this;
+        }
+
+        public T ttl(int ttl)
+        {
+            this.ttl = ttl;
+            return (T)this;
+        }
+
+        public T nowInSec(int nowInSec)
+        {
+            this.nowInSec = nowInSec;
+            return (T)this;
+        }
+    }
+
+    public static class MutationBuilder extends AbstractBuilder<Mutation.SimpleBuilder> implements Mutation.SimpleBuilder
+    {
+        private final String keyspaceName;
+        private final DecoratedKey key;
+
+        private final Map<UUID, PartitionUpdateBuilder> updateBuilders = new HashMap<>();
+
+        public MutationBuilder(String keyspaceName, DecoratedKey key)
+        {
+            this.keyspaceName = keyspaceName;
+            this.key = key;
+        }
+
+        public PartitionUpdate.SimpleBuilder update(CFMetaData metadata)
+        {
+            assert metadata.ksName.equals(keyspaceName);
+
+            PartitionUpdateBuilder builder = updateBuilders.get(metadata.cfId);
+            if (builder == null)
+            {
+                builder = new PartitionUpdateBuilder(metadata, key);
+                updateBuilders.put(metadata.cfId, builder);
+            }
+
+            copyParams(builder);
+
+            return builder;
+        }
+
+        public PartitionUpdate.SimpleBuilder update(String tableName)
+        {
+            CFMetaData metadata = Schema.instance.getCFMetaData(keyspaceName, tableName);
+            assert metadata != null : "Unknown table " + tableName + " in keyspace " + keyspaceName;
+            return update(metadata);
+        }
+
+        public Mutation build()
+        {
+            assert !updateBuilders.isEmpty() : "Cannot create empty mutation";
+
+            if (updateBuilders.size() == 1)
+                return new Mutation(updateBuilders.values().iterator().next().build());
+
+            Mutation mutation = new Mutation(keyspaceName, key);
+            for (PartitionUpdateBuilder builder : updateBuilders.values())
+                mutation.add(builder.build());
+            return mutation;
+        }
+    }
+
+    public static class PartitionUpdateBuilder extends AbstractBuilder<PartitionUpdate.SimpleBuilder> implements PartitionUpdate.SimpleBuilder
+    {
+        private final CFMetaData metadata;
+        private final DecoratedKey key;
+        private final Map<Clustering, RowBuilder> rowBuilders = new HashMap<>();
+        private List<RTBuilder> rangeBuilders = null; // We use that rarely, so create lazily
+
+        private DeletionTime partitionDeletion = DeletionTime.LIVE;
+
+        public PartitionUpdateBuilder(CFMetaData metadata, Object... partitionKeyValues)
+        {
+            this.metadata = metadata;
+            this.key = makePartitonKey(metadata, partitionKeyValues);
+        }
+
+        public CFMetaData metadata()
+        {
+            return metadata;
+        }
+
+        public Row.SimpleBuilder row(Object... clusteringValues)
+        {
+            Clustering clustering = makeClustering(metadata, clusteringValues);
+            RowBuilder builder = rowBuilders.get(clustering);
+            if (builder == null)
+            {
+                builder = new RowBuilder(metadata, clustering);
+                rowBuilders.put(clustering, builder);
+            }
+
+            copyParams(builder);
+
+            return builder;
+        }
+
+        public PartitionUpdate.SimpleBuilder delete()
+        {
+            this.partitionDeletion = new DeletionTime(timestamp, nowInSec);
+            return this;
+        }
+
+        public RangeTombstoneBuilder addRangeTombstone()
+        {
+            if (rangeBuilders == null)
+                rangeBuilders = new ArrayList<>();
+
+            RTBuilder builder = new RTBuilder(metadata.comparator, new DeletionTime(timestamp, nowInSec));
+            rangeBuilders.add(builder);
+            return builder;
+        }
+
+        public PartitionUpdate build()
+        {
+            // Collect all updated columns
+            PartitionColumns.Builder columns = PartitionColumns.builder();
+            for (RowBuilder builder : rowBuilders.values())
+                columns.addAll(builder.columns());
+
+            // Note that rowBuilders.size() could include the static column so could be 1 off the really need capacity
+            // of the final PartitionUpdate, but as that's just a sizing hint, we'll live.
+            PartitionUpdate update = new PartitionUpdate(metadata, key, columns.build(), rowBuilders.size());
+
+            update.addPartitionDeletion(partitionDeletion);
+            if (rangeBuilders != null)
+            {
+                for (RTBuilder builder : rangeBuilders)
+                    update.add(builder.build());
+            }
+
+            for (RowBuilder builder : rowBuilders.values())
+                update.add(builder.build());
+
+            return update;
+        }
+
+        public Mutation buildAsMutation()
+        {
+            return new Mutation(build());
+        }
+
+        private static class RTBuilder implements RangeTombstoneBuilder
+        {
+            private final ClusteringComparator comparator;
+            private final DeletionTime deletionTime;
+
+            private Object[] start;
+            private Object[] end;
+
+            private boolean startInclusive = true;
+            private boolean endInclusive = true;
+
+            private RTBuilder(ClusteringComparator comparator, DeletionTime deletionTime)
+            {
+                this.comparator = comparator;
+                this.deletionTime = deletionTime;
+            }
+
+            public RangeTombstoneBuilder start(Object... values)
+            {
+                this.start = values;
+                return this;
+            }
+
+            public RangeTombstoneBuilder end(Object... values)
+            {
+                this.end = values;
+                return this;
+            }
+
+            public RangeTombstoneBuilder inclStart()
+            {
+                this.startInclusive = true;
+                return this;
+            }
+
+            public RangeTombstoneBuilder exclStart()
+            {
+                this.startInclusive = false;
+                return this;
+            }
+
+            public RangeTombstoneBuilder inclEnd()
+            {
+                this.endInclusive = true;
+                return this;
+            }
+
+            public RangeTombstoneBuilder exclEnd()
+            {
+                this.endInclusive = false;
+                return this;
+            }
+
+            private RangeTombstone build()
+            {
+                ClusteringBound startBound = ClusteringBound.create(comparator, true, startInclusive, start);
+                ClusteringBound endBound = ClusteringBound.create(comparator, false, endInclusive, end);
+                return new RangeTombstone(Slice.make(startBound, endBound), deletionTime);
+            }
+        }
+    }
+
+    public static class RowBuilder extends AbstractBuilder<Row.SimpleBuilder> implements Row.SimpleBuilder
+    {
+        private final CFMetaData metadata;
+
+        private final Set<ColumnDefinition> columns = new HashSet<>();
+        private final Row.Builder builder;
+
+        private boolean initiated;
+        private boolean noPrimaryKeyLivenessInfo;
+
+        public RowBuilder(CFMetaData metadata, Object... clusteringColumns)
+        {
+            this.metadata = metadata;
+            this.builder = BTreeRow.unsortedBuilder(FBUtilities.nowInSeconds());
+
+            this.builder.newRow(makeClustering(metadata, clusteringColumns));
+        }
+
+        Set<ColumnDefinition> columns()
+        {
+            return columns;
+        }
+
+        private void maybeInit()
+        {
+            // We're working around the fact that Row.Builder requires that addPrimaryKeyLivenessInfo() and
+            // addRowDeletion() are called before any cell addition (which is done so the builder can more easily skip
+            // shadowed cells).
+            if (initiated)
+                return;
+
+            // If a CQL table, add the "row marker"
+            if (metadata.isCQLTable() && !noPrimaryKeyLivenessInfo)
+                builder.addPrimaryKeyLivenessInfo(LivenessInfo.create(timestamp, ttl, nowInSec));
+
+            initiated = true;
+        }
+
+        public Row.SimpleBuilder add(String columnName, Object value)
+        {
+            return add(columnName, value, true);
+        }
+
+        public Row.SimpleBuilder appendAll(String columnName, Object value)
+        {
+            return add(columnName, value, false);
+        }
+
+        private Row.SimpleBuilder add(String columnName, Object value, boolean overwriteForCollection)
+        {
+            maybeInit();
+            ColumnDefinition column = getColumn(columnName);
+
+            if (!overwriteForCollection && !(column.type.isMultiCell() && column.type.isCollection()))
+                throw new IllegalArgumentException("appendAll() can only be called on non-frozen colletions");
+
+            columns.add(column);
+
+            if (!column.type.isMultiCell())
+            {
+                builder.addCell(cell(column, toByteBuffer(value, column.type), null));
+                return this;
+            }
+
+            assert column.type instanceof CollectionType : "Collection are the only multi-cell types supported so far";
+
+            if (value == null)
+            {
+                builder.addComplexDeletion(column, new DeletionTime(timestamp, nowInSec));
+                return this;
+            }
+
+            // Erase previous entry if any.
+            if (overwriteForCollection)
+                builder.addComplexDeletion(column, new DeletionTime(timestamp - 1, nowInSec));
+            switch (((CollectionType)column.type).kind)
+            {
+                case LIST:
+                    ListType lt = (ListType)column.type;
+                    assert value instanceof List;
+                    for (Object elt : (List)value)
+                        builder.addCell(cell(column, toByteBuffer(elt, lt.getElementsType()), CellPath.create(ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes()))));
+                    break;
+                case SET:
+                    SetType st = (SetType)column.type;
+                    assert value instanceof Set;
+                    for (Object elt : (Set)value)
+                        builder.addCell(cell(column, ByteBufferUtil.EMPTY_BYTE_BUFFER, CellPath.create(toByteBuffer(elt, st.getElementsType()))));
+                    break;
+                case MAP:
+                    MapType mt = (MapType)column.type;
+                    assert value instanceof Map;
+                    for (Map.Entry entry : ((Map<?, ?>)value).entrySet())
+                        builder.addCell(cell(column,
+                                             toByteBuffer(entry.getValue(), mt.getValuesType()),
+                                             CellPath.create(toByteBuffer(entry.getKey(), mt.getKeysType()))));
+                    break;
+                default:
+                    throw new AssertionError();
+            }
+            return this;
+        }
+
+        public Row.SimpleBuilder delete()
+        {
+            assert !initiated : "If called, delete() should be called before any other column value addition";
+            builder.addRowDeletion(Row.Deletion.regular(new DeletionTime(timestamp, nowInSec)));
+            return this;
+        }
+
+        public Row.SimpleBuilder delete(String columnName)
+        {
+            return add(columnName, null);
+        }
+
+        public Row.SimpleBuilder noPrimaryKeyLivenessInfo()
+        {
+            this.noPrimaryKeyLivenessInfo = true;
+            return this;
+        }
+
+        public Row build()
+        {
+            maybeInit();
+            return builder.build();
+        }
+
+        private ColumnDefinition getColumn(String columnName)
+        {
+            ColumnDefinition column = metadata.getColumnDefinition(new ColumnIdentifier(columnName, true));
+            assert column != null : "Cannot find column " + columnName;
+            assert !column.isPrimaryKeyColumn();
+            assert !column.isStatic() || builder.clustering() == Clustering.STATIC_CLUSTERING : "Cannot add non-static column to static-row";
+            return column;
+        }
+
+        private Cell cell(ColumnDefinition column, ByteBuffer value, CellPath path)
+        {
+            if (value == null)
+                return BufferCell.tombstone(column, timestamp, nowInSec, path);
+
+            return ttl == LivenessInfo.NO_TTL
+                 ? BufferCell.live(column, timestamp, value, path)
+                 : BufferCell.expiring(column, timestamp, ttl, nowInSec, value, path);
+        }
+
+        private ByteBuffer toByteBuffer(Object value, AbstractType<?> type)
+        {
+            if (value == null)
+                return null;
+
+            if (value instanceof ByteBuffer)
+                return (ByteBuffer)value;
+
+            if (type.isCounter())
+            {
+                // See UpdateParameters.addCounter()
+                assert value instanceof Long : "Attempted to adjust Counter cell with non-long value.";
+                return CounterContext.instance().createGlobal(CounterId.getLocalId(), 1, (Long)value);
+            }
+
+            return ((AbstractType)type).decompose(value);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/SystemKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SystemKeyspace.java b/src/java/org/apache/cassandra/db/SystemKeyspace.java
index 584279d..36629a1 100644
--- a/src/java/org/apache/cassandra/db/SystemKeyspace.java
+++ b/src/java/org/apache/cassandra/db/SystemKeyspace.java
@@ -44,6 +44,7 @@ import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.commitlog.CommitLogPosition;
 import org.apache.cassandra.db.compaction.CompactionHistoryTabularData;
 import org.apache.cassandra.db.marshal.*;
+import org.apache.cassandra.db.rows.Rows;
 import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.dht.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
@@ -1233,11 +1234,11 @@ public final class SystemKeyspace
         {
             Range<Token> range = entry.getKey();
             Pair<Long, Long> values = entry.getValue();
-            new RowUpdateBuilder(SizeEstimates, timestamp, mutation)
-                .clustering(table, range.left.toString(), range.right.toString())
-                .add("partitions_count", values.left)
-                .add("mean_partition_size", values.right)
-                .build();
+            update.add(Rows.simpleBuilder(SizeEstimates, table, range.left.toString(), range.right.toString())
+                           .timestamp(timestamp)
+                           .add("partitions_count", values.left)
+                           .add("mean_partition_size", values.right)
+                           .build());
         }
 
         mutation.apply();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
index 1c05f3c..954168d 100644
--- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
+++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java
@@ -169,7 +169,7 @@ public abstract class AbstractBTreePartition implements Partition, Iterable<Row>
 
     public UnfilteredRowIterator unfilteredIterator()
     {
-        return unfilteredIterator(ColumnFilter.all(metadata()), Slices.ALL, false);
+        return unfilteredIterator(ColumnFilter.selection(columns()), Slices.ALL, false);
     }
 
     public UnfilteredRowIterator unfilteredIterator(ColumnFilter selection, Slices slices, boolean reversed)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java b/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
index d18392c..7796fd9 100644
--- a/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
+++ b/src/java/org/apache/cassandra/db/partitions/PartitionUpdate.java
@@ -614,6 +614,160 @@ public class PartitionUpdate extends AbstractBTreePartition
         return sb.toString();
     }
 
+    /**
+     * Creates a new simple partition update builder.
+     *
+     * @param metadata the metadata for the table this is a partition of.
+     * @param partitionKeyValues the values for partition key columns identifying this partition. The values for each
+     * partition key column can be passed either directly as {@code ByteBuffer} or using a "native" value (int for
+     * Int32Type, string for UTF8Type, ...). It is also allowed to pass a single {@code DecoratedKey} value directly.
+     * @return a newly created builder.
+     */
+    public static SimpleBuilder simpleBuilder(CFMetaData metadata, Object... partitionKeyValues)
+    {
+        return new SimpleBuilders.PartitionUpdateBuilder(metadata, partitionKeyValues);
+    }
+
+    /**
+     * Interface for building partition updates geared towards human.
+     * <p>
+     * This should generally not be used when performance matters too much, but provides a more convenient interface to
+     * build an update than using the class constructor when performance is not of the utmost importance.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * The metadata of the table this is a builder on.
+         */
+        public CFMetaData metadata();
+
+        /**
+         * Sets the timestamp to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Sets the current time to use for the following additions to this builder or any derived (row) builder.
+         *
+         * @param nowInSec the current time to use for following additions. If the current time hasn't been set, the current
+         * time in seconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder nowInSec(int nowInSec);
+
+        /**
+         * Adds the row identifier by the provided clustering and return a builder for that row.
+         *
+         * @param clusteringValues the value for the clustering columns of the row to add to this build. There may be no
+         * values if either the table has no clustering column, or if you want to edit the static row. Note that as a
+         * shortcut it is also allowed to pass a {@code Clustering} object directly, in which case that should be the
+         * only argument.
+         * @return a builder for the row identified by {@code clusteringValues}.
+         */
+        public Row.SimpleBuilder row(Object... clusteringValues);
+
+        /**
+         * Deletes the partition identified by this builder (using a partition level deletion).
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder delete();
+
+        /**
+         * Adds a new range tombstone to this update, returning a builder for that range.
+         *
+         * @return the range tombstone builder for the newly added range.
+         */
+        public RangeTombstoneBuilder addRangeTombstone();
+
+        /**
+         * Build the update represented by this builder.
+         *
+         * @return the built update.
+         */
+        public PartitionUpdate build();
+
+        /**
+         * As shortcut for {@code new Mutation(build())}.
+         *
+         * @return the built update, wrapped in a {@code Mutation}.
+         */
+        public Mutation buildAsMutation();
+
+        /**
+         * Interface to build range tombstone.
+         *
+         * By default, if no other methods are called, the represented range is inclusive of both start and end and
+         * includes everything (its start is {@code BOTTOM} and it's end is {@code TOP}).
+         */
+        public interface RangeTombstoneBuilder
+        {
+            /**
+             * Sets the start for the built range using the provided values.
+             *
+             * @param values the value for the start of the range. They act like the {@code clusteringValues} argument
+             * of the {@link PartitionUpdate.SimpleBuilder#row()} method, except that it doesn't have to be a full
+             * clustering, it can only be a prefix.
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder start(Object... values);
+
+            /**
+             * Sets the end for the built range using the provided values.
+             *
+             * @param values the value for the end of the range. They act like the {@code clusteringValues} argument
+             * of the {@link PartitionUpdate.SimpleBuilder#row()} method, except that it doesn't have to be a full
+             * clustering, it can only be a prefix.
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder end(Object... values);
+
+            /**
+             * Sets the start of this range as inclusive.
+             * <p>
+             * This is the default and don't need to be called, but can for explicitness.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder inclStart();
+
+            /**
+             * Sets the start of this range as exclusive.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder exclStart();
+
+            /**
+             * Sets the end of this range as inclusive.
+             * <p>
+             * This is the default and don't need to be called, but can for explicitness.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder inclEnd();
+
+            /**
+             * Sets the end of this range as exclusive.
+             *
+             * @return this builder.
+             */
+            public RangeTombstoneBuilder exclEnd();
+        }
+    }
+
     public static class PartitionUpdateSerializer
     {
         public void serialize(PartitionUpdate update, DataOutputPlus out, int version) throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Row.java b/src/java/org/apache/cassandra/db/rows/Row.java
index 4fc3e22..7e6d141 100644
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@ -468,6 +468,105 @@ public interface Row extends Unfiltered, Collection<ColumnData>
     }
 
     /**
+     * Row builder interface geared towards human.
+     * <p>
+     * Where the {@link Builder} deals with building rows efficiently from internal objects ({@code Cell}, {@code
+     * LivenessInfo}, ...), the {@code SimpleBuilder} is geared towards building rows from string column name and
+     * 'native' values (string for text, ints for numbers, et...). In particular, it is meant to be convenient, not
+     * efficient, and should be used only in place where performance is not of the utmost importance (it is used to
+     * build schema mutation for instance).
+     * <p>
+     * Also note that contrarily to {@link Builder}, the {@code SimpleBuilder} API has no {@code newRow()} method: it is
+     * expected that the clustering of the row built is provided by the constructor of the builder.
+     */
+    public interface SimpleBuilder
+    {
+        /**
+         * Sets the timestamp to use for the following additions.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * timestamp to be used for the row {@code LivenessInfo}.
+         *
+         * @param timestamp the timestamp to use for following additions. If that timestamp hasn't been set, the current
+         * time in microseconds will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder timestamp(long timestamp);
+
+        /**
+         * Sets the ttl to use for the following additions.
+         * <p>
+         * Note that the for non-compact tables, this method must be called before any column addition for this
+         * ttl to be used for the row {@code LivenessInfo}.
+         *
+         * @param ttl the ttl to use for following additions. If that ttl hasn't been set, no ttl will be used.
+         * @return this builder.
+         */
+        public SimpleBuilder ttl(int ttl);
+
+        /**
+         * Adds a value to a given column.
+         *
+         * @param columnName the name of the column for which to add a new value.
+         * @param value the value to add, which must be of the proper type for {@code columnName}. This can be {@code
+         * null} in which case the this is equivalent to {@code delete(columnName)}.
+         * @return this builder.
+         */
+        public SimpleBuilder add(String columnName, Object value);
+
+        /**
+         * Appends new values to a given non-frozen collection column.
+         * <p>
+         * This method is similar to {@code add()} but the collection elements added through this method are "appended"
+         * to any pre-exising elements. In other words, this is like {@code add()} except that it doesn't delete the
+         * previous value of the collection. This can only be called on non-frozen collection columns.
+         * <p>
+         * Note that this method can be used in replacement of {@code add()} if you know that there can't be any
+         * pre-existing value for that column, in which case this is slightly less expensive as it avoid the collection
+         * tombstone inherent to {@code add()}.
+         *
+         * @param columnName the name of the column for which to add a new value, which must be a non-frozen collection.
+         * @param value the value to add, which must be of the proper type for {@code columnName} (in other words, it
+         * <b>must</b> be a collection).
+         * @return this builder.
+         *
+         * @throws IllegalArgumentException if columnName is not a non-frozen collection column.
+         */
+        public SimpleBuilder appendAll(String columnName, Object value);
+
+        /**
+         * Deletes the whole row.
+         * <p>
+         * If called, this is generally the only method called on the builder (outside of {@code timestamp()}.
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder delete();
+
+        /**
+         * Removes the value for a given column (creating a tombstone).
+         *
+         * @param columnName the name of the column to delete.
+         * @return this builder.
+         */
+        public SimpleBuilder delete(String columnName);
+
+        /**
+         * Don't include any primary key {@code LivenessInfo} in the built row.
+         *
+         * @return this builder.
+         */
+        public SimpleBuilder noPrimaryKeyLivenessInfo();
+
+        /**
+         * Returns the built row.
+         *
+         * @return the built row.
+         */
+        public Row build();
+    }
+
+    /**
      * Utility class to help merging rows from multiple inputs (UnfilteredRowIterators).
      */
     public static class Merger

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Rows.java b/src/java/org/apache/cassandra/db/rows/Rows.java
index e325091..976d37e 100644
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@ -22,6 +22,7 @@ import java.util.*;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.PeekingIterator;
 
+import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.partitions.PartitionStatisticsCollector;
@@ -59,6 +60,21 @@ public abstract class Rows
     }
 
     /**
+     * Creates a new simple row builder.
+     *
+     * @param metadata the metadata of the table this is a row of.
+     * @param clusteringValues the value for the clustering columns of the row to add to this build. There may be no
+     * values if either the table has no clustering column, or if you want to edit the static row. Note that as a
+     * shortcut it is also allowed to pass a {@code Clustering} object directly, in which case that should be the
+     * only argument.
+     * @return a newly created builder.
+     */
+    public static Row.SimpleBuilder simpleBuilder(CFMetaData metadata, Object... clusteringValues)
+    {
+        return new SimpleBuilders.RowBuilder(metadata, clusteringValues);
+    }
+
+    /**
      * Collect statistics on a given row.
      *
      * @param row the row for which to collect stats.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/db/transform/BaseRows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/transform/BaseRows.java b/src/java/org/apache/cassandra/db/transform/BaseRows.java
index fb3b9f9..ce4e458 100644
--- a/src/java/org/apache/cassandra/db/transform/BaseRows.java
+++ b/src/java/org/apache/cassandra/db/transform/BaseRows.java
@@ -105,7 +105,8 @@ implements BaseRowIterator<R>
         super.add(transformation);
 
         // transform any existing data
-        staticRow = transformation.applyToStatic(staticRow);
+        if (staticRow != null)
+            staticRow = transformation.applyToStatic(staticRow);
         next = applyOne(next, transformation);
         partitionKey = transformation.applyToPartitionKey(partitionKey);
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/26838063/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
index 93591f0..7cc822f 100644
--- a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
+++ b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java
@@ -148,20 +148,20 @@ public final class LegacySchemaMigrator
     {
         logger.info("Migrating keyspace {}", keyspace);
 
-        Mutation mutation = SchemaKeyspace.makeCreateKeyspaceMutation(keyspace.name, keyspace.params, keyspace.timestamp);
+        Mutation.SimpleBuilder builder = SchemaKeyspace.makeCreateKeyspaceMutation(keyspace.name, keyspace.params, keyspace.timestamp);
         for (Table table : keyspace.tables)
-            SchemaKeyspace.addTableToSchemaMutation(table.metadata, table.timestamp, true, mutation);
+            SchemaKeyspace.addTableToSchemaMutation(table.metadata, true, builder.timestamp(table.timestamp));
 
         for (Type type : keyspace.types)
-            SchemaKeyspace.addTypeToSchemaMutation(type.metadata, type.timestamp, mutation);
+            SchemaKeyspace.addTypeToSchemaMutation(type.metadata, builder.timestamp(type.timestamp));
 
         for (Function function : keyspace.functions)
-            SchemaKeyspace.addFunctionToSchemaMutation(function.metadata, function.timestamp, mutation);
+            SchemaKeyspace.addFunctionToSchemaMutation(function.metadata, builder.timestamp(function.timestamp));
 
         for (Aggregate aggregate : keyspace.aggregates)
-            SchemaKeyspace.addAggregateToSchemaMutation(aggregate.metadata, aggregate.timestamp, mutation);
+            SchemaKeyspace.addAggregateToSchemaMutation(aggregate.metadata, builder.timestamp(aggregate.timestamp));
 
-        mutation.apply();
+        builder.build().apply();
     }
 
     /*