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/06/30 09:30:38 UTC

[1/6] cassandra git commit: Avoid deserialization error after altering column type

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.0 362e13206 -> e4c344c58
  refs/heads/cassandra-3.9 c7547e0de -> 1f014b2ca
  refs/heads/trunk e42352763 -> 9242c85cf


Avoid deserialization error after altering column type

This makes sure the column used when serializing intra-node messages is
"current". Previously, we would use the type used during deserialization
which could not be "current" due to an ALTER TYPE.

patch by slebresne; reviewed by thobbs for CASSANDRA-11820


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

Branch: refs/heads/cassandra-3.0
Commit: e4c344c58f8ca8f69224855080de4ec266fb671e
Parents: 362e132
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Mon Jun 27 14:17:27 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Jun 30 11:14:01 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/db/rows/BufferCell.java    | 16 +++----
 src/java/org/apache/cassandra/db/rows/Cell.java |  4 +-
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 98 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ae37d2c..573f704 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
 Merged from 2.2:
  * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/BufferCell.java b/src/java/org/apache/cassandra/db/rows/BufferCell.java
index 22b629a..db0ded5 100644
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@ -228,7 +228,7 @@ public class BufferCell extends AbstractCell
         private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
         private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 
-        public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
+        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
         {
             assert cell != null;
             boolean hasValue = cell.value().hasRemaining();
@@ -260,11 +260,11 @@ public class BufferCell extends AbstractCell
             if (isExpiring && !useRowTTL)
                 header.writeTTL(cell.ttl(), out);
 
-            if (cell.column().isComplex())
-                cell.column().cellPathSerializer().serialize(cell.path(), out);
+            if (column.isComplex())
+                column.cellPathSerializer().serialize(cell.path(), out);
 
             if (hasValue)
-                header.getType(cell.column()).writeValue(cell.value(), out);
+                header.getType(column).writeValue(cell.value(), out);
         }
 
         public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
@@ -308,7 +308,7 @@ public class BufferCell extends AbstractCell
             return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
         }
 
-        public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header)
+        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
         {
             long size = 1; // flags
             boolean hasValue = cell.value().hasRemaining();
@@ -325,11 +325,11 @@ public class BufferCell extends AbstractCell
             if (isExpiring && !useRowTTL)
                 size += header.ttlSerializedSize(cell.ttl());
 
-            if (cell.column().isComplex())
-                size += cell.column().cellPathSerializer().serializedSize(cell.path());
+            if (column.isComplex())
+                size += column.cellPathSerializer().serializedSize(cell.path());
 
             if (hasValue)
-                size += header.getType(cell.column()).writtenLength(cell.value());
+                size += header.getType(column).writtenLength(cell.value());
 
             return size;
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Cell.java b/src/java/org/apache/cassandra/db/rows/Cell.java
index b10ce06..d10cc74 100644
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@ -145,11 +145,11 @@ public abstract class Cell extends ColumnData
 
     public interface Serializer
     {
-        public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
+        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
 
         public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException;
 
-        public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header);
+        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header);
 
         // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
         public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
index e4202c9..dc6f187 100644
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
@@ -25,6 +25,7 @@ import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.io.util.DataInputPlus;
 import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.utils.SearchIterator;
 
 /**
  * Serialize/deserialize a single Unfiltered (both on-wire and on-disk).
@@ -177,16 +178,25 @@ public class UnfilteredSerializer
         if (!hasAllColumns)
             Columns.serializer.serializeSubset(Collections2.transform(row, ColumnData::column), headerColumns, out);
 
+        SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
         for (ColumnData data : row)
         {
+            // We can obtain the column for data directly from data.column(). However, if the cell/complex data
+            // originates from a sstable, the column we'll get will have the type used when the sstable was serialized,
+            // and if that type have been recently altered, that may not be the type we want to serialize the column
+            // with. So we use the ColumnDefinition from the "header" which is "current". Also see #11810 for what
+            // happens if we don't do that.
+            ColumnDefinition column = si.next(data.column());
+            assert column != null;
+
             if (data.column.isSimple())
-                Cell.serializer.serialize((Cell) data, out, pkLiveness, header);
+                Cell.serializer.serialize((Cell) data, column, out, pkLiveness, header);
             else
-                writeComplexColumn((ComplexColumnData) data, hasComplexDeletion, pkLiveness, header, out);
+                writeComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header, out);
         }
     }
 
-    private void writeComplexColumn(ComplexColumnData data, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header, DataOutputPlus out)
+    private void writeComplexColumn(ComplexColumnData data, ColumnDefinition column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header, DataOutputPlus out)
     throws IOException
     {
         if (hasComplexDeletion)
@@ -194,7 +204,7 @@ public class UnfilteredSerializer
 
         out.writeUnsignedVInt(data.cellsCount());
         for (Cell cell : data)
-            Cell.serializer.serialize(cell, out, rowLiveness, header);
+            Cell.serializer.serialize(cell, column, out, rowLiveness, header);
     }
 
     private void serialize(RangeTombstoneMarker marker, SerializationHeader header, DataOutputPlus out, long previousUnfilteredSize, int version)
@@ -274,18 +284,22 @@ public class UnfilteredSerializer
         if (!hasAllColumns)
             size += Columns.serializer.serializedSubsetSize(Collections2.transform(row, ColumnData::column), header.columns(isStatic));
 
+        SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
         for (ColumnData data : row)
         {
+            ColumnDefinition column = si.next(data.column());
+            assert column != null;
+
             if (data.column.isSimple())
-                size += Cell.serializer.serializedSize((Cell) data, pkLiveness, header);
+                size += Cell.serializer.serializedSize((Cell) data, column, pkLiveness, header);
             else
-                size += sizeOfComplexColumn((ComplexColumnData) data, hasComplexDeletion, pkLiveness, header);
+                size += sizeOfComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header);
         }
 
         return size;
     }
 
-    private long sizeOfComplexColumn(ComplexColumnData data, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header)
+    private long sizeOfComplexColumn(ComplexColumnData data, ColumnDefinition column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header)
     {
         long size = 0;
 
@@ -294,7 +308,7 @@ public class UnfilteredSerializer
 
         size += TypeSizes.sizeofUnsignedVInt(data.cellsCount());
         for (Cell cell : data)
-            size += Cell.serializer.serializedSize(cell, rowLiveness, header);
+            size += Cell.serializer.serializedSize(cell, column, rowLiveness, header);
 
         return size;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index fe03db4..a213edf 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -131,6 +131,7 @@ public abstract class CQLTester
 
     public static ResultMessage lastSchemaChangeResult;
 
+    private List<String> keyspaces = new ArrayList<>();
     private List<String> tables = new ArrayList<>();
     private List<String> types = new ArrayList<>();
     private List<String> functions = new ArrayList<>();
@@ -262,10 +263,12 @@ public abstract class CQLTester
         usePrepared = USE_PREPARED_VALUES;
         reusePrepared = REUSE_PREPARED;
 
+        final List<String> keyspacesToDrop = copy(keyspaces);
         final List<String> tablesToDrop = copy(tables);
         final List<String> typesToDrop = copy(types);
         final List<String> functionsToDrop = copy(functions);
         final List<String> aggregatesToDrop = copy(aggregates);
+        keyspaces = null;
         tables = null;
         types = null;
         functions = null;
@@ -290,6 +293,9 @@ public abstract class CQLTester
                     for (int i = typesToDrop.size() - 1; i >= 0; i--)
                         schemaChange(String.format("DROP TYPE IF EXISTS %s.%s", KEYSPACE, typesToDrop.get(i)));
 
+                    for (int i = keyspacesToDrop.size() - 1; i >= 0; i--)
+                        schemaChange(String.format("DROP KEYSPACE IF EXISTS %s", keyspacesToDrop.get(i)));
+
                     // Dropping doesn't delete the sstables. It's not a huge deal but it's cleaner to cleanup after us
                     // Thas said, we shouldn't delete blindly before the TransactionLogs.SSTableTidier for the table we drop
                     // have run or they will be unhappy. Since those taks are scheduled on StorageService.tasks and that's
@@ -501,6 +507,22 @@ public abstract class CQLTester
         schemaChange(fullQuery);
     }
 
+    protected String createKeyspace(String query)
+    {
+        String currentKeyspace = createKeyspaceName();
+        String fullQuery = String.format(query, currentKeyspace);
+        logger.info(fullQuery);
+        schemaChange(fullQuery);
+        return currentKeyspace;
+    }
+
+    protected String createKeyspaceName()
+    {
+        String currentKeyspace = "keyspace_" + seqNumber.getAndIncrement();
+        keyspaces.add(currentKeyspace);
+        return currentKeyspace;
+    }
+
     protected String createTable(String query)
     {
         String currentTable = createTableName();
@@ -519,8 +541,7 @@ public abstract class CQLTester
 
     protected void createTableMayThrow(String query) throws Throwable
     {
-        String currentTable = "table_" + seqNumber.getAndIncrement();
-        tables.add(currentTable);
+        String currentTable = createTableName();
         String fullQuery = formatQuery(query);
         logger.info(fullQuery);
         QueryProcessor.executeOnceInternal(fullQuery);
@@ -825,6 +846,16 @@ public abstract class CQLTester
      */
     public static void assertRowsIgnoringOrder(UntypedResultSet result, Object[]... rows)
     {
+        assertRowsIgnoringOrderInternal(result, false, rows);
+    }
+
+    public static void assertRowsIgnoringOrderAndExtra(UntypedResultSet result, Object[]... rows)
+    {
+        assertRowsIgnoringOrderInternal(result, true, rows);
+    }
+
+    private static void assertRowsIgnoringOrderInternal(UntypedResultSet result, boolean ignoreExtra, Object[]... rows)
+    {
         if (result == null)
         {
             if (rows.length > 0)
@@ -855,7 +886,7 @@ public abstract class CQLTester
 
         com.google.common.collect.Sets.SetView<List<ByteBuffer>> extra = com.google.common.collect.Sets.difference(actualRows, expectedRows);
         com.google.common.collect.Sets.SetView<List<ByteBuffer>> missing = com.google.common.collect.Sets.difference(expectedRows, actualRows);
-        if (!extra.isEmpty() || !missing.isEmpty())
+        if ((!ignoreExtra && !extra.isEmpty()) || !missing.isEmpty())
         {
             List<String> extraRows = makeRowStrings(extra, meta);
             List<String> missingRows = makeRowStrings(missing, meta);
@@ -876,7 +907,7 @@ public abstract class CQLTester
                 Assert.fail("Missing " + missing.size() + " row(s) in result: \n    " + missingRows.stream().collect(Collectors.joining("\n    ")));
         }
 
-        assert expectedRows.size() == actualRows.size();
+        assert ignoreExtra || expectedRows.size() == actualRows.size();
     }
 
     private static List<String> makeRowStrings(Iterable<List<ByteBuffer>> rows, List<ColumnSpecification> meta)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 9f8bea2..509aeac 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@ -24,6 +24,8 @@ import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.schema.SchemaKeyspace;
+import org.apache.cassandra.transport.Server;
+import org.apache.cassandra.utils.ByteBufferUtil;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -130,37 +132,33 @@ public class AlterTest extends CQLTester
         assertInvalidThrow(SyntaxException.class, "CREATE KEYSPACE ks1");
         assertInvalidThrow(ConfigurationException.class, "CREATE KEYSPACE ks1 WITH replication= { 'replication_factor' : 1 }");
 
-        execute("CREATE KEYSPACE ks1 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
-        execute("CREATE KEYSPACE ks2 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false");
+        String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
+        String ks2 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false");
 
-        assertRows(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"),
-                   row("ks1", true),
+        assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"),
                    row(KEYSPACE, true),
                    row(KEYSPACE_PER_TEST, true),
-                   row("ks2", false));
+                   row(ks1, true),
+                   row(ks2, false));
 
-        execute("ALTER KEYSPACE ks1 WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dc1' : 1 } AND durable_writes=False");
-        execute("ALTER KEYSPACE ks2 WITH durable_writes=true");
+        schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dc1' : 1 } AND durable_writes=False");
+        schemaChange("ALTER KEYSPACE " + ks2 + " WITH durable_writes=true");
 
-        assertRows(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"),
-                   row("ks1", false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", "dc1", "1")),
+        assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"),
                    row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")),
                    row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")),
-                   row("ks2", true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")));
+                   row(ks1, false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", "dc1", "1")),
+                   row(ks2, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")));
 
-        execute("USE ks1");
+        execute("USE " + ks1);
 
         assertInvalidThrow(ConfigurationException.class, "CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'min_threshold' : 4 }");
 
         execute("CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'class' : 'SizeTieredCompactionStrategy', 'min_threshold' : 7 }");
-        assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='ks1'"),
+        assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='" + ks1 + "'"),
                    row("cf1", map("class", "org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy",
                                   "min_threshold", "7",
                                   "max_threshold", "32")));
-
-        // clean-up
-        execute("DROP KEYSPACE ks1");
-        execute("DROP KEYSPACE ks2");
     }
 
     /**
@@ -324,4 +322,20 @@ public class AlterTest extends CQLTester
         createTable("CREATE TABLE %s (key blob, column1 blob, value blob, PRIMARY KEY ((key), column1)) WITH COMPACT STORAGE");
         assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s ALTER column1 TYPE ascii");
     }
+
+    @Test
+    public void testAlterToBlob() throws Throwable
+    {
+        // This tests for the bug from #11820 in particular
+
+        createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
+
+        execute("INSERT INTO %s (a, b) VALUES (1, 1)");
+
+        executeNet(Server.CURRENT_VERSION, "ALTER TABLE %s ALTER b TYPE BLOB");
+
+        assertRowsNet(Server.CURRENT_VERSION, executeNet(Server.CURRENT_VERSION, "SELECT * FROM %s WHERE a = 1"),
+            row(1, ByteBufferUtil.bytes(1))
+        );
+    }
 }


[4/6] cassandra git commit: Merge commit 'e4c344c58f8ca8f69224855080de4ec266fb671e' into cassandra-3.9

Posted by sl...@apache.org.
Merge commit 'e4c344c58f8ca8f69224855080de4ec266fb671e' into cassandra-3.9

* commit 'e4c344c58f8ca8f69224855080de4ec266fb671e':
  Avoid deserialization error after altering column type


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

Branch: refs/heads/trunk
Commit: 1f014b2cac739439bcf97adb34ae5b8ede6d2fed
Parents: c7547e0 e4c344c
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Jun 30 11:14:48 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Jun 30 11:16:06 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  2 +
 .../apache/cassandra/db/rows/BufferCell.java    |  1 -
 src/java/org/apache/cassandra/db/rows/Cell.java | 16 +++----
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 97 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 463cf78,573f704..ed884a9
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,35 -1,10 +1,37 @@@
 -3.0.9
 +3.9
++Merged from 3.0:
+  * Fix EOF exception when altering column type (CASSANDRA-11820)
  Merged from 2.2:
   * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
 +Merged from 2.1:
 + * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043)
 + * Remove finished incoming streaming connections from MessagingService (CASSANDRA-11854)
 +
  
 -3.0.8
 - * Fix potential race in schema during new table creation (CASSANDRA-12083)
 +3.8
 + * Improve details in compaction log message (CASSANDRA-12080)
 + * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
 + * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993)
 + * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579)
 + * Move skip_stop_words filter before stemming (CASSANDRA-12078)
 + * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
 + * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
 + * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966)
 + * Add cross-DC latency metrics (CASSANDRA-11596)
 + * Allow terms in selection clause (CASSANDRA-10783)
 + * Add bind variables to trace (CASSANDRA-11719)
 + * Switch counter shards' clock to timestamps (CASSANDRA-9811)
 + * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853)
 + * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718)
 + * Support older ant versions (CASSANDRA-11807)
 + * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623)
 + * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546)
 + * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
 + * Faster streaming (CASSANDRA-9766)
 + * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425)
 + * Add repaired percentage metric (CASSANDRA-11503)
 + * Add Change-Data-Capture (CASSANDRA-8844)
 +Merged from 3.0:
   * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/BufferCell.java
index d998d69,db0ded5..7bb1f4b
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@@ -125,5 -200,166 +125,4 @@@ public class BufferCell extends Abstrac
      {
          return EMPTY_SIZE + ObjectSizes.sizeOnHeapExcludingData(value) + (path == null ? 0 : path.unsharedHeapSizeExcludingData());
      }
 -
 -    /**
 -     * The serialization format for cell is:
 -     *     [ flags ][ timestamp ][ deletion time ][    ttl    ][ path size ][ path ][ value size ][ value ]
 -     *     [   1b  ][ 8b (vint) ][   4b (vint)   ][ 4b (vint) ][ 4b (vint) ][  arb ][  4b (vint) ][  arb  ]
 -     *
 -     * where not all field are always present (in fact, only the [ flags ] are guaranteed to be present). The fields have the following
 -     * meaning:
 -     *   - [ flags ] is the cell flags. It is a byte for which each bit represents a flag whose meaning is explained below (*_MASK constants)
 -     *   - [ timestamp ] is the cell timestamp. Present unless the cell has the USE_TIMESTAMP_MASK.
 -     *   - [ deletion time]: the local deletion time for the cell. Present if either the cell is deleted (IS_DELETED_MASK)
 -     *       or it is expiring (IS_EXPIRING_MASK) but doesn't have the USE_ROW_TTL_MASK.
 -     *   - [ ttl ]: the ttl for the cell. Present if the row is expiring (IS_EXPIRING_MASK) but doesn't have the
 -     *       USE_ROW_TTL_MASK.
 -     *   - [ value size ] is the size of the [ value ] field. It's present unless either the cell has the HAS_EMPTY_VALUE_MASK, or the value
 -     *       for columns of this type have a fixed length.
 -     *   - [ path size ] is the size of the [ path ] field. Present iff this is the cell of a complex column.
 -     *   - [ value ]: the cell value, unless it has the HAS_EMPTY_VALUE_MASK.
 -     *   - [ path ]: the cell path if the column this is a cell of is complex.
 -     */
 -    static class Serializer implements Cell.Serializer
 -    {
 -        private final static int IS_DELETED_MASK             = 0x01; // Whether the cell is a tombstone or not.
 -        private final static int IS_EXPIRING_MASK            = 0x02; // Whether the cell is expiring.
 -        private final static int HAS_EMPTY_VALUE_MASK        = 0x04; // Wether the cell has an empty value. This will be the case for tombstone in particular.
 -        private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
 -        private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 -
 -        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
 -        {
 -            assert cell != null;
 -            boolean hasValue = cell.value().hasRemaining();
 -            boolean isDeleted = cell.isTombstone();
 -            boolean isExpiring = cell.isExpiring();
 -            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 -            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 -            int flags = 0;
 -            if (!hasValue)
 -                flags |= HAS_EMPTY_VALUE_MASK;
 -
 -            if (isDeleted)
 -                flags |= IS_DELETED_MASK;
 -            else if (isExpiring)
 -                flags |= IS_EXPIRING_MASK;
 -
 -            if (useRowTimestamp)
 -                flags |= USE_ROW_TIMESTAMP_MASK;
 -            if (useRowTTL)
 -                flags |= USE_ROW_TTL_MASK;
 -
 -            out.writeByte((byte)flags);
 -
 -            if (!useRowTimestamp)
 -                header.writeTimestamp(cell.timestamp(), out);
 -
 -            if ((isDeleted || isExpiring) && !useRowTTL)
 -                header.writeLocalDeletionTime(cell.localDeletionTime(), out);
 -            if (isExpiring && !useRowTTL)
 -                header.writeTTL(cell.ttl(), out);
 -
 -            if (column.isComplex())
 -                column.cellPathSerializer().serialize(cell.path(), out);
 -
 -            if (hasValue)
 -                header.getType(column).writeValue(cell.value(), out);
 -        }
 -
 -        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
 -        {
 -            int flags = in.readUnsignedByte();
 -            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 -            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 -            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 -            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 -            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 -
 -            long timestamp = useRowTimestamp ? rowLiveness.timestamp() : header.readTimestamp(in);
 -
 -            int localDeletionTime = useRowTTL
 -                                  ? rowLiveness.localExpirationTime()
 -                                  : (isDeleted || isExpiring ? header.readLocalDeletionTime(in) : NO_DELETION_TIME);
 -
 -            int ttl = useRowTTL ? rowLiveness.ttl() : (isExpiring ? header.readTTL(in) : NO_TTL);
 -
 -            CellPath path = column.isComplex()
 -                          ? column.cellPathSerializer().deserialize(in)
 -                          : null;
 -
 -            boolean isCounter = localDeletionTime == NO_DELETION_TIME && column.type.isCounter();
 -
 -            ByteBuffer value = ByteBufferUtil.EMPTY_BYTE_BUFFER;
 -            if (hasValue)
 -            {
 -                if (helper.canSkipValue(column) || (path != null && helper.canSkipValue(path)))
 -                {
 -                    header.getType(column).skipValue(in);
 -                }
 -                else
 -                {
 -                    value = header.getType(column).readValue(in, DatabaseDescriptor.getMaxValueSize());
 -                    if (isCounter)
 -                        value = helper.maybeClearCounterValue(value);
 -                }
 -            }
 -
 -            return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
 -        }
 -
 -        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
 -        {
 -            long size = 1; // flags
 -            boolean hasValue = cell.value().hasRemaining();
 -            boolean isDeleted = cell.isTombstone();
 -            boolean isExpiring = cell.isExpiring();
 -            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 -            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 -
 -            if (!useRowTimestamp)
 -                size += header.timestampSerializedSize(cell.timestamp());
 -
 -            if ((isDeleted || isExpiring) && !useRowTTL)
 -                size += header.localDeletionTimeSerializedSize(cell.localDeletionTime());
 -            if (isExpiring && !useRowTTL)
 -                size += header.ttlSerializedSize(cell.ttl());
 -
 -            if (column.isComplex())
 -                size += column.cellPathSerializer().serializedSize(cell.path());
 -
 -            if (hasValue)
 -                size += header.getType(column).writtenLength(cell.value());
 -
 -            return size;
 -        }
 -
 -        // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
 -        public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException
 -        {
 -            int flags = in.readUnsignedByte();
 -            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 -            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 -            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 -            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 -            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 -
 -            if (!useRowTimestamp)
 -                header.skipTimestamp(in);
 -
 -            if (!useRowTTL && (isDeleted || isExpiring))
 -                header.skipLocalDeletionTime(in);
 -
 -            if (!useRowTTL && isExpiring)
 -                header.skipTTL(in);
 -
 -            if (column.isComplex())
 -                column.cellPathSerializer().skip(in);
 -
 -            if (hasValue)
 -                header.getType(column).skipValue(in);
--
 -            return true;
 -        }
 -    }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Cell.java
index 0b7c46e,d10cc74..19d1f30
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@@ -144,165 -143,15 +144,165 @@@ public abstract class Cell extends Colu
      // Overrides super type to provide a more precise return type.
      public abstract Cell purge(DeletionPurger purger, int nowInSec);
  
 -    public interface Serializer
 +    /**
 +     * The serialization format for cell is:
 +     *     [ flags ][ timestamp ][ deletion time ][    ttl    ][ path size ][ path ][ value size ][ value ]
 +     *     [   1b  ][ 8b (vint) ][   4b (vint)   ][ 4b (vint) ][ 4b (vint) ][  arb ][  4b (vint) ][  arb  ]
 +     *
 +     * where not all field are always present (in fact, only the [ flags ] are guaranteed to be present). The fields have the following
 +     * meaning:
 +     *   - [ flags ] is the cell flags. It is a byte for which each bit represents a flag whose meaning is explained below (*_MASK constants)
 +     *   - [ timestamp ] is the cell timestamp. Present unless the cell has the USE_TIMESTAMP_MASK.
 +     *   - [ deletion time]: the local deletion time for the cell. Present if either the cell is deleted (IS_DELETED_MASK)
 +     *       or it is expiring (IS_EXPIRING_MASK) but doesn't have the USE_ROW_TTL_MASK.
 +     *   - [ ttl ]: the ttl for the cell. Present if the row is expiring (IS_EXPIRING_MASK) but doesn't have the
 +     *       USE_ROW_TTL_MASK.
 +     *   - [ value size ] is the size of the [ value ] field. It's present unless either the cell has the HAS_EMPTY_VALUE_MASK, or the value
 +     *       for columns of this type have a fixed length.
 +     *   - [ path size ] is the size of the [ path ] field. Present iff this is the cell of a complex column.
 +     *   - [ value ]: the cell value, unless it has the HAS_EMPTY_VALUE_MASK.
 +     *   - [ path ]: the cell path if the column this is a cell of is complex.
 +     */
 +    static class Serializer
      {
 -        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
 +        private final static int IS_DELETED_MASK             = 0x01; // Whether the cell is a tombstone or not.
 +        private final static int IS_EXPIRING_MASK            = 0x02; // Whether the cell is expiring.
 +        private final static int HAS_EMPTY_VALUE_MASK        = 0x04; // Wether the cell has an empty value. This will be the case for tombstone in particular.
 +        private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
 +        private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 +
-         public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
++        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
 +        {
 +            assert cell != null;
 +            boolean hasValue = cell.value().hasRemaining();
 +            boolean isDeleted = cell.isTombstone();
 +            boolean isExpiring = cell.isExpiring();
 +            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 +            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 +            int flags = 0;
 +            if (!hasValue)
 +                flags |= HAS_EMPTY_VALUE_MASK;
 +
 +            if (isDeleted)
 +                flags |= IS_DELETED_MASK;
 +            else if (isExpiring)
 +                flags |= IS_EXPIRING_MASK;
 +
 +            if (useRowTimestamp)
 +                flags |= USE_ROW_TIMESTAMP_MASK;
 +            if (useRowTTL)
 +                flags |= USE_ROW_TTL_MASK;
 +
 +            out.writeByte((byte)flags);
 +
 +            if (!useRowTimestamp)
 +                header.writeTimestamp(cell.timestamp(), out);
 +
 +            if ((isDeleted || isExpiring) && !useRowTTL)
 +                header.writeLocalDeletionTime(cell.localDeletionTime(), out);
 +            if (isExpiring && !useRowTTL)
 +                header.writeTTL(cell.ttl(), out);
 +
-             if (cell.column().isComplex())
-                 cell.column().cellPathSerializer().serialize(cell.path(), out);
++            if (column.isComplex())
++                column.cellPathSerializer().serialize(cell.path(), out);
 +
 +            if (hasValue)
-                 header.getType(cell.column()).writeValue(cell.value(), out);
++                header.getType(column).writeValue(cell.value(), out);
 +        }
 +
 +        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
 +        {
 +            int flags = in.readUnsignedByte();
 +            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 +            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 +            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 +            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 +            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 +
 +            long timestamp = useRowTimestamp ? rowLiveness.timestamp() : header.readTimestamp(in);
 +
 +            int localDeletionTime = useRowTTL
 +                                    ? rowLiveness.localExpirationTime()
 +                                    : (isDeleted || isExpiring ? header.readLocalDeletionTime(in) : NO_DELETION_TIME);
 +
 +            int ttl = useRowTTL ? rowLiveness.ttl() : (isExpiring ? header.readTTL(in) : NO_TTL);
  
 -        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException;
 +            CellPath path = column.isComplex()
 +                            ? column.cellPathSerializer().deserialize(in)
 +                            : null;
  
 -        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header);
 +            ByteBuffer value = ByteBufferUtil.EMPTY_BYTE_BUFFER;
 +            if (hasValue)
 +            {
 +                if (helper.canSkipValue(column) || (path != null && helper.canSkipValue(path)))
 +                {
 +                    header.getType(column).skipValue(in);
 +                }
 +                else
 +                {
 +                    boolean isCounter = localDeletionTime == NO_DELETION_TIME && column.type.isCounter();
 +
 +                    value = header.getType(column).readValue(in, DatabaseDescriptor.getMaxValueSize());
 +                    if (isCounter)
 +                        value = helper.maybeClearCounterValue(value);
 +                }
 +            }
 +
 +            return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
 +        }
 +
-         public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header)
++        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
 +        {
 +            long size = 1; // flags
 +            boolean hasValue = cell.value().hasRemaining();
 +            boolean isDeleted = cell.isTombstone();
 +            boolean isExpiring = cell.isExpiring();
 +            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 +            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 +
 +            if (!useRowTimestamp)
 +                size += header.timestampSerializedSize(cell.timestamp());
 +
 +            if ((isDeleted || isExpiring) && !useRowTTL)
 +                size += header.localDeletionTimeSerializedSize(cell.localDeletionTime());
 +            if (isExpiring && !useRowTTL)
 +                size += header.ttlSerializedSize(cell.ttl());
 +
-             if (cell.column().isComplex())
-                 size += cell.column().cellPathSerializer().serializedSize(cell.path());
++            if (column.isComplex())
++                size += column.cellPathSerializer().serializedSize(cell.path());
 +
 +            if (hasValue)
-                 size += header.getType(cell.column()).writtenLength(cell.value());
++                size += header.getType(column).writtenLength(cell.value());
 +
 +            return size;
 +        }
  
          // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
 -        public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException;
 +        public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException
 +        {
 +            int flags = in.readUnsignedByte();
 +            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 +            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 +            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 +            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 +            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 +
 +            if (!useRowTimestamp)
 +                header.skipTimestamp(in);
 +
 +            if (!useRowTTL && (isDeleted || isExpiring))
 +                header.skipLocalDeletionTime(in);
 +
 +            if (!useRowTTL && isExpiring)
 +                header.skipTTL(in);
 +
 +            if (column.isComplex())
 +                column.cellPathSerializer().skip(in);
 +
 +            if (hasValue)
 +                header.getType(column).skipValue(in);
 +
 +            return true;
 +        }
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
index 890806c,dc6f187..5ca7e03
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
@@@ -25,8 -24,8 +25,9 @@@ import net.nicoulaj.compilecommand.anno
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.io.util.DataInputPlus;
 +import org.apache.cassandra.io.util.DataOutputBuffer;
  import org.apache.cassandra.io.util.DataOutputPlus;
+ import org.apache.cassandra.utils.SearchIterator;
  
  /**
   * Serialize/deserialize a single Unfiltered (both on-wire and on-disk).
@@@ -222,15 -175,24 +223,24 @@@ public class UnfilteredSerialize
          if ((flags & HAS_DELETION) != 0)
              header.writeDeletionTime(deletion.time(), out);
  
 -        if (!hasAllColumns)
 +        if ((flags & HAS_ALL_COLUMNS) == 0)
              Columns.serializer.serializeSubset(Collections2.transform(row, ColumnData::column), headerColumns, out);
  
+         SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
          for (ColumnData data : row)
          {
+             // We can obtain the column for data directly from data.column(). However, if the cell/complex data
+             // originates from a sstable, the column we'll get will have the type used when the sstable was serialized,
+             // and if that type have been recently altered, that may not be the type we want to serialize the column
+             // with. So we use the ColumnDefinition from the "header" which is "current". Also see #11810 for what
+             // happens if we don't do that.
+             ColumnDefinition column = si.next(data.column());
+             assert column != null;
+ 
              if (data.column.isSimple())
-                 Cell.serializer.serialize((Cell) data, out, pkLiveness, header);
+                 Cell.serializer.serialize((Cell) data, column, out, pkLiveness, header);
              else
-                 writeComplexColumn((ComplexColumnData) data, (flags & HAS_COMPLEX_DELETION) != 0, pkLiveness, header, out);
 -                writeComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header, out);
++                writeComplexColumn((ComplexColumnData) data, column, (flags & HAS_COMPLEX_DELETION) != 0, pkLiveness, header, out);
          }
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 3eb55fd,509aeac..bb4bf48
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@@ -325,84 -323,19 +323,100 @@@ public class AlterTest extends CQLTeste
          assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s ALTER column1 TYPE ascii");
      }
  
 +    /*
 +     * Test case to check addition of one column
 +    */
 +    @Test
 +    public void testAlterAddOneColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id int, name text, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s add mail text;");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "mail", "name");
 +    }
 +
 +    /*
 +     * Test case to check addition of more than one column
 +     */
 +    @Test
 +    public void testAlterAddMultiColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id int, yearofbirth int, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s add (firstname text, password blob, lastname text, \"SOME escaped col\" bigint)");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "SOME escaped col", "firstname", "lastname", "password", "yearofbirth");
 +    }
 +
 +    /*
 +     *  Should throw SyntaxException if multiple columns are added using wrong syntax.
 +     *  Expected Syntax : Alter table T1 add (C1 datatype,C2 datatype,C3 datatype)
 +     */
 +    @Test(expected = SyntaxException.class)
 +    public void testAlterAddMultiColumnWithoutBraces() throws Throwable
 +    {
 +        execute("ALTER TABLE %s.users add lastname text, password blob, yearofbirth int;");
 +    }
 +
 +    /*
 +     *  Test case to check deletion of one column
 +     */
 +    @Test
 +    public void testAlterDropOneColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s drop telephone");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth");
 +    }
 +
 +    @Test
 +    /*
 +     * Test case to check deletion of more than one column
 +     */
 +    public void testAlterDropMultiColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, \"SOME escaped col\" bigint, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s drop (address, telephone, \"SOME escaped col\");");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth");
 +    }
 +
 +    /*
 +     *  Should throw SyntaxException if multiple columns are dropped using wrong syntax.
 +     */
 +    @Test(expected = SyntaxException.class)
 +    public void testAlterDeletionColumnWithoutBraces() throws Throwable
 +    {
 +        execute("ALTER TABLE %s.users drop name,address;");
 +    }
 +
 +    @Test(expected = InvalidRequestException.class)
 +    public void testAlterAddDuplicateColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        execute("ALTER TABLE %s add (salary int, salary int);");
 +    }
 +
 +    @Test(expected = InvalidRequestException.class)
 +    public void testAlterDropDuplicateColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        execute("ALTER TABLE %s drop (address, address);");
 +    }
++
+     @Test
+     public void testAlterToBlob() throws Throwable
+     {
+         // This tests for the bug from #11820 in particular
+ 
+         createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
+ 
+         execute("INSERT INTO %s (a, b) VALUES (1, 1)");
+ 
+         executeNet(Server.CURRENT_VERSION, "ALTER TABLE %s ALTER b TYPE BLOB");
+ 
+         assertRowsNet(Server.CURRENT_VERSION, executeNet(Server.CURRENT_VERSION, "SELECT * FROM %s WHERE a = 1"),
+             row(1, ByteBufferUtil.bytes(1))
+         );
+     }
  }


[2/6] cassandra git commit: Avoid deserialization error after altering column type

Posted by sl...@apache.org.
Avoid deserialization error after altering column type

This makes sure the column used when serializing intra-node messages is
"current". Previously, we would use the type used during deserialization
which could not be "current" due to an ALTER TYPE.

patch by slebresne; reviewed by thobbs for CASSANDRA-11820


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

Branch: refs/heads/cassandra-3.9
Commit: e4c344c58f8ca8f69224855080de4ec266fb671e
Parents: 362e132
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Mon Jun 27 14:17:27 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Jun 30 11:14:01 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/db/rows/BufferCell.java    | 16 +++----
 src/java/org/apache/cassandra/db/rows/Cell.java |  4 +-
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 98 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ae37d2c..573f704 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
 Merged from 2.2:
  * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/BufferCell.java b/src/java/org/apache/cassandra/db/rows/BufferCell.java
index 22b629a..db0ded5 100644
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@ -228,7 +228,7 @@ public class BufferCell extends AbstractCell
         private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
         private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 
-        public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
+        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
         {
             assert cell != null;
             boolean hasValue = cell.value().hasRemaining();
@@ -260,11 +260,11 @@ public class BufferCell extends AbstractCell
             if (isExpiring && !useRowTTL)
                 header.writeTTL(cell.ttl(), out);
 
-            if (cell.column().isComplex())
-                cell.column().cellPathSerializer().serialize(cell.path(), out);
+            if (column.isComplex())
+                column.cellPathSerializer().serialize(cell.path(), out);
 
             if (hasValue)
-                header.getType(cell.column()).writeValue(cell.value(), out);
+                header.getType(column).writeValue(cell.value(), out);
         }
 
         public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
@@ -308,7 +308,7 @@ public class BufferCell extends AbstractCell
             return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
         }
 
-        public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header)
+        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
         {
             long size = 1; // flags
             boolean hasValue = cell.value().hasRemaining();
@@ -325,11 +325,11 @@ public class BufferCell extends AbstractCell
             if (isExpiring && !useRowTTL)
                 size += header.ttlSerializedSize(cell.ttl());
 
-            if (cell.column().isComplex())
-                size += cell.column().cellPathSerializer().serializedSize(cell.path());
+            if (column.isComplex())
+                size += column.cellPathSerializer().serializedSize(cell.path());
 
             if (hasValue)
-                size += header.getType(cell.column()).writtenLength(cell.value());
+                size += header.getType(column).writtenLength(cell.value());
 
             return size;
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Cell.java b/src/java/org/apache/cassandra/db/rows/Cell.java
index b10ce06..d10cc74 100644
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@ -145,11 +145,11 @@ public abstract class Cell extends ColumnData
 
     public interface Serializer
     {
-        public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
+        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
 
         public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException;
 
-        public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header);
+        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header);
 
         // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
         public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
index e4202c9..dc6f187 100644
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
@@ -25,6 +25,7 @@ import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.io.util.DataInputPlus;
 import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.utils.SearchIterator;
 
 /**
  * Serialize/deserialize a single Unfiltered (both on-wire and on-disk).
@@ -177,16 +178,25 @@ public class UnfilteredSerializer
         if (!hasAllColumns)
             Columns.serializer.serializeSubset(Collections2.transform(row, ColumnData::column), headerColumns, out);
 
+        SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
         for (ColumnData data : row)
         {
+            // We can obtain the column for data directly from data.column(). However, if the cell/complex data
+            // originates from a sstable, the column we'll get will have the type used when the sstable was serialized,
+            // and if that type have been recently altered, that may not be the type we want to serialize the column
+            // with. So we use the ColumnDefinition from the "header" which is "current". Also see #11810 for what
+            // happens if we don't do that.
+            ColumnDefinition column = si.next(data.column());
+            assert column != null;
+
             if (data.column.isSimple())
-                Cell.serializer.serialize((Cell) data, out, pkLiveness, header);
+                Cell.serializer.serialize((Cell) data, column, out, pkLiveness, header);
             else
-                writeComplexColumn((ComplexColumnData) data, hasComplexDeletion, pkLiveness, header, out);
+                writeComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header, out);
         }
     }
 
-    private void writeComplexColumn(ComplexColumnData data, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header, DataOutputPlus out)
+    private void writeComplexColumn(ComplexColumnData data, ColumnDefinition column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header, DataOutputPlus out)
     throws IOException
     {
         if (hasComplexDeletion)
@@ -194,7 +204,7 @@ public class UnfilteredSerializer
 
         out.writeUnsignedVInt(data.cellsCount());
         for (Cell cell : data)
-            Cell.serializer.serialize(cell, out, rowLiveness, header);
+            Cell.serializer.serialize(cell, column, out, rowLiveness, header);
     }
 
     private void serialize(RangeTombstoneMarker marker, SerializationHeader header, DataOutputPlus out, long previousUnfilteredSize, int version)
@@ -274,18 +284,22 @@ public class UnfilteredSerializer
         if (!hasAllColumns)
             size += Columns.serializer.serializedSubsetSize(Collections2.transform(row, ColumnData::column), header.columns(isStatic));
 
+        SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
         for (ColumnData data : row)
         {
+            ColumnDefinition column = si.next(data.column());
+            assert column != null;
+
             if (data.column.isSimple())
-                size += Cell.serializer.serializedSize((Cell) data, pkLiveness, header);
+                size += Cell.serializer.serializedSize((Cell) data, column, pkLiveness, header);
             else
-                size += sizeOfComplexColumn((ComplexColumnData) data, hasComplexDeletion, pkLiveness, header);
+                size += sizeOfComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header);
         }
 
         return size;
     }
 
-    private long sizeOfComplexColumn(ComplexColumnData data, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header)
+    private long sizeOfComplexColumn(ComplexColumnData data, ColumnDefinition column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header)
     {
         long size = 0;
 
@@ -294,7 +308,7 @@ public class UnfilteredSerializer
 
         size += TypeSizes.sizeofUnsignedVInt(data.cellsCount());
         for (Cell cell : data)
-            size += Cell.serializer.serializedSize(cell, rowLiveness, header);
+            size += Cell.serializer.serializedSize(cell, column, rowLiveness, header);
 
         return size;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index fe03db4..a213edf 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -131,6 +131,7 @@ public abstract class CQLTester
 
     public static ResultMessage lastSchemaChangeResult;
 
+    private List<String> keyspaces = new ArrayList<>();
     private List<String> tables = new ArrayList<>();
     private List<String> types = new ArrayList<>();
     private List<String> functions = new ArrayList<>();
@@ -262,10 +263,12 @@ public abstract class CQLTester
         usePrepared = USE_PREPARED_VALUES;
         reusePrepared = REUSE_PREPARED;
 
+        final List<String> keyspacesToDrop = copy(keyspaces);
         final List<String> tablesToDrop = copy(tables);
         final List<String> typesToDrop = copy(types);
         final List<String> functionsToDrop = copy(functions);
         final List<String> aggregatesToDrop = copy(aggregates);
+        keyspaces = null;
         tables = null;
         types = null;
         functions = null;
@@ -290,6 +293,9 @@ public abstract class CQLTester
                     for (int i = typesToDrop.size() - 1; i >= 0; i--)
                         schemaChange(String.format("DROP TYPE IF EXISTS %s.%s", KEYSPACE, typesToDrop.get(i)));
 
+                    for (int i = keyspacesToDrop.size() - 1; i >= 0; i--)
+                        schemaChange(String.format("DROP KEYSPACE IF EXISTS %s", keyspacesToDrop.get(i)));
+
                     // Dropping doesn't delete the sstables. It's not a huge deal but it's cleaner to cleanup after us
                     // Thas said, we shouldn't delete blindly before the TransactionLogs.SSTableTidier for the table we drop
                     // have run or they will be unhappy. Since those taks are scheduled on StorageService.tasks and that's
@@ -501,6 +507,22 @@ public abstract class CQLTester
         schemaChange(fullQuery);
     }
 
+    protected String createKeyspace(String query)
+    {
+        String currentKeyspace = createKeyspaceName();
+        String fullQuery = String.format(query, currentKeyspace);
+        logger.info(fullQuery);
+        schemaChange(fullQuery);
+        return currentKeyspace;
+    }
+
+    protected String createKeyspaceName()
+    {
+        String currentKeyspace = "keyspace_" + seqNumber.getAndIncrement();
+        keyspaces.add(currentKeyspace);
+        return currentKeyspace;
+    }
+
     protected String createTable(String query)
     {
         String currentTable = createTableName();
@@ -519,8 +541,7 @@ public abstract class CQLTester
 
     protected void createTableMayThrow(String query) throws Throwable
     {
-        String currentTable = "table_" + seqNumber.getAndIncrement();
-        tables.add(currentTable);
+        String currentTable = createTableName();
         String fullQuery = formatQuery(query);
         logger.info(fullQuery);
         QueryProcessor.executeOnceInternal(fullQuery);
@@ -825,6 +846,16 @@ public abstract class CQLTester
      */
     public static void assertRowsIgnoringOrder(UntypedResultSet result, Object[]... rows)
     {
+        assertRowsIgnoringOrderInternal(result, false, rows);
+    }
+
+    public static void assertRowsIgnoringOrderAndExtra(UntypedResultSet result, Object[]... rows)
+    {
+        assertRowsIgnoringOrderInternal(result, true, rows);
+    }
+
+    private static void assertRowsIgnoringOrderInternal(UntypedResultSet result, boolean ignoreExtra, Object[]... rows)
+    {
         if (result == null)
         {
             if (rows.length > 0)
@@ -855,7 +886,7 @@ public abstract class CQLTester
 
         com.google.common.collect.Sets.SetView<List<ByteBuffer>> extra = com.google.common.collect.Sets.difference(actualRows, expectedRows);
         com.google.common.collect.Sets.SetView<List<ByteBuffer>> missing = com.google.common.collect.Sets.difference(expectedRows, actualRows);
-        if (!extra.isEmpty() || !missing.isEmpty())
+        if ((!ignoreExtra && !extra.isEmpty()) || !missing.isEmpty())
         {
             List<String> extraRows = makeRowStrings(extra, meta);
             List<String> missingRows = makeRowStrings(missing, meta);
@@ -876,7 +907,7 @@ public abstract class CQLTester
                 Assert.fail("Missing " + missing.size() + " row(s) in result: \n    " + missingRows.stream().collect(Collectors.joining("\n    ")));
         }
 
-        assert expectedRows.size() == actualRows.size();
+        assert ignoreExtra || expectedRows.size() == actualRows.size();
     }
 
     private static List<String> makeRowStrings(Iterable<List<ByteBuffer>> rows, List<ColumnSpecification> meta)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 9f8bea2..509aeac 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@ -24,6 +24,8 @@ import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.schema.SchemaKeyspace;
+import org.apache.cassandra.transport.Server;
+import org.apache.cassandra.utils.ByteBufferUtil;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -130,37 +132,33 @@ public class AlterTest extends CQLTester
         assertInvalidThrow(SyntaxException.class, "CREATE KEYSPACE ks1");
         assertInvalidThrow(ConfigurationException.class, "CREATE KEYSPACE ks1 WITH replication= { 'replication_factor' : 1 }");
 
-        execute("CREATE KEYSPACE ks1 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
-        execute("CREATE KEYSPACE ks2 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false");
+        String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
+        String ks2 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false");
 
-        assertRows(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"),
-                   row("ks1", true),
+        assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"),
                    row(KEYSPACE, true),
                    row(KEYSPACE_PER_TEST, true),
-                   row("ks2", false));
+                   row(ks1, true),
+                   row(ks2, false));
 
-        execute("ALTER KEYSPACE ks1 WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dc1' : 1 } AND durable_writes=False");
-        execute("ALTER KEYSPACE ks2 WITH durable_writes=true");
+        schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dc1' : 1 } AND durable_writes=False");
+        schemaChange("ALTER KEYSPACE " + ks2 + " WITH durable_writes=true");
 
-        assertRows(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"),
-                   row("ks1", false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", "dc1", "1")),
+        assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"),
                    row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")),
                    row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")),
-                   row("ks2", true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")));
+                   row(ks1, false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", "dc1", "1")),
+                   row(ks2, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")));
 
-        execute("USE ks1");
+        execute("USE " + ks1);
 
         assertInvalidThrow(ConfigurationException.class, "CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'min_threshold' : 4 }");
 
         execute("CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'class' : 'SizeTieredCompactionStrategy', 'min_threshold' : 7 }");
-        assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='ks1'"),
+        assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='" + ks1 + "'"),
                    row("cf1", map("class", "org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy",
                                   "min_threshold", "7",
                                   "max_threshold", "32")));
-
-        // clean-up
-        execute("DROP KEYSPACE ks1");
-        execute("DROP KEYSPACE ks2");
     }
 
     /**
@@ -324,4 +322,20 @@ public class AlterTest extends CQLTester
         createTable("CREATE TABLE %s (key blob, column1 blob, value blob, PRIMARY KEY ((key), column1)) WITH COMPACT STORAGE");
         assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s ALTER column1 TYPE ascii");
     }
+
+    @Test
+    public void testAlterToBlob() throws Throwable
+    {
+        // This tests for the bug from #11820 in particular
+
+        createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
+
+        execute("INSERT INTO %s (a, b) VALUES (1, 1)");
+
+        executeNet(Server.CURRENT_VERSION, "ALTER TABLE %s ALTER b TYPE BLOB");
+
+        assertRowsNet(Server.CURRENT_VERSION, executeNet(Server.CURRENT_VERSION, "SELECT * FROM %s WHERE a = 1"),
+            row(1, ByteBufferUtil.bytes(1))
+        );
+    }
 }


[5/6] cassandra git commit: Merge commit 'e4c344c58f8ca8f69224855080de4ec266fb671e' into cassandra-3.9

Posted by sl...@apache.org.
Merge commit 'e4c344c58f8ca8f69224855080de4ec266fb671e' into cassandra-3.9

* commit 'e4c344c58f8ca8f69224855080de4ec266fb671e':
  Avoid deserialization error after altering column type


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

Branch: refs/heads/cassandra-3.9
Commit: 1f014b2cac739439bcf97adb34ae5b8ede6d2fed
Parents: c7547e0 e4c344c
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Jun 30 11:14:48 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Jun 30 11:16:06 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  2 +
 .../apache/cassandra/db/rows/BufferCell.java    |  1 -
 src/java/org/apache/cassandra/db/rows/Cell.java | 16 +++----
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 97 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 463cf78,573f704..ed884a9
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,35 -1,10 +1,37 @@@
 -3.0.9
 +3.9
++Merged from 3.0:
+  * Fix EOF exception when altering column type (CASSANDRA-11820)
  Merged from 2.2:
   * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
 +Merged from 2.1:
 + * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043)
 + * Remove finished incoming streaming connections from MessagingService (CASSANDRA-11854)
 +
  
 -3.0.8
 - * Fix potential race in schema during new table creation (CASSANDRA-12083)
 +3.8
 + * Improve details in compaction log message (CASSANDRA-12080)
 + * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
 + * Chunk cache to request compressor-compatible buffers if pool space is exhausted (CASSANDRA-11993)
 + * Remove DatabaseDescriptor dependencies from SequentialWriter (CASSANDRA-11579)
 + * Move skip_stop_words filter before stemming (CASSANDRA-12078)
 + * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
 + * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
 + * When SEPWorker assigned work, set thread name to match pool (CASSANDRA-11966)
 + * Add cross-DC latency metrics (CASSANDRA-11596)
 + * Allow terms in selection clause (CASSANDRA-10783)
 + * Add bind variables to trace (CASSANDRA-11719)
 + * Switch counter shards' clock to timestamps (CASSANDRA-9811)
 + * Introduce HdrHistogram and response/service/wait separation to stress tool (CASSANDRA-11853)
 + * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes field (CASSANDRA-11718)
 + * Support older ant versions (CASSANDRA-11807)
 + * Estimate compressed on disk size when deciding if sstable size limit reached (CASSANDRA-11623)
 + * cassandra-stress profiles should support case sensitive schemas (CASSANDRA-11546)
 + * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
 + * Faster streaming (CASSANDRA-9766)
 + * Add prepared query parameter to trace for "Execute CQL3 prepared query" session (CASSANDRA-11425)
 + * Add repaired percentage metric (CASSANDRA-11503)
 + * Add Change-Data-Capture (CASSANDRA-8844)
 +Merged from 3.0:
   * 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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/BufferCell.java
index d998d69,db0ded5..7bb1f4b
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@@ -125,5 -200,166 +125,4 @@@ public class BufferCell extends Abstrac
      {
          return EMPTY_SIZE + ObjectSizes.sizeOnHeapExcludingData(value) + (path == null ? 0 : path.unsharedHeapSizeExcludingData());
      }
 -
 -    /**
 -     * The serialization format for cell is:
 -     *     [ flags ][ timestamp ][ deletion time ][    ttl    ][ path size ][ path ][ value size ][ value ]
 -     *     [   1b  ][ 8b (vint) ][   4b (vint)   ][ 4b (vint) ][ 4b (vint) ][  arb ][  4b (vint) ][  arb  ]
 -     *
 -     * where not all field are always present (in fact, only the [ flags ] are guaranteed to be present). The fields have the following
 -     * meaning:
 -     *   - [ flags ] is the cell flags. It is a byte for which each bit represents a flag whose meaning is explained below (*_MASK constants)
 -     *   - [ timestamp ] is the cell timestamp. Present unless the cell has the USE_TIMESTAMP_MASK.
 -     *   - [ deletion time]: the local deletion time for the cell. Present if either the cell is deleted (IS_DELETED_MASK)
 -     *       or it is expiring (IS_EXPIRING_MASK) but doesn't have the USE_ROW_TTL_MASK.
 -     *   - [ ttl ]: the ttl for the cell. Present if the row is expiring (IS_EXPIRING_MASK) but doesn't have the
 -     *       USE_ROW_TTL_MASK.
 -     *   - [ value size ] is the size of the [ value ] field. It's present unless either the cell has the HAS_EMPTY_VALUE_MASK, or the value
 -     *       for columns of this type have a fixed length.
 -     *   - [ path size ] is the size of the [ path ] field. Present iff this is the cell of a complex column.
 -     *   - [ value ]: the cell value, unless it has the HAS_EMPTY_VALUE_MASK.
 -     *   - [ path ]: the cell path if the column this is a cell of is complex.
 -     */
 -    static class Serializer implements Cell.Serializer
 -    {
 -        private final static int IS_DELETED_MASK             = 0x01; // Whether the cell is a tombstone or not.
 -        private final static int IS_EXPIRING_MASK            = 0x02; // Whether the cell is expiring.
 -        private final static int HAS_EMPTY_VALUE_MASK        = 0x04; // Wether the cell has an empty value. This will be the case for tombstone in particular.
 -        private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
 -        private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 -
 -        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
 -        {
 -            assert cell != null;
 -            boolean hasValue = cell.value().hasRemaining();
 -            boolean isDeleted = cell.isTombstone();
 -            boolean isExpiring = cell.isExpiring();
 -            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 -            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 -            int flags = 0;
 -            if (!hasValue)
 -                flags |= HAS_EMPTY_VALUE_MASK;
 -
 -            if (isDeleted)
 -                flags |= IS_DELETED_MASK;
 -            else if (isExpiring)
 -                flags |= IS_EXPIRING_MASK;
 -
 -            if (useRowTimestamp)
 -                flags |= USE_ROW_TIMESTAMP_MASK;
 -            if (useRowTTL)
 -                flags |= USE_ROW_TTL_MASK;
 -
 -            out.writeByte((byte)flags);
 -
 -            if (!useRowTimestamp)
 -                header.writeTimestamp(cell.timestamp(), out);
 -
 -            if ((isDeleted || isExpiring) && !useRowTTL)
 -                header.writeLocalDeletionTime(cell.localDeletionTime(), out);
 -            if (isExpiring && !useRowTTL)
 -                header.writeTTL(cell.ttl(), out);
 -
 -            if (column.isComplex())
 -                column.cellPathSerializer().serialize(cell.path(), out);
 -
 -            if (hasValue)
 -                header.getType(column).writeValue(cell.value(), out);
 -        }
 -
 -        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
 -        {
 -            int flags = in.readUnsignedByte();
 -            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 -            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 -            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 -            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 -            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 -
 -            long timestamp = useRowTimestamp ? rowLiveness.timestamp() : header.readTimestamp(in);
 -
 -            int localDeletionTime = useRowTTL
 -                                  ? rowLiveness.localExpirationTime()
 -                                  : (isDeleted || isExpiring ? header.readLocalDeletionTime(in) : NO_DELETION_TIME);
 -
 -            int ttl = useRowTTL ? rowLiveness.ttl() : (isExpiring ? header.readTTL(in) : NO_TTL);
 -
 -            CellPath path = column.isComplex()
 -                          ? column.cellPathSerializer().deserialize(in)
 -                          : null;
 -
 -            boolean isCounter = localDeletionTime == NO_DELETION_TIME && column.type.isCounter();
 -
 -            ByteBuffer value = ByteBufferUtil.EMPTY_BYTE_BUFFER;
 -            if (hasValue)
 -            {
 -                if (helper.canSkipValue(column) || (path != null && helper.canSkipValue(path)))
 -                {
 -                    header.getType(column).skipValue(in);
 -                }
 -                else
 -                {
 -                    value = header.getType(column).readValue(in, DatabaseDescriptor.getMaxValueSize());
 -                    if (isCounter)
 -                        value = helper.maybeClearCounterValue(value);
 -                }
 -            }
 -
 -            return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
 -        }
 -
 -        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
 -        {
 -            long size = 1; // flags
 -            boolean hasValue = cell.value().hasRemaining();
 -            boolean isDeleted = cell.isTombstone();
 -            boolean isExpiring = cell.isExpiring();
 -            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 -            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 -
 -            if (!useRowTimestamp)
 -                size += header.timestampSerializedSize(cell.timestamp());
 -
 -            if ((isDeleted || isExpiring) && !useRowTTL)
 -                size += header.localDeletionTimeSerializedSize(cell.localDeletionTime());
 -            if (isExpiring && !useRowTTL)
 -                size += header.ttlSerializedSize(cell.ttl());
 -
 -            if (column.isComplex())
 -                size += column.cellPathSerializer().serializedSize(cell.path());
 -
 -            if (hasValue)
 -                size += header.getType(column).writtenLength(cell.value());
 -
 -            return size;
 -        }
 -
 -        // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
 -        public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException
 -        {
 -            int flags = in.readUnsignedByte();
 -            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 -            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 -            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 -            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 -            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 -
 -            if (!useRowTimestamp)
 -                header.skipTimestamp(in);
 -
 -            if (!useRowTTL && (isDeleted || isExpiring))
 -                header.skipLocalDeletionTime(in);
 -
 -            if (!useRowTTL && isExpiring)
 -                header.skipTTL(in);
 -
 -            if (column.isComplex())
 -                column.cellPathSerializer().skip(in);
 -
 -            if (hasValue)
 -                header.getType(column).skipValue(in);
--
 -            return true;
 -        }
 -    }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Cell.java
index 0b7c46e,d10cc74..19d1f30
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@@ -144,165 -143,15 +144,165 @@@ public abstract class Cell extends Colu
      // Overrides super type to provide a more precise return type.
      public abstract Cell purge(DeletionPurger purger, int nowInSec);
  
 -    public interface Serializer
 +    /**
 +     * The serialization format for cell is:
 +     *     [ flags ][ timestamp ][ deletion time ][    ttl    ][ path size ][ path ][ value size ][ value ]
 +     *     [   1b  ][ 8b (vint) ][   4b (vint)   ][ 4b (vint) ][ 4b (vint) ][  arb ][  4b (vint) ][  arb  ]
 +     *
 +     * where not all field are always present (in fact, only the [ flags ] are guaranteed to be present). The fields have the following
 +     * meaning:
 +     *   - [ flags ] is the cell flags. It is a byte for which each bit represents a flag whose meaning is explained below (*_MASK constants)
 +     *   - [ timestamp ] is the cell timestamp. Present unless the cell has the USE_TIMESTAMP_MASK.
 +     *   - [ deletion time]: the local deletion time for the cell. Present if either the cell is deleted (IS_DELETED_MASK)
 +     *       or it is expiring (IS_EXPIRING_MASK) but doesn't have the USE_ROW_TTL_MASK.
 +     *   - [ ttl ]: the ttl for the cell. Present if the row is expiring (IS_EXPIRING_MASK) but doesn't have the
 +     *       USE_ROW_TTL_MASK.
 +     *   - [ value size ] is the size of the [ value ] field. It's present unless either the cell has the HAS_EMPTY_VALUE_MASK, or the value
 +     *       for columns of this type have a fixed length.
 +     *   - [ path size ] is the size of the [ path ] field. Present iff this is the cell of a complex column.
 +     *   - [ value ]: the cell value, unless it has the HAS_EMPTY_VALUE_MASK.
 +     *   - [ path ]: the cell path if the column this is a cell of is complex.
 +     */
 +    static class Serializer
      {
 -        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
 +        private final static int IS_DELETED_MASK             = 0x01; // Whether the cell is a tombstone or not.
 +        private final static int IS_EXPIRING_MASK            = 0x02; // Whether the cell is expiring.
 +        private final static int HAS_EMPTY_VALUE_MASK        = 0x04; // Wether the cell has an empty value. This will be the case for tombstone in particular.
 +        private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
 +        private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 +
-         public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
++        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
 +        {
 +            assert cell != null;
 +            boolean hasValue = cell.value().hasRemaining();
 +            boolean isDeleted = cell.isTombstone();
 +            boolean isExpiring = cell.isExpiring();
 +            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 +            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 +            int flags = 0;
 +            if (!hasValue)
 +                flags |= HAS_EMPTY_VALUE_MASK;
 +
 +            if (isDeleted)
 +                flags |= IS_DELETED_MASK;
 +            else if (isExpiring)
 +                flags |= IS_EXPIRING_MASK;
 +
 +            if (useRowTimestamp)
 +                flags |= USE_ROW_TIMESTAMP_MASK;
 +            if (useRowTTL)
 +                flags |= USE_ROW_TTL_MASK;
 +
 +            out.writeByte((byte)flags);
 +
 +            if (!useRowTimestamp)
 +                header.writeTimestamp(cell.timestamp(), out);
 +
 +            if ((isDeleted || isExpiring) && !useRowTTL)
 +                header.writeLocalDeletionTime(cell.localDeletionTime(), out);
 +            if (isExpiring && !useRowTTL)
 +                header.writeTTL(cell.ttl(), out);
 +
-             if (cell.column().isComplex())
-                 cell.column().cellPathSerializer().serialize(cell.path(), out);
++            if (column.isComplex())
++                column.cellPathSerializer().serialize(cell.path(), out);
 +
 +            if (hasValue)
-                 header.getType(cell.column()).writeValue(cell.value(), out);
++                header.getType(column).writeValue(cell.value(), out);
 +        }
 +
 +        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
 +        {
 +            int flags = in.readUnsignedByte();
 +            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 +            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 +            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 +            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 +            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 +
 +            long timestamp = useRowTimestamp ? rowLiveness.timestamp() : header.readTimestamp(in);
 +
 +            int localDeletionTime = useRowTTL
 +                                    ? rowLiveness.localExpirationTime()
 +                                    : (isDeleted || isExpiring ? header.readLocalDeletionTime(in) : NO_DELETION_TIME);
 +
 +            int ttl = useRowTTL ? rowLiveness.ttl() : (isExpiring ? header.readTTL(in) : NO_TTL);
  
 -        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException;
 +            CellPath path = column.isComplex()
 +                            ? column.cellPathSerializer().deserialize(in)
 +                            : null;
  
 -        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header);
 +            ByteBuffer value = ByteBufferUtil.EMPTY_BYTE_BUFFER;
 +            if (hasValue)
 +            {
 +                if (helper.canSkipValue(column) || (path != null && helper.canSkipValue(path)))
 +                {
 +                    header.getType(column).skipValue(in);
 +                }
 +                else
 +                {
 +                    boolean isCounter = localDeletionTime == NO_DELETION_TIME && column.type.isCounter();
 +
 +                    value = header.getType(column).readValue(in, DatabaseDescriptor.getMaxValueSize());
 +                    if (isCounter)
 +                        value = helper.maybeClearCounterValue(value);
 +                }
 +            }
 +
 +            return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
 +        }
 +
-         public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header)
++        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
 +        {
 +            long size = 1; // flags
 +            boolean hasValue = cell.value().hasRemaining();
 +            boolean isDeleted = cell.isTombstone();
 +            boolean isExpiring = cell.isExpiring();
 +            boolean useRowTimestamp = !rowLiveness.isEmpty() && cell.timestamp() == rowLiveness.timestamp();
 +            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == rowLiveness.localExpirationTime();
 +
 +            if (!useRowTimestamp)
 +                size += header.timestampSerializedSize(cell.timestamp());
 +
 +            if ((isDeleted || isExpiring) && !useRowTTL)
 +                size += header.localDeletionTimeSerializedSize(cell.localDeletionTime());
 +            if (isExpiring && !useRowTTL)
 +                size += header.ttlSerializedSize(cell.ttl());
 +
-             if (cell.column().isComplex())
-                 size += cell.column().cellPathSerializer().serializedSize(cell.path());
++            if (column.isComplex())
++                size += column.cellPathSerializer().serializedSize(cell.path());
 +
 +            if (hasValue)
-                 size += header.getType(cell.column()).writtenLength(cell.value());
++                size += header.getType(column).writtenLength(cell.value());
 +
 +            return size;
 +        }
  
          // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
 -        public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException;
 +        public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException
 +        {
 +            int flags = in.readUnsignedByte();
 +            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 +            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 +            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 +            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 +            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 +
 +            if (!useRowTimestamp)
 +                header.skipTimestamp(in);
 +
 +            if (!useRowTTL && (isDeleted || isExpiring))
 +                header.skipLocalDeletionTime(in);
 +
 +            if (!useRowTTL && isExpiring)
 +                header.skipTTL(in);
 +
 +            if (column.isComplex())
 +                column.cellPathSerializer().skip(in);
 +
 +            if (hasValue)
 +                header.getType(column).skipValue(in);
 +
 +            return true;
 +        }
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
index 890806c,dc6f187..5ca7e03
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
@@@ -25,8 -24,8 +25,9 @@@ import net.nicoulaj.compilecommand.anno
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.io.util.DataInputPlus;
 +import org.apache.cassandra.io.util.DataOutputBuffer;
  import org.apache.cassandra.io.util.DataOutputPlus;
+ import org.apache.cassandra.utils.SearchIterator;
  
  /**
   * Serialize/deserialize a single Unfiltered (both on-wire and on-disk).
@@@ -222,15 -175,24 +223,24 @@@ public class UnfilteredSerialize
          if ((flags & HAS_DELETION) != 0)
              header.writeDeletionTime(deletion.time(), out);
  
 -        if (!hasAllColumns)
 +        if ((flags & HAS_ALL_COLUMNS) == 0)
              Columns.serializer.serializeSubset(Collections2.transform(row, ColumnData::column), headerColumns, out);
  
+         SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
          for (ColumnData data : row)
          {
+             // We can obtain the column for data directly from data.column(). However, if the cell/complex data
+             // originates from a sstable, the column we'll get will have the type used when the sstable was serialized,
+             // and if that type have been recently altered, that may not be the type we want to serialize the column
+             // with. So we use the ColumnDefinition from the "header" which is "current". Also see #11810 for what
+             // happens if we don't do that.
+             ColumnDefinition column = si.next(data.column());
+             assert column != null;
+ 
              if (data.column.isSimple())
-                 Cell.serializer.serialize((Cell) data, out, pkLiveness, header);
+                 Cell.serializer.serialize((Cell) data, column, out, pkLiveness, header);
              else
-                 writeComplexColumn((ComplexColumnData) data, (flags & HAS_COMPLEX_DELETION) != 0, pkLiveness, header, out);
 -                writeComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header, out);
++                writeComplexColumn((ComplexColumnData) data, column, (flags & HAS_COMPLEX_DELETION) != 0, pkLiveness, header, out);
          }
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 3eb55fd,509aeac..bb4bf48
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@@ -325,84 -323,19 +323,100 @@@ public class AlterTest extends CQLTeste
          assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s ALTER column1 TYPE ascii");
      }
  
 +    /*
 +     * Test case to check addition of one column
 +    */
 +    @Test
 +    public void testAlterAddOneColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id int, name text, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s add mail text;");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "mail", "name");
 +    }
 +
 +    /*
 +     * Test case to check addition of more than one column
 +     */
 +    @Test
 +    public void testAlterAddMultiColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id int, yearofbirth int, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s add (firstname text, password blob, lastname text, \"SOME escaped col\" bigint)");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "SOME escaped col", "firstname", "lastname", "password", "yearofbirth");
 +    }
 +
 +    /*
 +     *  Should throw SyntaxException if multiple columns are added using wrong syntax.
 +     *  Expected Syntax : Alter table T1 add (C1 datatype,C2 datatype,C3 datatype)
 +     */
 +    @Test(expected = SyntaxException.class)
 +    public void testAlterAddMultiColumnWithoutBraces() throws Throwable
 +    {
 +        execute("ALTER TABLE %s.users add lastname text, password blob, yearofbirth int;");
 +    }
 +
 +    /*
 +     *  Test case to check deletion of one column
 +     */
 +    @Test
 +    public void testAlterDropOneColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s drop telephone");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth");
 +    }
 +
 +    @Test
 +    /*
 +     * Test case to check deletion of more than one column
 +     */
 +    public void testAlterDropMultiColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, \"SOME escaped col\" bigint, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s drop (address, telephone, \"SOME escaped col\");");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth");
 +    }
 +
 +    /*
 +     *  Should throw SyntaxException if multiple columns are dropped using wrong syntax.
 +     */
 +    @Test(expected = SyntaxException.class)
 +    public void testAlterDeletionColumnWithoutBraces() throws Throwable
 +    {
 +        execute("ALTER TABLE %s.users drop name,address;");
 +    }
 +
 +    @Test(expected = InvalidRequestException.class)
 +    public void testAlterAddDuplicateColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        execute("ALTER TABLE %s add (salary int, salary int);");
 +    }
 +
 +    @Test(expected = InvalidRequestException.class)
 +    public void testAlterDropDuplicateColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        execute("ALTER TABLE %s drop (address, address);");
 +    }
++
+     @Test
+     public void testAlterToBlob() throws Throwable
+     {
+         // This tests for the bug from #11820 in particular
+ 
+         createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
+ 
+         execute("INSERT INTO %s (a, b) VALUES (1, 1)");
+ 
+         executeNet(Server.CURRENT_VERSION, "ALTER TABLE %s ALTER b TYPE BLOB");
+ 
+         assertRowsNet(Server.CURRENT_VERSION, executeNet(Server.CURRENT_VERSION, "SELECT * FROM %s WHERE a = 1"),
+             row(1, ByteBufferUtil.bytes(1))
+         );
+     }
  }


[6/6] 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:
  Avoid deserialization error after altering column type


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

Branch: refs/heads/trunk
Commit: 9242c85cf48bbfe7e46fde180576315370c66b0c
Parents: e423527 1f014b2
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Thu Jun 30 11:16:17 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Jun 30 11:16:17 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  2 +
 .../apache/cassandra/db/rows/BufferCell.java    |  1 -
 src/java/org/apache/cassandra/db/rows/Cell.java | 16 +++----
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 97 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/9242c85c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 81740ab,ed884a9..6068b14
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,7 -1,6 +1,9 @@@
 +3.10
 + * Remove pre-startup check for open JMX port (CASSANDRA-12074)
 +
  3.9
+ Merged from 3.0:
+  * Fix EOF exception when altering column type (CASSANDRA-11820)
  Merged from 2.2:
   * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
  Merged from 2.1:


[3/6] cassandra git commit: Avoid deserialization error after altering column type

Posted by sl...@apache.org.
Avoid deserialization error after altering column type

This makes sure the column used when serializing intra-node messages is
"current". Previously, we would use the type used during deserialization
which could not be "current" due to an ALTER TYPE.

patch by slebresne; reviewed by thobbs for CASSANDRA-11820


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

Branch: refs/heads/trunk
Commit: e4c344c58f8ca8f69224855080de4ec266fb671e
Parents: 362e132
Author: Sylvain Lebresne <sy...@datastax.com>
Authored: Mon Jun 27 14:17:27 2016 +0200
Committer: Sylvain Lebresne <sy...@datastax.com>
Committed: Thu Jun 30 11:14:01 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../apache/cassandra/db/rows/BufferCell.java    | 16 +++----
 src/java/org/apache/cassandra/db/rows/Cell.java |  4 +-
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 98 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ae37d2c..573f704 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.9
+ * Fix EOF exception when altering column type (CASSANDRA-11820)
 Merged from 2.2:
  * MemoryUtil.getShort() should return an unsigned short also for architectures not supporting unaligned memory accesses (CASSANDRA-11973)
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/BufferCell.java b/src/java/org/apache/cassandra/db/rows/BufferCell.java
index 22b629a..db0ded5 100644
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@ -228,7 +228,7 @@ public class BufferCell extends AbstractCell
         private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // Wether the cell has the same timestamp than the row this is a cell of.
         private final static int USE_ROW_TTL_MASK            = 0x10; // Wether the cell has the same ttl than the row this is a cell of.
 
-        public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
+        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException
         {
             assert cell != null;
             boolean hasValue = cell.value().hasRemaining();
@@ -260,11 +260,11 @@ public class BufferCell extends AbstractCell
             if (isExpiring && !useRowTTL)
                 header.writeTTL(cell.ttl(), out);
 
-            if (cell.column().isComplex())
-                cell.column().cellPathSerializer().serialize(cell.path(), out);
+            if (column.isComplex())
+                column.cellPathSerializer().serialize(cell.path(), out);
 
             if (hasValue)
-                header.getType(cell.column()).writeValue(cell.value(), out);
+                header.getType(column).writeValue(cell.value(), out);
         }
 
         public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException
@@ -308,7 +308,7 @@ public class BufferCell extends AbstractCell
             return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path);
         }
 
-        public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header)
+        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header)
         {
             long size = 1; // flags
             boolean hasValue = cell.value().hasRemaining();
@@ -325,11 +325,11 @@ public class BufferCell extends AbstractCell
             if (isExpiring && !useRowTTL)
                 size += header.ttlSerializedSize(cell.ttl());
 
-            if (cell.column().isComplex())
-                size += cell.column().cellPathSerializer().serializedSize(cell.path());
+            if (column.isComplex())
+                size += column.cellPathSerializer().serializedSize(cell.path());
 
             if (hasValue)
-                size += header.getType(cell.column()).writtenLength(cell.value());
+                size += header.getType(column).writtenLength(cell.value());
 
             return size;
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Cell.java b/src/java/org/apache/cassandra/db/rows/Cell.java
index b10ce06..d10cc74 100644
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@ -145,11 +145,11 @@ public abstract class Cell extends ColumnData
 
     public interface Serializer
     {
-        public void serialize(Cell cell, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
+        public void serialize(Cell cell, ColumnDefinition column, DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) throws IOException;
 
         public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, ColumnDefinition column, SerializationHeader header, SerializationHelper helper) throws IOException;
 
-        public long serializedSize(Cell cell, LivenessInfo rowLiveness, SerializationHeader header);
+        public long serializedSize(Cell cell, ColumnDefinition column, LivenessInfo rowLiveness, SerializationHeader header);
 
         // Returns if the skipped cell was an actual cell (i.e. it had its presence flag).
         public boolean skip(DataInputPlus in, ColumnDefinition column, SerializationHeader header) throws IOException;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
index e4202c9..dc6f187 100644
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
@@ -25,6 +25,7 @@ import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.io.util.DataInputPlus;
 import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.utils.SearchIterator;
 
 /**
  * Serialize/deserialize a single Unfiltered (both on-wire and on-disk).
@@ -177,16 +178,25 @@ public class UnfilteredSerializer
         if (!hasAllColumns)
             Columns.serializer.serializeSubset(Collections2.transform(row, ColumnData::column), headerColumns, out);
 
+        SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
         for (ColumnData data : row)
         {
+            // We can obtain the column for data directly from data.column(). However, if the cell/complex data
+            // originates from a sstable, the column we'll get will have the type used when the sstable was serialized,
+            // and if that type have been recently altered, that may not be the type we want to serialize the column
+            // with. So we use the ColumnDefinition from the "header" which is "current". Also see #11810 for what
+            // happens if we don't do that.
+            ColumnDefinition column = si.next(data.column());
+            assert column != null;
+
             if (data.column.isSimple())
-                Cell.serializer.serialize((Cell) data, out, pkLiveness, header);
+                Cell.serializer.serialize((Cell) data, column, out, pkLiveness, header);
             else
-                writeComplexColumn((ComplexColumnData) data, hasComplexDeletion, pkLiveness, header, out);
+                writeComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header, out);
         }
     }
 
-    private void writeComplexColumn(ComplexColumnData data, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header, DataOutputPlus out)
+    private void writeComplexColumn(ComplexColumnData data, ColumnDefinition column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header, DataOutputPlus out)
     throws IOException
     {
         if (hasComplexDeletion)
@@ -194,7 +204,7 @@ public class UnfilteredSerializer
 
         out.writeUnsignedVInt(data.cellsCount());
         for (Cell cell : data)
-            Cell.serializer.serialize(cell, out, rowLiveness, header);
+            Cell.serializer.serialize(cell, column, out, rowLiveness, header);
     }
 
     private void serialize(RangeTombstoneMarker marker, SerializationHeader header, DataOutputPlus out, long previousUnfilteredSize, int version)
@@ -274,18 +284,22 @@ public class UnfilteredSerializer
         if (!hasAllColumns)
             size += Columns.serializer.serializedSubsetSize(Collections2.transform(row, ColumnData::column), header.columns(isStatic));
 
+        SearchIterator<ColumnDefinition, ColumnDefinition> si = headerColumns.iterator();
         for (ColumnData data : row)
         {
+            ColumnDefinition column = si.next(data.column());
+            assert column != null;
+
             if (data.column.isSimple())
-                size += Cell.serializer.serializedSize((Cell) data, pkLiveness, header);
+                size += Cell.serializer.serializedSize((Cell) data, column, pkLiveness, header);
             else
-                size += sizeOfComplexColumn((ComplexColumnData) data, hasComplexDeletion, pkLiveness, header);
+                size += sizeOfComplexColumn((ComplexColumnData) data, column, hasComplexDeletion, pkLiveness, header);
         }
 
         return size;
     }
 
-    private long sizeOfComplexColumn(ComplexColumnData data, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header)
+    private long sizeOfComplexColumn(ComplexColumnData data, ColumnDefinition column, boolean hasComplexDeletion, LivenessInfo rowLiveness, SerializationHeader header)
     {
         long size = 0;
 
@@ -294,7 +308,7 @@ public class UnfilteredSerializer
 
         size += TypeSizes.sizeofUnsignedVInt(data.cellsCount());
         for (Cell cell : data)
-            size += Cell.serializer.serializedSize(cell, rowLiveness, header);
+            size += Cell.serializer.serializedSize(cell, column, rowLiveness, header);
 
         return size;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index fe03db4..a213edf 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -131,6 +131,7 @@ public abstract class CQLTester
 
     public static ResultMessage lastSchemaChangeResult;
 
+    private List<String> keyspaces = new ArrayList<>();
     private List<String> tables = new ArrayList<>();
     private List<String> types = new ArrayList<>();
     private List<String> functions = new ArrayList<>();
@@ -262,10 +263,12 @@ public abstract class CQLTester
         usePrepared = USE_PREPARED_VALUES;
         reusePrepared = REUSE_PREPARED;
 
+        final List<String> keyspacesToDrop = copy(keyspaces);
         final List<String> tablesToDrop = copy(tables);
         final List<String> typesToDrop = copy(types);
         final List<String> functionsToDrop = copy(functions);
         final List<String> aggregatesToDrop = copy(aggregates);
+        keyspaces = null;
         tables = null;
         types = null;
         functions = null;
@@ -290,6 +293,9 @@ public abstract class CQLTester
                     for (int i = typesToDrop.size() - 1; i >= 0; i--)
                         schemaChange(String.format("DROP TYPE IF EXISTS %s.%s", KEYSPACE, typesToDrop.get(i)));
 
+                    for (int i = keyspacesToDrop.size() - 1; i >= 0; i--)
+                        schemaChange(String.format("DROP KEYSPACE IF EXISTS %s", keyspacesToDrop.get(i)));
+
                     // Dropping doesn't delete the sstables. It's not a huge deal but it's cleaner to cleanup after us
                     // Thas said, we shouldn't delete blindly before the TransactionLogs.SSTableTidier for the table we drop
                     // have run or they will be unhappy. Since those taks are scheduled on StorageService.tasks and that's
@@ -501,6 +507,22 @@ public abstract class CQLTester
         schemaChange(fullQuery);
     }
 
+    protected String createKeyspace(String query)
+    {
+        String currentKeyspace = createKeyspaceName();
+        String fullQuery = String.format(query, currentKeyspace);
+        logger.info(fullQuery);
+        schemaChange(fullQuery);
+        return currentKeyspace;
+    }
+
+    protected String createKeyspaceName()
+    {
+        String currentKeyspace = "keyspace_" + seqNumber.getAndIncrement();
+        keyspaces.add(currentKeyspace);
+        return currentKeyspace;
+    }
+
     protected String createTable(String query)
     {
         String currentTable = createTableName();
@@ -519,8 +541,7 @@ public abstract class CQLTester
 
     protected void createTableMayThrow(String query) throws Throwable
     {
-        String currentTable = "table_" + seqNumber.getAndIncrement();
-        tables.add(currentTable);
+        String currentTable = createTableName();
         String fullQuery = formatQuery(query);
         logger.info(fullQuery);
         QueryProcessor.executeOnceInternal(fullQuery);
@@ -825,6 +846,16 @@ public abstract class CQLTester
      */
     public static void assertRowsIgnoringOrder(UntypedResultSet result, Object[]... rows)
     {
+        assertRowsIgnoringOrderInternal(result, false, rows);
+    }
+
+    public static void assertRowsIgnoringOrderAndExtra(UntypedResultSet result, Object[]... rows)
+    {
+        assertRowsIgnoringOrderInternal(result, true, rows);
+    }
+
+    private static void assertRowsIgnoringOrderInternal(UntypedResultSet result, boolean ignoreExtra, Object[]... rows)
+    {
         if (result == null)
         {
             if (rows.length > 0)
@@ -855,7 +886,7 @@ public abstract class CQLTester
 
         com.google.common.collect.Sets.SetView<List<ByteBuffer>> extra = com.google.common.collect.Sets.difference(actualRows, expectedRows);
         com.google.common.collect.Sets.SetView<List<ByteBuffer>> missing = com.google.common.collect.Sets.difference(expectedRows, actualRows);
-        if (!extra.isEmpty() || !missing.isEmpty())
+        if ((!ignoreExtra && !extra.isEmpty()) || !missing.isEmpty())
         {
             List<String> extraRows = makeRowStrings(extra, meta);
             List<String> missingRows = makeRowStrings(missing, meta);
@@ -876,7 +907,7 @@ public abstract class CQLTester
                 Assert.fail("Missing " + missing.size() + " row(s) in result: \n    " + missingRows.stream().collect(Collectors.joining("\n    ")));
         }
 
-        assert expectedRows.size() == actualRows.size();
+        assert ignoreExtra || expectedRows.size() == actualRows.size();
     }
 
     private static List<String> makeRowStrings(Iterable<List<ByteBuffer>> rows, List<ColumnSpecification> meta)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e4c344c5/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 9f8bea2..509aeac 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@ -24,6 +24,8 @@ import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.schema.SchemaKeyspace;
+import org.apache.cassandra.transport.Server;
+import org.apache.cassandra.utils.ByteBufferUtil;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -130,37 +132,33 @@ public class AlterTest extends CQLTester
         assertInvalidThrow(SyntaxException.class, "CREATE KEYSPACE ks1");
         assertInvalidThrow(ConfigurationException.class, "CREATE KEYSPACE ks1 WITH replication= { 'replication_factor' : 1 }");
 
-        execute("CREATE KEYSPACE ks1 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
-        execute("CREATE KEYSPACE ks2 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false");
+        String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
+        String ks2 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND durable_writes=false");
 
-        assertRows(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"),
-                   row("ks1", true),
+        assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes FROM system_schema.keyspaces"),
                    row(KEYSPACE, true),
                    row(KEYSPACE_PER_TEST, true),
-                   row("ks2", false));
+                   row(ks1, true),
+                   row(ks2, false));
 
-        execute("ALTER KEYSPACE ks1 WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dc1' : 1 } AND durable_writes=False");
-        execute("ALTER KEYSPACE ks2 WITH durable_writes=true");
+        schemaChange("ALTER KEYSPACE " + ks1 + " WITH replication = { 'class' : 'NetworkTopologyStrategy', 'dc1' : 1 } AND durable_writes=False");
+        schemaChange("ALTER KEYSPACE " + ks2 + " WITH durable_writes=true");
 
-        assertRows(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"),
-                   row("ks1", false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", "dc1", "1")),
+        assertRowsIgnoringOrderAndExtra(execute("SELECT keyspace_name, durable_writes, replication FROM system_schema.keyspaces"),
                    row(KEYSPACE, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")),
                    row(KEYSPACE_PER_TEST, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")),
-                   row("ks2", true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")));
+                   row(ks1, false, map("class", "org.apache.cassandra.locator.NetworkTopologyStrategy", "dc1", "1")),
+                   row(ks2, true, map("class", "org.apache.cassandra.locator.SimpleStrategy", "replication_factor", "1")));
 
-        execute("USE ks1");
+        execute("USE " + ks1);
 
         assertInvalidThrow(ConfigurationException.class, "CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'min_threshold' : 4 }");
 
         execute("CREATE TABLE cf1 (a int PRIMARY KEY, b int) WITH compaction = { 'class' : 'SizeTieredCompactionStrategy', 'min_threshold' : 7 }");
-        assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='ks1'"),
+        assertRows(execute("SELECT table_name, compaction FROM system_schema.tables WHERE keyspace_name='" + ks1 + "'"),
                    row("cf1", map("class", "org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy",
                                   "min_threshold", "7",
                                   "max_threshold", "32")));
-
-        // clean-up
-        execute("DROP KEYSPACE ks1");
-        execute("DROP KEYSPACE ks2");
     }
 
     /**
@@ -324,4 +322,20 @@ public class AlterTest extends CQLTester
         createTable("CREATE TABLE %s (key blob, column1 blob, value blob, PRIMARY KEY ((key), column1)) WITH COMPACT STORAGE");
         assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s ALTER column1 TYPE ascii");
     }
+
+    @Test
+    public void testAlterToBlob() throws Throwable
+    {
+        // This tests for the bug from #11820 in particular
+
+        createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
+
+        execute("INSERT INTO %s (a, b) VALUES (1, 1)");
+
+        executeNet(Server.CURRENT_VERSION, "ALTER TABLE %s ALTER b TYPE BLOB");
+
+        assertRowsNet(Server.CURRENT_VERSION, executeNet(Server.CURRENT_VERSION, "SELECT * FROM %s WHERE a = 1"),
+            row(1, ByteBufferUtil.bytes(1))
+        );
+    }
 }