You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2017/08/24 16:24:31 UTC

[1/8] cassandra git commit: Copy session properties on cqlsh.py do_login

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.0 1a2ad2ee0 -> cf0b6d107
  refs/heads/cassandra-3.11 000007f67 -> 8d98a9923
  refs/heads/trunk 41ef972b1 -> 4838e81a6


Copy session properties on cqlsh.py do_login

patch by Andres de la Peña; reviewed by Zhao Yang for CASSANDRA-13640


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

Branch: refs/heads/cassandra-3.11
Commit: 1a2ad2ee047a9ed2c5112376585323b27ebfb7b0
Parents: 5e57dd1
Author: Andrés de la Peña <a....@gmail.com>
Authored: Thu Aug 24 16:56:12 2017 +0100
Committer: Andrés de la Peña <a....@gmail.com>
Committed: Thu Aug 24 16:56:12 2017 +0100

----------------------------------------------------------------------
 CHANGES.txt  | 1 +
 bin/cqlsh.py | 6 ++++++
 2 files changed, 7 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1a2ad2ee/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 2b49bc3..a35cc1b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
  * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
  * Don't let stress write warmup data if n=0 (CASSANDRA-13773)
  * Gossip thread slows down when using batch commit log (CASSANDRA-12966)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1a2ad2ee/bin/cqlsh.py
----------------------------------------------------------------------
diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index c132592..8eb42a3 100644
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -2206,6 +2206,12 @@ class Shell(cmd.Cmd):
         else:
             session = conn.connect()
 
+        # Copy session properties
+        session.default_timeout = self.session.default_timeout
+        session.row_factory = self.session.row_factory
+        session.default_consistency_level = self.session.default_consistency_level
+        session.max_trace_wait = self.session.max_trace_wait
+
         # Update after we've connected in case we fail to authenticate
         self.conn = conn
         self.auth_provider = auth_provider


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[5/8] cassandra git commit: Fix the merging of cells with different user type versions

Posted by bl...@apache.org.
Fix the merging of cells with different user type versions

patch by Benjamin Lerer; reviewed by Robert Stupp  for CASSANDRA-13776


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

Branch: refs/heads/cassandra-3.0
Commit: cf0b6d107bade419dada49a5da40d2579c80ade8
Parents: 1a2ad2e
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Aug 24 18:19:28 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Aug 24 18:19:28 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/db/SerializationHeader.java       |  13 +-
 .../cassandra/db/marshal/AbstractType.java      |  10 +
 .../apache/cassandra/db/marshal/TupleType.java  |   5 +
 .../apache/cassandra/db/marshal/UserType.java   |  10 +
 .../db/rows/AbstractTypeVersionComparator.java  | 121 ++++++++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  18 +-
 src/java/org/apache/cassandra/db/rows/Rows.java |  21 ++-
 .../io/sstable/format/SSTableReader.java        |  23 +--
 .../org/apache/cassandra/cql3/CQLTester.java    |  20 +-
 .../cql3/validation/entities/UserTypesTest.java | 151 +++++++++++++++
 .../rows/AbstractTypeVersionComparatorTest.java | 184 +++++++++++++++++++
 12 files changed, 555 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index a35cc1b..ba35152 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Fix the merging of cells with different user type versions (CASSANDRA-13776)
  * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
  * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
  * Don't let stress write warmup data if n=0 (CASSANDRA-13773)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/SerializationHeader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SerializationHeader.java b/src/java/org/apache/cassandra/db/SerializationHeader.java
index 19dad95..494c2a3 100644
--- a/src/java/org/apache/cassandra/db/SerializationHeader.java
+++ b/src/java/org/apache/cassandra/db/SerializationHeader.java
@@ -108,7 +108,8 @@ public class SerializationHeader
         // but rather on their stats stored in StatsMetadata that are fully accurate.
         EncodingStats.Collector stats = new EncodingStats.Collector();
         PartitionColumns.Builder columns = PartitionColumns.builder();
-        for (SSTableReader sstable : sstables)
+        // We need to order the SSTables by descending generation to be sure that we use latest column definitions.
+        for (SSTableReader sstable : orderByDescendingGeneration(sstables))
         {
             stats.updateTimestamp(sstable.getMinTimestamp());
             stats.updateLocalDeletionTime(sstable.getMinLocalDeletionTime());
@@ -121,6 +122,16 @@ public class SerializationHeader
         return new SerializationHeader(true, metadata, columns.build(), stats.get());
     }
 
+    private static Collection<SSTableReader> orderByDescendingGeneration(Collection<SSTableReader> sstables)
+    {
+        if (sstables.size() < 2)
+            return sstables;
+
+        List<SSTableReader> readers = new ArrayList<>(sstables);
+        readers.sort(SSTableReader.generationReverseComparator);
+        return readers;
+    }
+
     public SerializationHeader(boolean isForSSTable,
                                CFMetaData metadata,
                                PartitionColumns columns,

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 77e0971..20062bd 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -325,6 +325,16 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
         return false;
     }
 
+    public boolean isTuple()
+    {
+        return false;
+    }
+
+    public boolean isUDT()
+    {
+        return false;
+    }
+
     public AbstractType<?> freeze()
     {
         return this;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java
index 2d6363e..5c74332 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -347,4 +347,9 @@ public class TupleType extends AbstractType<ByteBuffer>
     {
         return getClass().getName() + TypeParser.stringifyTypeParameters(types, true);
     }
+
+    public boolean isTuple()
+    {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java
index b91dbf8..03545ca 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -232,4 +232,14 @@ public class UserType extends TupleType
     {
         return serializer;
     }
+
+    public boolean isTuple()
+    {
+        return false;
+    }
+
+    public boolean isUDT()
+    {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java b/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
new file mode 100644
index 0000000..e47f681
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.db.rows;
+
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.cassandra.db.marshal.*;
+
+/**
+ * A {@code Comparator} use to determine which version of a type should be used.
+ * <p>In the case of UDTs it is possible to have 2 versions or more of the same type, if some fields has been added to
+ * the type. To avoid problems the latest type need to be used.</p>
+ */
+final class AbstractTypeVersionComparator implements Comparator<AbstractType<?>>
+{
+    public static final Comparator<AbstractType<?>> INSTANCE = new AbstractTypeVersionComparator();
+
+    private AbstractTypeVersionComparator()
+    {
+    }
+
+    @Override
+    public int compare(AbstractType<?> type, AbstractType<?> otherType)
+    {
+        if (!type.getClass().equals(otherType.getClass()))
+            throw new IllegalArgumentException(String.format("Trying to compare 2 different types: %s and %s",
+                                                             type,
+                                                             otherType));
+
+        if (type.equals(otherType))
+            return 0;
+
+        // The only case where 2 types can differ is if they contains some UDTs and one of them has more
+        // fields (due to an ALTER type ADD) than in the other type. In this case we need to pick the type with
+        // the bigger amount of fields.
+        if (type.isUDT())
+            return compareUserType((UserType) type, (UserType) otherType);
+
+        if (type.isTuple())
+            return compareTuple((TupleType) type, (TupleType) otherType);
+
+        if (type.isCollection())
+            return compareCollectionTypes(type, otherType);
+
+        if (type instanceof CompositeType)
+            return compareCompositeTypes((CompositeType) type, (CompositeType) otherType);
+
+        // In theory we should never reach that point but to be on the safe side we allow it.
+        return 0;
+    }
+
+    private int compareCompositeTypes(CompositeType type, CompositeType otherType)
+    {
+        List<AbstractType<?>> types = type.getComponents();
+        List<AbstractType<?>> otherTypes = otherType.getComponents();
+
+        if (types.size() != otherTypes.size())
+            return Integer.compare(types.size(), otherTypes.size());
+
+        for (int i = 0, m = type.componentsCount(); i < m ; i++)
+        {
+            int test = compare(types.get(i), otherTypes.get(i));
+            if (test != 0);
+                return test;
+        }
+        return 0;
+    }
+
+    private int compareCollectionTypes(AbstractType<?> type, AbstractType<?> otherType)
+    {
+        if (type instanceof MapType)
+            return compareMapType((MapType<?, ?>) type, (MapType<?, ?>) otherType);
+
+        if (type instanceof SetType)
+            return compare(((SetType<?>) type).getElementsType(), ((SetType<?>) otherType).getElementsType());
+
+        return compare(((ListType<?>) type).getElementsType(), ((ListType<?>) otherType).getElementsType());
+    }
+
+    private int compareMapType(MapType<?, ?> type, MapType<?, ?> otherType)
+    {
+        int test = compare(type.getKeysType(), otherType.getKeysType());
+        return test != 0 ? test : compare(type.getValuesType(), otherType.getValuesType());
+    }
+
+    private int compareUserType(UserType type, UserType otherType)
+    {
+        return compareTuple(type, otherType);
+    }
+
+    private int compareTuple(TupleType type, TupleType otherType)
+    {
+        if (type.size() != otherType.size())
+            return Integer.compare(type.size(), otherType.size());
+
+        int test = 0;
+        int i = 0;
+        while (test == 0 && i < type.size())
+        {
+            test = compare(type.type(i), otherType.type(i));
+            i++;
+        }
+        return test;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Row.java b/src/java/org/apache/cassandra/db/rows/Row.java
index a61f365..9ab1f09 100644
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@ -601,10 +601,25 @@ public interface Row extends Unfiltered, Collection<ColumnData>
 
             public void reduce(int idx, ColumnData data)
             {
-                column = data.column();
+                if (useColumnDefinition(data.column()))
+                    column = data.column();
+
                 versions.add(data);
             }
 
+            /**
+             * Determines it the {@code ColumnDefinition} is the one that should be used.
+             * @param dataColumn the {@code ColumnDefinition} to use.
+             * @return {@code true} if the {@code ColumnDefinition} is the one that should be used, {@code false} otherwise.
+             */
+            private boolean useColumnDefinition(ColumnDefinition dataColumn)
+            {
+                if (column == null)
+                    return true;
+
+                return AbstractTypeVersionComparator.INSTANCE.compare(column.type, dataColumn.type) < 0;
+            }
+
             protected ColumnData getReduced()
             {
                 if (column.isSimple())
@@ -654,6 +669,7 @@ public interface Row extends Unfiltered, Collection<ColumnData>
 
             protected void onKeyChange()
             {
+                column = null;
                 versions.clear();
             }
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Rows.java b/src/java/org/apache/cassandra/db/rows/Rows.java
index e325091..09213a4 100644
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@ -279,7 +279,8 @@ public abstract class Rows
             int comparison = nexta == null ? 1 : nextb == null ? -1 : nexta.column.compareTo(nextb.column);
             ColumnData cura = comparison <= 0 ? nexta : null;
             ColumnData curb = comparison >= 0 ? nextb : null;
-            ColumnDefinition column = (cura != null ? cura : curb).column;
+            ColumnDefinition column = getColumnDefinition(cura, curb);
+
             if (column.isSimple())
             {
                 timeDelta = Math.min(timeDelta, Cells.reconcile((Cell) cura, (Cell) curb, deletion, builder, nowInSec));
@@ -309,4 +310,22 @@ public abstract class Rows
         }
         return timeDelta;
     }
+
+    /**
+     * Returns the {@code ColumnDefinition} to use for merging the columns.
+     * If the 2 column definitions are different the latest one will be returned.
+     */
+    private static ColumnDefinition getColumnDefinition(ColumnData cura, ColumnData curb)
+    {
+        if (cura == null)
+            return curb.column;
+
+        if (curb == null)
+            return cura.column;
+
+        if (AbstractTypeVersionComparator.INSTANCE.compare(cura.column.type, curb.column.type) >= 0)
+            return cura.column;
+
+        return curb.column;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
index f38738d..cd41b5b 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
@@ -45,7 +45,6 @@ import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.commitlog.ReplayPosition;
 import org.apache.cassandra.db.filter.ColumnFilter;
 import org.apache.cassandra.db.rows.SliceableUnfilteredRowIterator;
 import org.apache.cassandra.dht.AbstractBounds;
@@ -141,26 +140,14 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
     }
     private static final RateLimiter meterSyncThrottle = RateLimiter.create(100.0);
 
-    public static final Comparator<SSTableReader> maxTimestampComparator = new Comparator<SSTableReader>()
-    {
-        public int compare(SSTableReader o1, SSTableReader o2)
-        {
-            long ts1 = o1.getMaxTimestamp();
-            long ts2 = o2.getMaxTimestamp();
-            return (ts1 > ts2 ? -1 : (ts1 == ts2 ? 0 : 1));
-        }
-    };
+    public static final Comparator<SSTableReader> maxTimestampComparator = (o1, o2) -> Long.compare(o1.getMaxTimestamp(), o2.getMaxTimestamp());
 
     // it's just an object, which we use regular Object equality on; we introduce a special class just for easy recognition
     public static final class UniqueIdentifier {}
 
-    public static final Comparator<SSTableReader> sstableComparator = new Comparator<SSTableReader>()
-    {
-        public int compare(SSTableReader o1, SSTableReader o2)
-        {
-            return o1.first.compareTo(o2.first);
-        }
-    };
+    public static final Comparator<SSTableReader> sstableComparator = (o1, o2) -> o1.first.compareTo(o2.first);
+
+    public static final Comparator<SSTableReader> generationReverseComparator = (o1, o2) -> -Integer.compare(o1.descriptor.generation, o2.descriptor.generation);
 
     public static final Ordering<SSTableReader> sstableOrdering = Ordering.from(sstableComparator);
 
@@ -1717,7 +1704,7 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
     /**
      * Direct I/O SSTableScanner over an iterator of bounds.
      *
-     * @param bounds the keys to cover
+     * @param rangeIterator the keys to cover
      * @return A Scanner for seeking over the rows of the SSTable.
      */
     public abstract ISSTableScanner getScanner(Iterator<AbstractBounds<PartitionPosition>> rangeIterator);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/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 ba23c67..40aec88 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -396,7 +396,8 @@ public abstract class CQLTester
     public void disableCompaction(String keyspace)
     {
         ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
-        store.disableAutoCompaction();
+        if (store != null)
+            store.disableAutoCompaction();
     }
 
     public void flush(boolean forceFlush)
@@ -437,6 +438,23 @@ public abstract class CQLTester
         }
     }
 
+    public void disableCompaction()
+    {
+        disableCompaction(KEYSPACE);
+    }
+
+    public void enableCompaction(String keyspace)
+    {
+        ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
+        if (store != null)
+            store.enableAutoCompaction();
+    }
+
+    public void enableCompaction()
+    {
+        enableCompaction(KEYSPACE);
+    }
+
     public void cleanupCache()
     {
         ColumnFamilyStore store = getCurrentColumnFamilyStore();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index c279e00..dfc2e5e 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -562,6 +562,157 @@ public class UserTypesTest extends CQLTester
         assertInvalidMessage("Cannot drop user type " + typeWithKs(t), "DROP TYPE " + typeWithKs(t) + ';');
     }
 
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinSet() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y set<frozen<" + columnType + ">>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType(1), userType(2)));
+            assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1), userType(2))));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, set(userType(1), userType(2))));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    set(userType(1, 1), userType(1, 2), userType(2, 1)));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, set(userType(1),
+                                      userType(1, 1),
+                                      userType(1, 2),
+                                      userType(2),
+                                      userType(2, 1))));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, set(userType(1),
+                                  userType(1, 1),
+                                  userType(1, 2),
+                                  userType(2),
+                                  userType(2, 1))));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinMap() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y map<frozen<" + columnType + ">, int>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType(1), 1, userType(2), 2));
+            assertRows(execute("SELECT * FROM %s"), row(1, map(userType(1), 1, userType(2), 2)));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, map(userType(1), 1, userType(2), 2)));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    map(userType(1, 1), 1, userType(1, 2), 1, userType(2, 1), 2));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, map(userType(1), 1,
+                                      userType(1, 1), 1,
+                                      userType(1, 2), 1,
+                                      userType(2), 2,
+                                      userType(2, 1), 2)));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, map(userType(1), 1,
+                                  userType(1, 1), 1,
+                                  userType(1, 2), 1,
+                                  userType(2), 2,
+                                  userType(2, 1), 2)));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinList() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y list<frozen<" + columnType + ">>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType(1), userType(2)));
+            assertRows(execute("SELECT * FROM %s"), row(1, list(userType(1), userType(2))));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, list(userType(1), userType(2))));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    list(userType(1, 1), userType(1, 2), userType(2, 1)));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, list(userType(1),
+                                       userType(2),
+                                       userType(1, 1),
+                                       userType(1, 2),
+                                       userType(2, 1))));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, list(userType(1),
+                                   userType(2),
+                                   userType(1, 1),
+                                   userType(1, 2),
+                                   userType(2, 1))));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testAlteringUserTypeNestedWithinSetWithView() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        createTable("CREATE TABLE %s (pk int, c int, v int, s set<frozen<" + columnType + ">>, PRIMARY KEY (pk, c))");
+        execute("CREATE MATERIALIZED VIEW " + keyspace() + ".view1 AS SELECT c, pk, v FROM %s WHERE pk IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL PRIMARY KEY (c, pk)");
+
+        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType(1), userType(2)));
+        flush();
+
+        execute("ALTER TYPE " + columnType + " ADD b int");
+        execute("UPDATE %s SET s = s + ?, v = ? WHERE pk = ? AND c = ?",
+                set(userType(1, 1), userType(1, 2), userType(2, 1)), 2, 1, 1);
+
+        assertRows(execute("SELECT * FROM %s WHERE pk = ? AND c = ?", 1, 1),
+                       row(1, 1,set(userType(1), userType(1, 1), userType(1, 2), userType(2), userType(2, 1)), 2));
+    }
+
     private String typeWithKs(String type1)
     {
         return keyspace() + '.' + type1;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
new file mode 100644
index 0000000..ad0c05c
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.db.rows;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.*;
+
+import static java.util.Arrays.asList;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class AbstractTypeVersionComparatorTest
+{
+    private UserType udtWith2Fields;
+    private UserType udtWith3Fields;
+
+    @Before
+    public void setUp()
+    {
+        udtWith2Fields = new UserType("ks",
+                                      bytes("myType"),
+                                      asList(bytes("a"), bytes("b")),
+                                      asList(Int32Type.instance, Int32Type.instance));
+        udtWith3Fields = new UserType("ks",
+                                      bytes("myType"),
+                                      asList(bytes("a"), bytes("b"), bytes("c")),
+                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance));
+    }
+
+    @After
+    public void tearDown()
+    {
+        udtWith2Fields = null;
+        udtWith3Fields = null;
+    }
+
+    @Test
+    public void testWithTuples()
+    {
+        checkComparisonResults(new TupleType(asList(Int32Type.instance, Int32Type.instance)),
+                               new TupleType(asList(Int32Type.instance, Int32Type.instance, Int32Type.instance)));
+    }
+
+    @Test
+    public void testWithUDTs()
+    {
+        checkComparisonResults(udtWith2Fields, udtWith3Fields);
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinSet()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            SetType<ByteBuffer> set1 = SetType.getInstance(udtWith2Fields, isMultiCell);
+            SetType<ByteBuffer> set2 = SetType.getInstance(udtWith3Fields, isMultiCell);
+            checkComparisonResults(set1, set2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinList()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            ListType<ByteBuffer> list1 = ListType.getInstance(udtWith2Fields, isMultiCell);
+            ListType<ByteBuffer> list2 = ListType.getInstance(udtWith3Fields, isMultiCell);
+            checkComparisonResults(list1, list2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinMap()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            MapType<ByteBuffer, Integer> map1 = MapType.getInstance(udtWith2Fields, Int32Type.instance, isMultiCell);
+            MapType<ByteBuffer, Integer> map2 = MapType.getInstance(udtWith3Fields, Int32Type.instance, isMultiCell);
+            checkComparisonResults(map1, map2);
+        }
+
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            MapType<Integer, ByteBuffer> map1 = MapType.getInstance(Int32Type.instance, udtWith2Fields, isMultiCell);
+            MapType<Integer, ByteBuffer> map2 = MapType.getInstance(Int32Type.instance, udtWith3Fields, isMultiCell);
+            checkComparisonResults(map1, map2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinTuple()
+    {
+        TupleType tuple1 = new TupleType(asList(udtWith2Fields, Int32Type.instance));
+        TupleType tuple2 = new TupleType(asList(udtWith3Fields, Int32Type.instance));
+        checkComparisonResults(tuple1, tuple2);
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinComposite()
+    {
+        CompositeType composite1 = CompositeType.getInstance(asList(udtWith2Fields, Int32Type.instance));
+        CompositeType composite2 = CompositeType.getInstance(asList(udtWith3Fields, Int32Type.instance));
+        checkComparisonResults(composite1, composite2);
+    }
+
+    @Test
+    public void testWithDeeplyNestedUDT()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            ListType<Set<ByteBuffer>> list1 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith2Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+            ListType<Set<ByteBuffer>> list2 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith3Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+            checkComparisonResults(list1, list2);
+        }
+    }
+
+    @Test
+    public void testInvalidComparison()
+    {
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type) and org.apache.cassandra.db.marshal.Int32Type",
+                                udtWith2Fields,
+                                Int32Type.instance);
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                SetType.getInstance(UTF8Type.instance, true),
+                                SetType.getInstance(InetAddressType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                ListType.getInstance(UTF8Type.instance, true),
+                                ListType.getInstance(InetAddressType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                MapType.getInstance(UTF8Type.instance, IntegerType.instance, true),
+                                MapType.getInstance(InetAddressType.instance, IntegerType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                MapType.getInstance(IntegerType.instance, UTF8Type.instance, true),
+                                MapType.getInstance(IntegerType.instance, InetAddressType.instance, true));
+    }
+
+    private void assertInvalidComparison(String expectedMessage, AbstractType<?> oldVersion, AbstractType<?> newVersion)
+    {
+        try
+        {
+            checkComparisonResults(oldVersion, newVersion);
+            fail("comparison doesn't throw expected IllegalArgumentException: " + expectedMessage);
+        }
+        catch (IllegalArgumentException e)
+        {
+            assertEquals(e.getMessage(), expectedMessage);
+        }
+    }
+
+    private void checkComparisonResults(AbstractType<?> oldVersion, AbstractType<?> newVersion)
+    {
+        assertEquals(0, compare(oldVersion, oldVersion));
+        assertEquals(0, compare(newVersion, newVersion));
+        assertEquals(-1, compare(oldVersion, newVersion));
+        assertEquals(1, compare(newVersion, oldVersion));
+    }
+
+    private int compare(AbstractType<?> left, AbstractType<?> right)
+    {
+        return AbstractTypeVersionComparator.INSTANCE.compare(left, right);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[6/8] cassandra git commit: Merge branch cassandra-3.0 into cassandra-3.11

Posted by bl...@apache.org.
Merge branch cassandra-3.0 into cassandra-3.11


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

Branch: refs/heads/trunk
Commit: 8d98a99231f38102d0ef572ffe948d705169aae4
Parents: 000007f cf0b6d1
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Aug 24 18:21:08 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Aug 24 18:22:13 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/db/SerializationHeader.java       |  13 +-
 .../db/rows/AbstractTypeVersionComparator.java  | 121 ++++++++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  18 +-
 src/java/org/apache/cassandra/db/rows/Rows.java |  22 ++-
 .../io/sstable/format/SSTableReader.java        |  20 +-
 .../org/apache/cassandra/cql3/CQLTester.java    |  20 +-
 .../cql3/validation/entities/UserTypesTest.java | 151 +++++++++++++++
 .../rows/AbstractTypeVersionComparatorTest.java | 188 +++++++++++++++++++
 9 files changed, 533 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index d39f78c,ba35152..8fb8e2f
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,11 -1,5 +1,12 @@@
 -3.0.15
 +3.11.1
 + * Fix cassandra-stress hang issues when an error during cluster connection happens (CASSANDRA-12938)
 + * Better bootstrap failure message when blocked by (potential) range movement (CASSANDRA-13744)
 + * "ignore" option is ignored in sstableloader (CASSANDRA-13721)
 + * Deadlock in AbstractCommitLogSegmentManager (CASSANDRA-13652)
 + * Duplicate the buffer before passing it to analyser in SASI operation (CASSANDRA-13512)
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
+  * Fix the merging of cells with different user type versions (CASSANDRA-13776)
   * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
   * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
   * Don't let stress write warmup data if n=0 (CASSANDRA-13773)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/src/java/org/apache/cassandra/db/SerializationHeader.java
----------------------------------------------------------------------

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

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Rows.java
index a92bdac,09213a4..3331506
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@@ -26,9 -25,7 +26,8 @@@ import org.apache.cassandra.config.CFMe
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.db.partitions.PartitionStatisticsCollector;
- import org.apache.cassandra.db.rows.Row.Deletion;
  import org.apache.cassandra.utils.MergeIterator;
 +import org.apache.cassandra.utils.WrappedInt;
  
  /**
   * Static utilities to work on Row objects.
@@@ -330,78 -312,20 +330,96 @@@ public abstract class Row
      }
  
      /**
 +     * Returns a row that is obtained from the given existing row by removing everything that is shadowed by data in
 +     * the update row. In other words, produces the smallest result row such that
 +     * {@code merge(result, update, nowInSec) == merge(existing, update, nowInSec)} after filtering by rangeDeletion.
 +     *
 +     * @param existing source row
 +     * @param update shadowing row
 +     * @param rangeDeletion extra {@code DeletionTime} from covering tombstone
 +     * @param nowInSec the current time in seconds (which plays a role during reconciliation
 +     * because deleted cells always have precedence on timestamp equality and deciding if a
 +     * cell is a live or not depends on the current time due to expiring cells).
 +     */
 +    public static Row removeShadowedCells(Row existing, Row update, DeletionTime rangeDeletion, int nowInSec)
 +    {
 +        Row.Builder builder = BTreeRow.sortedBuilder();
 +        Clustering clustering = existing.clustering();
 +        builder.newRow(clustering);
 +
 +        DeletionTime deletion = update.deletion().time();
 +        if (rangeDeletion.supersedes(deletion))
 +            deletion = rangeDeletion;
 +
 +        LivenessInfo existingInfo = existing.primaryKeyLivenessInfo();
 +        if (!deletion.deletes(existingInfo))
 +            builder.addPrimaryKeyLivenessInfo(existingInfo);
 +        Row.Deletion rowDeletion = existing.deletion();
 +        if (!deletion.supersedes(rowDeletion.time()))
 +            builder.addRowDeletion(rowDeletion);
 +
 +        Iterator<ColumnData> a = existing.iterator();
 +        Iterator<ColumnData> b = update.iterator();
 +        ColumnData nexta = a.hasNext() ? a.next() : null, nextb = b.hasNext() ? b.next() : null;
 +        while (nexta != null)
 +        {
 +            int comparison = nextb == null ? -1 : nexta.column.compareTo(nextb.column);
 +            if (comparison <= 0)
 +            {
 +                ColumnData cura = nexta;
 +                ColumnDefinition column = cura.column;
 +                ColumnData curb = comparison == 0 ? nextb : null;
 +                if (column.isSimple())
 +                {
 +                    Cells.addNonShadowed((Cell) cura, (Cell) curb, deletion, builder, nowInSec);
 +                }
 +                else
 +                {
 +                    ComplexColumnData existingData = (ComplexColumnData) cura;
 +                    ComplexColumnData updateData = (ComplexColumnData) curb;
 +
 +                    DeletionTime existingDt = existingData.complexDeletion();
 +                    DeletionTime updateDt = updateData == null ? DeletionTime.LIVE : updateData.complexDeletion();
 +
 +                    DeletionTime maxDt = updateDt.supersedes(deletion) ? updateDt : deletion;
 +                    if (existingDt.supersedes(maxDt))
 +                    {
 +                        builder.addComplexDeletion(column, existingDt);
 +                        maxDt = existingDt;
 +                    }
 +
 +                    Iterator<Cell> existingCells = existingData.iterator();
 +                    Iterator<Cell> updateCells = updateData == null ? null : updateData.iterator();
 +                    Cells.addNonShadowedComplex(column, existingCells, updateCells, maxDt, builder, nowInSec);
 +                }
 +                nexta = a.hasNext() ? a.next() : null;
 +                if (curb != null)
 +                    nextb = b.hasNext() ? b.next() : null;
 +            }
 +            else
 +            {
 +                nextb = b.hasNext() ? b.next() : null;
 +            }
 +        }
 +        Row row = builder.build();
 +        return row != null && !row.isEmpty() ? row : null;
 +    }
++
++    /**
+      * Returns the {@code ColumnDefinition} to use for merging the columns.
+      * If the 2 column definitions are different the latest one will be returned.
+      */
+     private static ColumnDefinition getColumnDefinition(ColumnData cura, ColumnData curb)
+     {
+         if (cura == null)
+             return curb.column;
+ 
+         if (curb == null)
+             return cura.column;
+ 
+         if (AbstractTypeVersionComparator.INSTANCE.compare(cura.column.type, curb.column.type) >= 0)
+             return cura.column;
+ 
+         return curb.column;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/CQLTester.java
index 2542db0,40aec88..c0513ce
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@@ -425,9 -396,34 +425,10 @@@ public abstract class CQLTeste
      public void disableCompaction(String keyspace)
      {
          ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
-         store.disableAutoCompaction();
+         if (store != null)
+             store.disableAutoCompaction();
      }
  
 -    public void flush(boolean forceFlush)
 -    {
 -        if (forceFlush)
 -            flush();
 -    }
 -
 -    @FunctionalInterface
 -    public interface CheckedFunction {
 -        void apply() throws Throwable;
 -    }
 -
 -    /**
 -     * Runs the given function before and after a flush of sstables.  This is useful for checking that behavior is
 -     * the same whether data is in memtables or sstables.
 -     * @param runnable
 -     * @throws Throwable
 -     */
 -    public void beforeAndAfterFlush(CheckedFunction runnable) throws Throwable
 -    {
 -        runnable.apply();
 -        flush();
 -        runnable.apply();
 -    }
 -
      public void compact()
      {
          try

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index b78eb3e,dfc2e5e..9ea5572
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -624,109 -563,156 +624,260 @@@ public class UserTypesTest extends CQLT
      }
  
      @Test
 +    public void testInsertNonFrozenUDT() throws Throwable
 +    {
 +        String typeName = createType("CREATE TYPE %s (a int, b text)");
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, 0, "abc");
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, 0, null);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", null, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +    }
 +
 +    @Test
 +    public void testUpdateNonFrozenUDT() throws Throwable
 +    {
 +        String typeName = createType("CREATE TYPE %s (a int, b text)");
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        // overwrite the whole UDT
 +        execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", 1, "b", "def"), 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "def")));
 +
 +        execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", 0, "b", null), 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", null, "b", "abc"), 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +
 +        // individually set fields to non-null values
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", 1, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "abc")));
 +
 +        execute("UPDATE %s SET v.b = ? WHERE k = ?", "def", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "def")));
 +
 +        execute("UPDATE %s SET v.a = ?, v.b = ? WHERE k = ?", 0, "abc", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("UPDATE %s SET v.b = ?, v.a = ? WHERE k = ?", "abc", 0, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        // individually set fields to null values
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", null, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", 0, 0);
 +        execute("UPDATE %s SET v.b = ? WHERE k = ?", null, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", null, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        assertInvalid("UPDATE %s SET v.bad = ? FROM %s WHERE k = ?", 0, 0);
 +        assertInvalid("UPDATE %s SET v = ? FROM %s WHERE k = ?", 0, 0);
 +        assertInvalid("UPDATE %s SET v = ? FROM %s WHERE k = ?", userType("a", 1, "b", "abc", "bad", 123), 0);
 +    }
 +
 +    @Test
 +    public void testDeleteNonFrozenUDT() throws Throwable
 +    {
 +        String typeName = createType("CREATE TYPE %s (a int, b text)");
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("DELETE v.b FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v.a FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +
 +        execute("DELETE v.b FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        // delete both fields at once
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v.a, v.b FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        // same, but reverse field order
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v.b, v.a FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        // delete the whole thing at once
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        assertInvalid("DELETE v.bad FROM %s WHERE k = ?", 0);
 +    }
 +
++    @Test
+     public void testReadAfterAlteringUserTypeNestedWithinSet() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         try
+         {
+             createTable("CREATE TABLE %s (x int PRIMARY KEY, y set<frozen<" + columnType + ">>)");
+             disableCompaction();
+ 
 -            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType(1), userType(2)));
 -            assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1), userType(2))));
++            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType("a", 1), userType("a", 2)));
++            assertRows(execute("SELECT * FROM %s"), row(1, set(userType("a", 1), userType("a", 2))));
+             flush();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, set(userType(1), userType(2))));
++                       row(1, set(userType("a", 1), userType("a", 2))));
+ 
+             execute("ALTER TYPE " + columnType + " ADD b int");
+             execute("UPDATE %s SET y = y + ? WHERE x = 1",
 -                    set(userType(1, 1), userType(1, 2), userType(2, 1)));
++                    set(userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2, "b", 1)));
+ 
+             flush();
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                           row(1, set(userType(1),
 -                                      userType(1, 1),
 -                                      userType(1, 2),
 -                                      userType(2),
 -                                      userType(2, 1))));
++                           row(1, set(userType("a", 1),
++                                      userType("a", 1, "b", 1),
++                                      userType("a", 1, "b", 2),
++                                      userType("a", 2),
++                                      userType("a", 2, "b", 1))));
+ 
+             compact();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, set(userType(1),
 -                                  userType(1, 1),
 -                                  userType(1, 2),
 -                                  userType(2),
 -                                  userType(2, 1))));
++                       row(1, set(userType("a", 1),
++                                  userType("a", 1, "b", 1),
++                                  userType("a", 1, "b", 2),
++                                  userType("a", 2),
++                                  userType("a", 2, "b", 1))));
+         }
+         finally
+         {
+             enableCompaction();
+         }
+     }
+ 
+     @Test
+     public void testReadAfterAlteringUserTypeNestedWithinMap() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         try
+         {
+             createTable("CREATE TABLE %s (x int PRIMARY KEY, y map<frozen<" + columnType + ">, int>)");
+             disableCompaction();
+ 
 -            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType(1), 1, userType(2), 2));
 -            assertRows(execute("SELECT * FROM %s"), row(1, map(userType(1), 1, userType(2), 2)));
++            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType("a", 1), 1, userType("a", 2), 2));
++            assertRows(execute("SELECT * FROM %s"), row(1, map(userType("a", 1), 1, userType("a", 2), 2)));
+             flush();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, map(userType(1), 1, userType(2), 2)));
++                       row(1, map(userType("a", 1), 1, userType("a", 2), 2)));
+ 
+             execute("ALTER TYPE " + columnType + " ADD b int");
+             execute("UPDATE %s SET y = y + ? WHERE x = 1",
 -                    map(userType(1, 1), 1, userType(1, 2), 1, userType(2, 1), 2));
++                    map(userType("a", 1, "b", 1), 1, userType("a", 1, "b", 2), 1, userType("a", 2, "b", 1), 2));
+ 
+             flush();
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                           row(1, map(userType(1), 1,
 -                                      userType(1, 1), 1,
 -                                      userType(1, 2), 1,
 -                                      userType(2), 2,
 -                                      userType(2, 1), 2)));
++                           row(1, map(userType("a", 1), 1,
++                                      userType("a", 1, "b", 1), 1,
++                                      userType("a", 1, "b", 2), 1,
++                                      userType("a", 2), 2,
++                                      userType("a", 2, "b", 1), 2)));
+ 
+             compact();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, map(userType(1), 1,
 -                                  userType(1, 1), 1,
 -                                  userType(1, 2), 1,
 -                                  userType(2), 2,
 -                                  userType(2, 1), 2)));
++                       row(1, map(userType("a", 1), 1,
++                                  userType("a", 1, "b", 1), 1,
++                                  userType("a", 1, "b", 2), 1,
++                                  userType("a", 2), 2,
++                                  userType("a", 2, "b", 1), 2)));
+         }
+         finally
+         {
+             enableCompaction();
+         }
+     }
+ 
+     @Test
+     public void testReadAfterAlteringUserTypeNestedWithinList() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         try
+         {
+             createTable("CREATE TABLE %s (x int PRIMARY KEY, y list<frozen<" + columnType + ">>)");
+             disableCompaction();
+ 
 -            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType(1), userType(2)));
 -            assertRows(execute("SELECT * FROM %s"), row(1, list(userType(1), userType(2))));
++            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType("a", 1), userType("a", 2)));
++            assertRows(execute("SELECT * FROM %s"), row(1, list(userType("a", 1), userType("a", 2))));
+             flush();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, list(userType(1), userType(2))));
++                       row(1, list(userType("a", 1), userType("a", 2))));
+ 
+             execute("ALTER TYPE " + columnType + " ADD b int");
+             execute("UPDATE %s SET y = y + ? WHERE x = 1",
 -                    list(userType(1, 1), userType(1, 2), userType(2, 1)));
++                    list(userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2, "b", 1)));
+ 
+             flush();
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                           row(1, list(userType(1),
 -                                       userType(2),
 -                                       userType(1, 1),
 -                                       userType(1, 2),
 -                                       userType(2, 1))));
++                           row(1, list(userType("a", 1),
++                                       userType("a", 2),
++                                       userType("a", 1, "b", 1),
++                                       userType("a", 1, "b", 2),
++                                       userType("a", 2, "b", 1))));
+ 
+             compact();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, list(userType(1),
 -                                   userType(2),
 -                                   userType(1, 1),
 -                                   userType(1, 2),
 -                                   userType(2, 1))));
++                       row(1, list(userType("a", 1),
++                                   userType("a", 2),
++                                   userType("a", 1, "b", 1),
++                                   userType("a", 1, "b", 2),
++                                   userType("a", 2, "b", 1))));
+         }
+         finally
+         {
+             enableCompaction();
+         }
+     }
+ 
+     @Test
+     public void testAlteringUserTypeNestedWithinSetWithView() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         createTable("CREATE TABLE %s (pk int, c int, v int, s set<frozen<" + columnType + ">>, PRIMARY KEY (pk, c))");
+         execute("CREATE MATERIALIZED VIEW " + keyspace() + ".view1 AS SELECT c, pk, v FROM %s WHERE pk IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL PRIMARY KEY (c, pk)");
+ 
 -        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType(1), userType(2)));
++        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType("a", 1), userType("a", 2)));
+         flush();
+ 
+         execute("ALTER TYPE " + columnType + " ADD b int");
+         execute("UPDATE %s SET s = s + ?, v = ? WHERE pk = ? AND c = ?",
 -                set(userType(1, 1), userType(1, 2), userType(2, 1)), 2, 1, 1);
++                set(userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2, "b", 1)), 2, 1, 1);
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = ? AND c = ?", 1, 1),
 -                       row(1, 1,set(userType(1), userType(1, 1), userType(1, 2), userType(2), userType(2, 1)), 2));
++                       row(1, 1,set(userType("a", 1), userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2), userType("a", 2, "b", 1)), 2));
+     }
+ 
      private String typeWithKs(String type1)
      {
          return keyspace() + '.' + type1;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
index 0000000,ad0c05c..7170696
mode 000000,100644..100644
--- a/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
+++ b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
@@@ -1,0 -1,184 +1,188 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.cassandra.db.rows;
+ 
+ import java.nio.ByteBuffer;
+ import java.util.Set;
+ 
+ import org.junit.After;
+ import org.junit.Before;
+ import org.junit.Test;
+ 
+ import org.apache.cassandra.db.marshal.*;
+ 
+ import static java.util.Arrays.asList;
++import static org.apache.cassandra.cql3.FieldIdentifier.forUnquoted;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+ import static org.junit.Assert.assertEquals;
+ import static org.junit.Assert.fail;
+ 
+ public class AbstractTypeVersionComparatorTest
+ {
+     private UserType udtWith2Fields;
+     private UserType udtWith3Fields;
+ 
+     @Before
+     public void setUp()
+     {
+         udtWith2Fields = new UserType("ks",
+                                       bytes("myType"),
 -                                      asList(bytes("a"), bytes("b")),
 -                                      asList(Int32Type.instance, Int32Type.instance));
++                                      asList(forUnquoted("a"), forUnquoted("b")),
++                                      asList(Int32Type.instance, Int32Type.instance),
++                                      false);
+         udtWith3Fields = new UserType("ks",
+                                       bytes("myType"),
 -                                      asList(bytes("a"), bytes("b"), bytes("c")),
 -                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance));
++                                      asList(forUnquoted("a"), forUnquoted("b"), forUnquoted("c")),
++                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance),
++                                      false);
+     }
+ 
+     @After
+     public void tearDown()
+     {
+         udtWith2Fields = null;
+         udtWith3Fields = null;
+     }
+ 
+     @Test
+     public void testWithTuples()
+     {
+         checkComparisonResults(new TupleType(asList(Int32Type.instance, Int32Type.instance)),
+                                new TupleType(asList(Int32Type.instance, Int32Type.instance, Int32Type.instance)));
+     }
+ 
+     @Test
+     public void testWithUDTs()
+     {
+         checkComparisonResults(udtWith2Fields, udtWith3Fields);
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinSet()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             SetType<ByteBuffer> set1 = SetType.getInstance(udtWith2Fields, isMultiCell);
+             SetType<ByteBuffer> set2 = SetType.getInstance(udtWith3Fields, isMultiCell);
+             checkComparisonResults(set1, set2);
+         }
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinList()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             ListType<ByteBuffer> list1 = ListType.getInstance(udtWith2Fields, isMultiCell);
+             ListType<ByteBuffer> list2 = ListType.getInstance(udtWith3Fields, isMultiCell);
+             checkComparisonResults(list1, list2);
+         }
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinMap()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             MapType<ByteBuffer, Integer> map1 = MapType.getInstance(udtWith2Fields, Int32Type.instance, isMultiCell);
+             MapType<ByteBuffer, Integer> map2 = MapType.getInstance(udtWith3Fields, Int32Type.instance, isMultiCell);
+             checkComparisonResults(map1, map2);
+         }
+ 
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             MapType<Integer, ByteBuffer> map1 = MapType.getInstance(Int32Type.instance, udtWith2Fields, isMultiCell);
+             MapType<Integer, ByteBuffer> map2 = MapType.getInstance(Int32Type.instance, udtWith3Fields, isMultiCell);
+             checkComparisonResults(map1, map2);
+         }
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinTuple()
+     {
+         TupleType tuple1 = new TupleType(asList(udtWith2Fields, Int32Type.instance));
+         TupleType tuple2 = new TupleType(asList(udtWith3Fields, Int32Type.instance));
+         checkComparisonResults(tuple1, tuple2);
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinComposite()
+     {
+         CompositeType composite1 = CompositeType.getInstance(asList(udtWith2Fields, Int32Type.instance));
+         CompositeType composite2 = CompositeType.getInstance(asList(udtWith3Fields, Int32Type.instance));
+         checkComparisonResults(composite1, composite2);
+     }
+ 
+     @Test
+     public void testWithDeeplyNestedUDT()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             ListType<Set<ByteBuffer>> list1 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith2Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+             ListType<Set<ByteBuffer>> list2 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith3Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+             checkComparisonResults(list1, list2);
+         }
+     }
+ 
+     @Test
+     public void testInvalidComparison()
+     {
 -        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type) and org.apache.cassandra.db.marshal.Int32Type",
++        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.FrozenType(org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type)) and org.apache.cassandra.db.marshal.Int32Type",
+                                 udtWith2Fields,
+                                 Int32Type.instance);
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 SetType.getInstance(UTF8Type.instance, true),
+                                 SetType.getInstance(InetAddressType.instance, true));
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 ListType.getInstance(UTF8Type.instance, true),
+                                 ListType.getInstance(InetAddressType.instance, true));
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 MapType.getInstance(UTF8Type.instance, IntegerType.instance, true),
+                                 MapType.getInstance(InetAddressType.instance, IntegerType.instance, true));
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 MapType.getInstance(IntegerType.instance, UTF8Type.instance, true),
+                                 MapType.getInstance(IntegerType.instance, InetAddressType.instance, true));
+     }
+ 
+     private void assertInvalidComparison(String expectedMessage, AbstractType<?> oldVersion, AbstractType<?> newVersion)
+     {
+         try
+         {
+             checkComparisonResults(oldVersion, newVersion);
+             fail("comparison doesn't throw expected IllegalArgumentException: " + expectedMessage);
+         }
+         catch (IllegalArgumentException e)
+         {
++            System.out.println(e.getMessage());
+             assertEquals(e.getMessage(), expectedMessage);
+         }
+     }
+ 
+     private void checkComparisonResults(AbstractType<?> oldVersion, AbstractType<?> newVersion)
+     {
+         assertEquals(0, compare(oldVersion, oldVersion));
+         assertEquals(0, compare(newVersion, newVersion));
+         assertEquals(-1, compare(oldVersion, newVersion));
+         assertEquals(1, compare(newVersion, oldVersion));
+     }
+ 
 -    private int compare(AbstractType<?> left, AbstractType<?> right)
++    private static int compare(AbstractType<?> left, AbstractType<?> right)
+     {
+         return AbstractTypeVersionComparator.INSTANCE.compare(left, right);
+     }
+ }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[4/8] cassandra git commit: Fix the merging of cells with different user type versions

Posted by bl...@apache.org.
Fix the merging of cells with different user type versions

patch by Benjamin Lerer; reviewed by Robert Stupp  for CASSANDRA-13776


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

Branch: refs/heads/trunk
Commit: cf0b6d107bade419dada49a5da40d2579c80ade8
Parents: 1a2ad2e
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Aug 24 18:19:28 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Aug 24 18:19:28 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/db/SerializationHeader.java       |  13 +-
 .../cassandra/db/marshal/AbstractType.java      |  10 +
 .../apache/cassandra/db/marshal/TupleType.java  |   5 +
 .../apache/cassandra/db/marshal/UserType.java   |  10 +
 .../db/rows/AbstractTypeVersionComparator.java  | 121 ++++++++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  18 +-
 src/java/org/apache/cassandra/db/rows/Rows.java |  21 ++-
 .../io/sstable/format/SSTableReader.java        |  23 +--
 .../org/apache/cassandra/cql3/CQLTester.java    |  20 +-
 .../cql3/validation/entities/UserTypesTest.java | 151 +++++++++++++++
 .../rows/AbstractTypeVersionComparatorTest.java | 184 +++++++++++++++++++
 12 files changed, 555 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index a35cc1b..ba35152 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Fix the merging of cells with different user type versions (CASSANDRA-13776)
  * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
  * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
  * Don't let stress write warmup data if n=0 (CASSANDRA-13773)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/SerializationHeader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SerializationHeader.java b/src/java/org/apache/cassandra/db/SerializationHeader.java
index 19dad95..494c2a3 100644
--- a/src/java/org/apache/cassandra/db/SerializationHeader.java
+++ b/src/java/org/apache/cassandra/db/SerializationHeader.java
@@ -108,7 +108,8 @@ public class SerializationHeader
         // but rather on their stats stored in StatsMetadata that are fully accurate.
         EncodingStats.Collector stats = new EncodingStats.Collector();
         PartitionColumns.Builder columns = PartitionColumns.builder();
-        for (SSTableReader sstable : sstables)
+        // We need to order the SSTables by descending generation to be sure that we use latest column definitions.
+        for (SSTableReader sstable : orderByDescendingGeneration(sstables))
         {
             stats.updateTimestamp(sstable.getMinTimestamp());
             stats.updateLocalDeletionTime(sstable.getMinLocalDeletionTime());
@@ -121,6 +122,16 @@ public class SerializationHeader
         return new SerializationHeader(true, metadata, columns.build(), stats.get());
     }
 
+    private static Collection<SSTableReader> orderByDescendingGeneration(Collection<SSTableReader> sstables)
+    {
+        if (sstables.size() < 2)
+            return sstables;
+
+        List<SSTableReader> readers = new ArrayList<>(sstables);
+        readers.sort(SSTableReader.generationReverseComparator);
+        return readers;
+    }
+
     public SerializationHeader(boolean isForSSTable,
                                CFMetaData metadata,
                                PartitionColumns columns,

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 77e0971..20062bd 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -325,6 +325,16 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
         return false;
     }
 
+    public boolean isTuple()
+    {
+        return false;
+    }
+
+    public boolean isUDT()
+    {
+        return false;
+    }
+
     public AbstractType<?> freeze()
     {
         return this;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java
index 2d6363e..5c74332 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -347,4 +347,9 @@ public class TupleType extends AbstractType<ByteBuffer>
     {
         return getClass().getName() + TypeParser.stringifyTypeParameters(types, true);
     }
+
+    public boolean isTuple()
+    {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java
index b91dbf8..03545ca 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -232,4 +232,14 @@ public class UserType extends TupleType
     {
         return serializer;
     }
+
+    public boolean isTuple()
+    {
+        return false;
+    }
+
+    public boolean isUDT()
+    {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java b/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
new file mode 100644
index 0000000..e47f681
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.db.rows;
+
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.cassandra.db.marshal.*;
+
+/**
+ * A {@code Comparator} use to determine which version of a type should be used.
+ * <p>In the case of UDTs it is possible to have 2 versions or more of the same type, if some fields has been added to
+ * the type. To avoid problems the latest type need to be used.</p>
+ */
+final class AbstractTypeVersionComparator implements Comparator<AbstractType<?>>
+{
+    public static final Comparator<AbstractType<?>> INSTANCE = new AbstractTypeVersionComparator();
+
+    private AbstractTypeVersionComparator()
+    {
+    }
+
+    @Override
+    public int compare(AbstractType<?> type, AbstractType<?> otherType)
+    {
+        if (!type.getClass().equals(otherType.getClass()))
+            throw new IllegalArgumentException(String.format("Trying to compare 2 different types: %s and %s",
+                                                             type,
+                                                             otherType));
+
+        if (type.equals(otherType))
+            return 0;
+
+        // The only case where 2 types can differ is if they contains some UDTs and one of them has more
+        // fields (due to an ALTER type ADD) than in the other type. In this case we need to pick the type with
+        // the bigger amount of fields.
+        if (type.isUDT())
+            return compareUserType((UserType) type, (UserType) otherType);
+
+        if (type.isTuple())
+            return compareTuple((TupleType) type, (TupleType) otherType);
+
+        if (type.isCollection())
+            return compareCollectionTypes(type, otherType);
+
+        if (type instanceof CompositeType)
+            return compareCompositeTypes((CompositeType) type, (CompositeType) otherType);
+
+        // In theory we should never reach that point but to be on the safe side we allow it.
+        return 0;
+    }
+
+    private int compareCompositeTypes(CompositeType type, CompositeType otherType)
+    {
+        List<AbstractType<?>> types = type.getComponents();
+        List<AbstractType<?>> otherTypes = otherType.getComponents();
+
+        if (types.size() != otherTypes.size())
+            return Integer.compare(types.size(), otherTypes.size());
+
+        for (int i = 0, m = type.componentsCount(); i < m ; i++)
+        {
+            int test = compare(types.get(i), otherTypes.get(i));
+            if (test != 0);
+                return test;
+        }
+        return 0;
+    }
+
+    private int compareCollectionTypes(AbstractType<?> type, AbstractType<?> otherType)
+    {
+        if (type instanceof MapType)
+            return compareMapType((MapType<?, ?>) type, (MapType<?, ?>) otherType);
+
+        if (type instanceof SetType)
+            return compare(((SetType<?>) type).getElementsType(), ((SetType<?>) otherType).getElementsType());
+
+        return compare(((ListType<?>) type).getElementsType(), ((ListType<?>) otherType).getElementsType());
+    }
+
+    private int compareMapType(MapType<?, ?> type, MapType<?, ?> otherType)
+    {
+        int test = compare(type.getKeysType(), otherType.getKeysType());
+        return test != 0 ? test : compare(type.getValuesType(), otherType.getValuesType());
+    }
+
+    private int compareUserType(UserType type, UserType otherType)
+    {
+        return compareTuple(type, otherType);
+    }
+
+    private int compareTuple(TupleType type, TupleType otherType)
+    {
+        if (type.size() != otherType.size())
+            return Integer.compare(type.size(), otherType.size());
+
+        int test = 0;
+        int i = 0;
+        while (test == 0 && i < type.size())
+        {
+            test = compare(type.type(i), otherType.type(i));
+            i++;
+        }
+        return test;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Row.java b/src/java/org/apache/cassandra/db/rows/Row.java
index a61f365..9ab1f09 100644
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@ -601,10 +601,25 @@ public interface Row extends Unfiltered, Collection<ColumnData>
 
             public void reduce(int idx, ColumnData data)
             {
-                column = data.column();
+                if (useColumnDefinition(data.column()))
+                    column = data.column();
+
                 versions.add(data);
             }
 
+            /**
+             * Determines it the {@code ColumnDefinition} is the one that should be used.
+             * @param dataColumn the {@code ColumnDefinition} to use.
+             * @return {@code true} if the {@code ColumnDefinition} is the one that should be used, {@code false} otherwise.
+             */
+            private boolean useColumnDefinition(ColumnDefinition dataColumn)
+            {
+                if (column == null)
+                    return true;
+
+                return AbstractTypeVersionComparator.INSTANCE.compare(column.type, dataColumn.type) < 0;
+            }
+
             protected ColumnData getReduced()
             {
                 if (column.isSimple())
@@ -654,6 +669,7 @@ public interface Row extends Unfiltered, Collection<ColumnData>
 
             protected void onKeyChange()
             {
+                column = null;
                 versions.clear();
             }
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Rows.java b/src/java/org/apache/cassandra/db/rows/Rows.java
index e325091..09213a4 100644
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@ -279,7 +279,8 @@ public abstract class Rows
             int comparison = nexta == null ? 1 : nextb == null ? -1 : nexta.column.compareTo(nextb.column);
             ColumnData cura = comparison <= 0 ? nexta : null;
             ColumnData curb = comparison >= 0 ? nextb : null;
-            ColumnDefinition column = (cura != null ? cura : curb).column;
+            ColumnDefinition column = getColumnDefinition(cura, curb);
+
             if (column.isSimple())
             {
                 timeDelta = Math.min(timeDelta, Cells.reconcile((Cell) cura, (Cell) curb, deletion, builder, nowInSec));
@@ -309,4 +310,22 @@ public abstract class Rows
         }
         return timeDelta;
     }
+
+    /**
+     * Returns the {@code ColumnDefinition} to use for merging the columns.
+     * If the 2 column definitions are different the latest one will be returned.
+     */
+    private static ColumnDefinition getColumnDefinition(ColumnData cura, ColumnData curb)
+    {
+        if (cura == null)
+            return curb.column;
+
+        if (curb == null)
+            return cura.column;
+
+        if (AbstractTypeVersionComparator.INSTANCE.compare(cura.column.type, curb.column.type) >= 0)
+            return cura.column;
+
+        return curb.column;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
index f38738d..cd41b5b 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
@@ -45,7 +45,6 @@ import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.commitlog.ReplayPosition;
 import org.apache.cassandra.db.filter.ColumnFilter;
 import org.apache.cassandra.db.rows.SliceableUnfilteredRowIterator;
 import org.apache.cassandra.dht.AbstractBounds;
@@ -141,26 +140,14 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
     }
     private static final RateLimiter meterSyncThrottle = RateLimiter.create(100.0);
 
-    public static final Comparator<SSTableReader> maxTimestampComparator = new Comparator<SSTableReader>()
-    {
-        public int compare(SSTableReader o1, SSTableReader o2)
-        {
-            long ts1 = o1.getMaxTimestamp();
-            long ts2 = o2.getMaxTimestamp();
-            return (ts1 > ts2 ? -1 : (ts1 == ts2 ? 0 : 1));
-        }
-    };
+    public static final Comparator<SSTableReader> maxTimestampComparator = (o1, o2) -> Long.compare(o1.getMaxTimestamp(), o2.getMaxTimestamp());
 
     // it's just an object, which we use regular Object equality on; we introduce a special class just for easy recognition
     public static final class UniqueIdentifier {}
 
-    public static final Comparator<SSTableReader> sstableComparator = new Comparator<SSTableReader>()
-    {
-        public int compare(SSTableReader o1, SSTableReader o2)
-        {
-            return o1.first.compareTo(o2.first);
-        }
-    };
+    public static final Comparator<SSTableReader> sstableComparator = (o1, o2) -> o1.first.compareTo(o2.first);
+
+    public static final Comparator<SSTableReader> generationReverseComparator = (o1, o2) -> -Integer.compare(o1.descriptor.generation, o2.descriptor.generation);
 
     public static final Ordering<SSTableReader> sstableOrdering = Ordering.from(sstableComparator);
 
@@ -1717,7 +1704,7 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
     /**
      * Direct I/O SSTableScanner over an iterator of bounds.
      *
-     * @param bounds the keys to cover
+     * @param rangeIterator the keys to cover
      * @return A Scanner for seeking over the rows of the SSTable.
      */
     public abstract ISSTableScanner getScanner(Iterator<AbstractBounds<PartitionPosition>> rangeIterator);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/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 ba23c67..40aec88 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -396,7 +396,8 @@ public abstract class CQLTester
     public void disableCompaction(String keyspace)
     {
         ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
-        store.disableAutoCompaction();
+        if (store != null)
+            store.disableAutoCompaction();
     }
 
     public void flush(boolean forceFlush)
@@ -437,6 +438,23 @@ public abstract class CQLTester
         }
     }
 
+    public void disableCompaction()
+    {
+        disableCompaction(KEYSPACE);
+    }
+
+    public void enableCompaction(String keyspace)
+    {
+        ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
+        if (store != null)
+            store.enableAutoCompaction();
+    }
+
+    public void enableCompaction()
+    {
+        enableCompaction(KEYSPACE);
+    }
+
     public void cleanupCache()
     {
         ColumnFamilyStore store = getCurrentColumnFamilyStore();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index c279e00..dfc2e5e 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -562,6 +562,157 @@ public class UserTypesTest extends CQLTester
         assertInvalidMessage("Cannot drop user type " + typeWithKs(t), "DROP TYPE " + typeWithKs(t) + ';');
     }
 
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinSet() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y set<frozen<" + columnType + ">>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType(1), userType(2)));
+            assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1), userType(2))));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, set(userType(1), userType(2))));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    set(userType(1, 1), userType(1, 2), userType(2, 1)));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, set(userType(1),
+                                      userType(1, 1),
+                                      userType(1, 2),
+                                      userType(2),
+                                      userType(2, 1))));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, set(userType(1),
+                                  userType(1, 1),
+                                  userType(1, 2),
+                                  userType(2),
+                                  userType(2, 1))));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinMap() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y map<frozen<" + columnType + ">, int>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType(1), 1, userType(2), 2));
+            assertRows(execute("SELECT * FROM %s"), row(1, map(userType(1), 1, userType(2), 2)));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, map(userType(1), 1, userType(2), 2)));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    map(userType(1, 1), 1, userType(1, 2), 1, userType(2, 1), 2));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, map(userType(1), 1,
+                                      userType(1, 1), 1,
+                                      userType(1, 2), 1,
+                                      userType(2), 2,
+                                      userType(2, 1), 2)));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, map(userType(1), 1,
+                                  userType(1, 1), 1,
+                                  userType(1, 2), 1,
+                                  userType(2), 2,
+                                  userType(2, 1), 2)));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinList() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y list<frozen<" + columnType + ">>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType(1), userType(2)));
+            assertRows(execute("SELECT * FROM %s"), row(1, list(userType(1), userType(2))));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, list(userType(1), userType(2))));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    list(userType(1, 1), userType(1, 2), userType(2, 1)));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, list(userType(1),
+                                       userType(2),
+                                       userType(1, 1),
+                                       userType(1, 2),
+                                       userType(2, 1))));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, list(userType(1),
+                                   userType(2),
+                                   userType(1, 1),
+                                   userType(1, 2),
+                                   userType(2, 1))));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testAlteringUserTypeNestedWithinSetWithView() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        createTable("CREATE TABLE %s (pk int, c int, v int, s set<frozen<" + columnType + ">>, PRIMARY KEY (pk, c))");
+        execute("CREATE MATERIALIZED VIEW " + keyspace() + ".view1 AS SELECT c, pk, v FROM %s WHERE pk IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL PRIMARY KEY (c, pk)");
+
+        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType(1), userType(2)));
+        flush();
+
+        execute("ALTER TYPE " + columnType + " ADD b int");
+        execute("UPDATE %s SET s = s + ?, v = ? WHERE pk = ? AND c = ?",
+                set(userType(1, 1), userType(1, 2), userType(2, 1)), 2, 1, 1);
+
+        assertRows(execute("SELECT * FROM %s WHERE pk = ? AND c = ?", 1, 1),
+                       row(1, 1,set(userType(1), userType(1, 1), userType(1, 2), userType(2), userType(2, 1)), 2));
+    }
+
     private String typeWithKs(String type1)
     {
         return keyspace() + '.' + type1;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
new file mode 100644
index 0000000..ad0c05c
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.db.rows;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.*;
+
+import static java.util.Arrays.asList;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class AbstractTypeVersionComparatorTest
+{
+    private UserType udtWith2Fields;
+    private UserType udtWith3Fields;
+
+    @Before
+    public void setUp()
+    {
+        udtWith2Fields = new UserType("ks",
+                                      bytes("myType"),
+                                      asList(bytes("a"), bytes("b")),
+                                      asList(Int32Type.instance, Int32Type.instance));
+        udtWith3Fields = new UserType("ks",
+                                      bytes("myType"),
+                                      asList(bytes("a"), bytes("b"), bytes("c")),
+                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance));
+    }
+
+    @After
+    public void tearDown()
+    {
+        udtWith2Fields = null;
+        udtWith3Fields = null;
+    }
+
+    @Test
+    public void testWithTuples()
+    {
+        checkComparisonResults(new TupleType(asList(Int32Type.instance, Int32Type.instance)),
+                               new TupleType(asList(Int32Type.instance, Int32Type.instance, Int32Type.instance)));
+    }
+
+    @Test
+    public void testWithUDTs()
+    {
+        checkComparisonResults(udtWith2Fields, udtWith3Fields);
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinSet()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            SetType<ByteBuffer> set1 = SetType.getInstance(udtWith2Fields, isMultiCell);
+            SetType<ByteBuffer> set2 = SetType.getInstance(udtWith3Fields, isMultiCell);
+            checkComparisonResults(set1, set2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinList()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            ListType<ByteBuffer> list1 = ListType.getInstance(udtWith2Fields, isMultiCell);
+            ListType<ByteBuffer> list2 = ListType.getInstance(udtWith3Fields, isMultiCell);
+            checkComparisonResults(list1, list2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinMap()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            MapType<ByteBuffer, Integer> map1 = MapType.getInstance(udtWith2Fields, Int32Type.instance, isMultiCell);
+            MapType<ByteBuffer, Integer> map2 = MapType.getInstance(udtWith3Fields, Int32Type.instance, isMultiCell);
+            checkComparisonResults(map1, map2);
+        }
+
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            MapType<Integer, ByteBuffer> map1 = MapType.getInstance(Int32Type.instance, udtWith2Fields, isMultiCell);
+            MapType<Integer, ByteBuffer> map2 = MapType.getInstance(Int32Type.instance, udtWith3Fields, isMultiCell);
+            checkComparisonResults(map1, map2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinTuple()
+    {
+        TupleType tuple1 = new TupleType(asList(udtWith2Fields, Int32Type.instance));
+        TupleType tuple2 = new TupleType(asList(udtWith3Fields, Int32Type.instance));
+        checkComparisonResults(tuple1, tuple2);
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinComposite()
+    {
+        CompositeType composite1 = CompositeType.getInstance(asList(udtWith2Fields, Int32Type.instance));
+        CompositeType composite2 = CompositeType.getInstance(asList(udtWith3Fields, Int32Type.instance));
+        checkComparisonResults(composite1, composite2);
+    }
+
+    @Test
+    public void testWithDeeplyNestedUDT()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            ListType<Set<ByteBuffer>> list1 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith2Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+            ListType<Set<ByteBuffer>> list2 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith3Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+            checkComparisonResults(list1, list2);
+        }
+    }
+
+    @Test
+    public void testInvalidComparison()
+    {
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type) and org.apache.cassandra.db.marshal.Int32Type",
+                                udtWith2Fields,
+                                Int32Type.instance);
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                SetType.getInstance(UTF8Type.instance, true),
+                                SetType.getInstance(InetAddressType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                ListType.getInstance(UTF8Type.instance, true),
+                                ListType.getInstance(InetAddressType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                MapType.getInstance(UTF8Type.instance, IntegerType.instance, true),
+                                MapType.getInstance(InetAddressType.instance, IntegerType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                MapType.getInstance(IntegerType.instance, UTF8Type.instance, true),
+                                MapType.getInstance(IntegerType.instance, InetAddressType.instance, true));
+    }
+
+    private void assertInvalidComparison(String expectedMessage, AbstractType<?> oldVersion, AbstractType<?> newVersion)
+    {
+        try
+        {
+            checkComparisonResults(oldVersion, newVersion);
+            fail("comparison doesn't throw expected IllegalArgumentException: " + expectedMessage);
+        }
+        catch (IllegalArgumentException e)
+        {
+            assertEquals(e.getMessage(), expectedMessage);
+        }
+    }
+
+    private void checkComparisonResults(AbstractType<?> oldVersion, AbstractType<?> newVersion)
+    {
+        assertEquals(0, compare(oldVersion, oldVersion));
+        assertEquals(0, compare(newVersion, newVersion));
+        assertEquals(-1, compare(oldVersion, newVersion));
+        assertEquals(1, compare(newVersion, oldVersion));
+    }
+
+    private int compare(AbstractType<?> left, AbstractType<?> right)
+    {
+        return AbstractTypeVersionComparator.INSTANCE.compare(left, right);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[2/8] cassandra git commit: Copy session properties on cqlsh.py do_login

Posted by bl...@apache.org.
Copy session properties on cqlsh.py do_login

patch by Andres de la Peña; reviewed by Zhao Yang for CASSANDRA-13640


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

Branch: refs/heads/trunk
Commit: 1a2ad2ee047a9ed2c5112376585323b27ebfb7b0
Parents: 5e57dd1
Author: Andrés de la Peña <a....@gmail.com>
Authored: Thu Aug 24 16:56:12 2017 +0100
Committer: Andrés de la Peña <a....@gmail.com>
Committed: Thu Aug 24 16:56:12 2017 +0100

----------------------------------------------------------------------
 CHANGES.txt  | 1 +
 bin/cqlsh.py | 6 ++++++
 2 files changed, 7 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1a2ad2ee/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 2b49bc3..a35cc1b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
  * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
  * Don't let stress write warmup data if n=0 (CASSANDRA-13773)
  * Gossip thread slows down when using batch commit log (CASSANDRA-12966)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1a2ad2ee/bin/cqlsh.py
----------------------------------------------------------------------
diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index c132592..8eb42a3 100644
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -2206,6 +2206,12 @@ class Shell(cmd.Cmd):
         else:
             session = conn.connect()
 
+        # Copy session properties
+        session.default_timeout = self.session.default_timeout
+        session.row_factory = self.session.row_factory
+        session.default_consistency_level = self.session.default_consistency_level
+        session.max_trace_wait = self.session.max_trace_wait
+
         # Update after we've connected in case we fail to authenticate
         self.conn = conn
         self.auth_provider = auth_provider


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[7/8] cassandra git commit: Merge branch cassandra-3.0 into cassandra-3.11

Posted by bl...@apache.org.
Merge branch cassandra-3.0 into cassandra-3.11


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

Branch: refs/heads/cassandra-3.11
Commit: 8d98a99231f38102d0ef572ffe948d705169aae4
Parents: 000007f cf0b6d1
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Aug 24 18:21:08 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Aug 24 18:22:13 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/db/SerializationHeader.java       |  13 +-
 .../db/rows/AbstractTypeVersionComparator.java  | 121 ++++++++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  18 +-
 src/java/org/apache/cassandra/db/rows/Rows.java |  22 ++-
 .../io/sstable/format/SSTableReader.java        |  20 +-
 .../org/apache/cassandra/cql3/CQLTester.java    |  20 +-
 .../cql3/validation/entities/UserTypesTest.java | 151 +++++++++++++++
 .../rows/AbstractTypeVersionComparatorTest.java | 188 +++++++++++++++++++
 9 files changed, 533 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index d39f78c,ba35152..8fb8e2f
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,11 -1,5 +1,12 @@@
 -3.0.15
 +3.11.1
 + * Fix cassandra-stress hang issues when an error during cluster connection happens (CASSANDRA-12938)
 + * Better bootstrap failure message when blocked by (potential) range movement (CASSANDRA-13744)
 + * "ignore" option is ignored in sstableloader (CASSANDRA-13721)
 + * Deadlock in AbstractCommitLogSegmentManager (CASSANDRA-13652)
 + * Duplicate the buffer before passing it to analyser in SASI operation (CASSANDRA-13512)
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
+  * Fix the merging of cells with different user type versions (CASSANDRA-13776)
   * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
   * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
   * Don't let stress write warmup data if n=0 (CASSANDRA-13773)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/src/java/org/apache/cassandra/db/SerializationHeader.java
----------------------------------------------------------------------

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

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Rows.java
index a92bdac,09213a4..3331506
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@@ -26,9 -25,7 +26,8 @@@ import org.apache.cassandra.config.CFMe
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.db.partitions.PartitionStatisticsCollector;
- import org.apache.cassandra.db.rows.Row.Deletion;
  import org.apache.cassandra.utils.MergeIterator;
 +import org.apache.cassandra.utils.WrappedInt;
  
  /**
   * Static utilities to work on Row objects.
@@@ -330,78 -312,20 +330,96 @@@ public abstract class Row
      }
  
      /**
 +     * Returns a row that is obtained from the given existing row by removing everything that is shadowed by data in
 +     * the update row. In other words, produces the smallest result row such that
 +     * {@code merge(result, update, nowInSec) == merge(existing, update, nowInSec)} after filtering by rangeDeletion.
 +     *
 +     * @param existing source row
 +     * @param update shadowing row
 +     * @param rangeDeletion extra {@code DeletionTime} from covering tombstone
 +     * @param nowInSec the current time in seconds (which plays a role during reconciliation
 +     * because deleted cells always have precedence on timestamp equality and deciding if a
 +     * cell is a live or not depends on the current time due to expiring cells).
 +     */
 +    public static Row removeShadowedCells(Row existing, Row update, DeletionTime rangeDeletion, int nowInSec)
 +    {
 +        Row.Builder builder = BTreeRow.sortedBuilder();
 +        Clustering clustering = existing.clustering();
 +        builder.newRow(clustering);
 +
 +        DeletionTime deletion = update.deletion().time();
 +        if (rangeDeletion.supersedes(deletion))
 +            deletion = rangeDeletion;
 +
 +        LivenessInfo existingInfo = existing.primaryKeyLivenessInfo();
 +        if (!deletion.deletes(existingInfo))
 +            builder.addPrimaryKeyLivenessInfo(existingInfo);
 +        Row.Deletion rowDeletion = existing.deletion();
 +        if (!deletion.supersedes(rowDeletion.time()))
 +            builder.addRowDeletion(rowDeletion);
 +
 +        Iterator<ColumnData> a = existing.iterator();
 +        Iterator<ColumnData> b = update.iterator();
 +        ColumnData nexta = a.hasNext() ? a.next() : null, nextb = b.hasNext() ? b.next() : null;
 +        while (nexta != null)
 +        {
 +            int comparison = nextb == null ? -1 : nexta.column.compareTo(nextb.column);
 +            if (comparison <= 0)
 +            {
 +                ColumnData cura = nexta;
 +                ColumnDefinition column = cura.column;
 +                ColumnData curb = comparison == 0 ? nextb : null;
 +                if (column.isSimple())
 +                {
 +                    Cells.addNonShadowed((Cell) cura, (Cell) curb, deletion, builder, nowInSec);
 +                }
 +                else
 +                {
 +                    ComplexColumnData existingData = (ComplexColumnData) cura;
 +                    ComplexColumnData updateData = (ComplexColumnData) curb;
 +
 +                    DeletionTime existingDt = existingData.complexDeletion();
 +                    DeletionTime updateDt = updateData == null ? DeletionTime.LIVE : updateData.complexDeletion();
 +
 +                    DeletionTime maxDt = updateDt.supersedes(deletion) ? updateDt : deletion;
 +                    if (existingDt.supersedes(maxDt))
 +                    {
 +                        builder.addComplexDeletion(column, existingDt);
 +                        maxDt = existingDt;
 +                    }
 +
 +                    Iterator<Cell> existingCells = existingData.iterator();
 +                    Iterator<Cell> updateCells = updateData == null ? null : updateData.iterator();
 +                    Cells.addNonShadowedComplex(column, existingCells, updateCells, maxDt, builder, nowInSec);
 +                }
 +                nexta = a.hasNext() ? a.next() : null;
 +                if (curb != null)
 +                    nextb = b.hasNext() ? b.next() : null;
 +            }
 +            else
 +            {
 +                nextb = b.hasNext() ? b.next() : null;
 +            }
 +        }
 +        Row row = builder.build();
 +        return row != null && !row.isEmpty() ? row : null;
 +    }
++
++    /**
+      * Returns the {@code ColumnDefinition} to use for merging the columns.
+      * If the 2 column definitions are different the latest one will be returned.
+      */
+     private static ColumnDefinition getColumnDefinition(ColumnData cura, ColumnData curb)
+     {
+         if (cura == null)
+             return curb.column;
+ 
+         if (curb == null)
+             return cura.column;
+ 
+         if (AbstractTypeVersionComparator.INSTANCE.compare(cura.column.type, curb.column.type) >= 0)
+             return cura.column;
+ 
+         return curb.column;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/CQLTester.java
index 2542db0,40aec88..c0513ce
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@@ -425,9 -396,34 +425,10 @@@ public abstract class CQLTeste
      public void disableCompaction(String keyspace)
      {
          ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
-         store.disableAutoCompaction();
+         if (store != null)
+             store.disableAutoCompaction();
      }
  
 -    public void flush(boolean forceFlush)
 -    {
 -        if (forceFlush)
 -            flush();
 -    }
 -
 -    @FunctionalInterface
 -    public interface CheckedFunction {
 -        void apply() throws Throwable;
 -    }
 -
 -    /**
 -     * Runs the given function before and after a flush of sstables.  This is useful for checking that behavior is
 -     * the same whether data is in memtables or sstables.
 -     * @param runnable
 -     * @throws Throwable
 -     */
 -    public void beforeAndAfterFlush(CheckedFunction runnable) throws Throwable
 -    {
 -        runnable.apply();
 -        flush();
 -        runnable.apply();
 -    }
 -
      public void compact()
      {
          try

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index b78eb3e,dfc2e5e..9ea5572
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -624,109 -563,156 +624,260 @@@ public class UserTypesTest extends CQLT
      }
  
      @Test
 +    public void testInsertNonFrozenUDT() throws Throwable
 +    {
 +        String typeName = createType("CREATE TYPE %s (a int, b text)");
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, 0, "abc");
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, 0, null);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", null, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +    }
 +
 +    @Test
 +    public void testUpdateNonFrozenUDT() throws Throwable
 +    {
 +        String typeName = createType("CREATE TYPE %s (a int, b text)");
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        // overwrite the whole UDT
 +        execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", 1, "b", "def"), 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "def")));
 +
 +        execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", 0, "b", null), 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", null, "b", "abc"), 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +
 +        // individually set fields to non-null values
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", 1, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "abc")));
 +
 +        execute("UPDATE %s SET v.b = ? WHERE k = ?", "def", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "def")));
 +
 +        execute("UPDATE %s SET v.a = ?, v.b = ? WHERE k = ?", 0, "abc", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("UPDATE %s SET v.b = ?, v.a = ? WHERE k = ?", "abc", 0, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        // individually set fields to null values
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", null, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", 0, 0);
 +        execute("UPDATE %s SET v.b = ? WHERE k = ?", null, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("UPDATE %s SET v.a = ? WHERE k = ?", null, 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        assertInvalid("UPDATE %s SET v.bad = ? FROM %s WHERE k = ?", 0, 0);
 +        assertInvalid("UPDATE %s SET v = ? FROM %s WHERE k = ?", 0, 0);
 +        assertInvalid("UPDATE %s SET v = ? FROM %s WHERE k = ?", userType("a", 1, "b", "abc", "bad", 123), 0);
 +    }
 +
 +    @Test
 +    public void testDeleteNonFrozenUDT() throws Throwable
 +    {
 +        String typeName = createType("CREATE TYPE %s (a int, b text)");
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
 +
 +        execute("DELETE v.b FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
 +
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v.a FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
 +
 +        execute("DELETE v.b FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        // delete both fields at once
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v.a, v.b FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        // same, but reverse field order
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v.b, v.a FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        // delete the whole thing at once
 +        execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
 +        execute("DELETE v FROM %s WHERE k = ?", 0);
 +        assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
 +
 +        assertInvalid("DELETE v.bad FROM %s WHERE k = ?", 0);
 +    }
 +
++    @Test
+     public void testReadAfterAlteringUserTypeNestedWithinSet() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         try
+         {
+             createTable("CREATE TABLE %s (x int PRIMARY KEY, y set<frozen<" + columnType + ">>)");
+             disableCompaction();
+ 
 -            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType(1), userType(2)));
 -            assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1), userType(2))));
++            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType("a", 1), userType("a", 2)));
++            assertRows(execute("SELECT * FROM %s"), row(1, set(userType("a", 1), userType("a", 2))));
+             flush();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, set(userType(1), userType(2))));
++                       row(1, set(userType("a", 1), userType("a", 2))));
+ 
+             execute("ALTER TYPE " + columnType + " ADD b int");
+             execute("UPDATE %s SET y = y + ? WHERE x = 1",
 -                    set(userType(1, 1), userType(1, 2), userType(2, 1)));
++                    set(userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2, "b", 1)));
+ 
+             flush();
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                           row(1, set(userType(1),
 -                                      userType(1, 1),
 -                                      userType(1, 2),
 -                                      userType(2),
 -                                      userType(2, 1))));
++                           row(1, set(userType("a", 1),
++                                      userType("a", 1, "b", 1),
++                                      userType("a", 1, "b", 2),
++                                      userType("a", 2),
++                                      userType("a", 2, "b", 1))));
+ 
+             compact();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, set(userType(1),
 -                                  userType(1, 1),
 -                                  userType(1, 2),
 -                                  userType(2),
 -                                  userType(2, 1))));
++                       row(1, set(userType("a", 1),
++                                  userType("a", 1, "b", 1),
++                                  userType("a", 1, "b", 2),
++                                  userType("a", 2),
++                                  userType("a", 2, "b", 1))));
+         }
+         finally
+         {
+             enableCompaction();
+         }
+     }
+ 
+     @Test
+     public void testReadAfterAlteringUserTypeNestedWithinMap() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         try
+         {
+             createTable("CREATE TABLE %s (x int PRIMARY KEY, y map<frozen<" + columnType + ">, int>)");
+             disableCompaction();
+ 
 -            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType(1), 1, userType(2), 2));
 -            assertRows(execute("SELECT * FROM %s"), row(1, map(userType(1), 1, userType(2), 2)));
++            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType("a", 1), 1, userType("a", 2), 2));
++            assertRows(execute("SELECT * FROM %s"), row(1, map(userType("a", 1), 1, userType("a", 2), 2)));
+             flush();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, map(userType(1), 1, userType(2), 2)));
++                       row(1, map(userType("a", 1), 1, userType("a", 2), 2)));
+ 
+             execute("ALTER TYPE " + columnType + " ADD b int");
+             execute("UPDATE %s SET y = y + ? WHERE x = 1",
 -                    map(userType(1, 1), 1, userType(1, 2), 1, userType(2, 1), 2));
++                    map(userType("a", 1, "b", 1), 1, userType("a", 1, "b", 2), 1, userType("a", 2, "b", 1), 2));
+ 
+             flush();
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                           row(1, map(userType(1), 1,
 -                                      userType(1, 1), 1,
 -                                      userType(1, 2), 1,
 -                                      userType(2), 2,
 -                                      userType(2, 1), 2)));
++                           row(1, map(userType("a", 1), 1,
++                                      userType("a", 1, "b", 1), 1,
++                                      userType("a", 1, "b", 2), 1,
++                                      userType("a", 2), 2,
++                                      userType("a", 2, "b", 1), 2)));
+ 
+             compact();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, map(userType(1), 1,
 -                                  userType(1, 1), 1,
 -                                  userType(1, 2), 1,
 -                                  userType(2), 2,
 -                                  userType(2, 1), 2)));
++                       row(1, map(userType("a", 1), 1,
++                                  userType("a", 1, "b", 1), 1,
++                                  userType("a", 1, "b", 2), 1,
++                                  userType("a", 2), 2,
++                                  userType("a", 2, "b", 1), 2)));
+         }
+         finally
+         {
+             enableCompaction();
+         }
+     }
+ 
+     @Test
+     public void testReadAfterAlteringUserTypeNestedWithinList() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         try
+         {
+             createTable("CREATE TABLE %s (x int PRIMARY KEY, y list<frozen<" + columnType + ">>)");
+             disableCompaction();
+ 
 -            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType(1), userType(2)));
 -            assertRows(execute("SELECT * FROM %s"), row(1, list(userType(1), userType(2))));
++            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType("a", 1), userType("a", 2)));
++            assertRows(execute("SELECT * FROM %s"), row(1, list(userType("a", 1), userType("a", 2))));
+             flush();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, list(userType(1), userType(2))));
++                       row(1, list(userType("a", 1), userType("a", 2))));
+ 
+             execute("ALTER TYPE " + columnType + " ADD b int");
+             execute("UPDATE %s SET y = y + ? WHERE x = 1",
 -                    list(userType(1, 1), userType(1, 2), userType(2, 1)));
++                    list(userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2, "b", 1)));
+ 
+             flush();
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                           row(1, list(userType(1),
 -                                       userType(2),
 -                                       userType(1, 1),
 -                                       userType(1, 2),
 -                                       userType(2, 1))));
++                           row(1, list(userType("a", 1),
++                                       userType("a", 2),
++                                       userType("a", 1, "b", 1),
++                                       userType("a", 1, "b", 2),
++                                       userType("a", 2, "b", 1))));
+ 
+             compact();
+ 
+             assertRows(execute("SELECT * FROM %s WHERE x = 1"),
 -                       row(1, list(userType(1),
 -                                   userType(2),
 -                                   userType(1, 1),
 -                                   userType(1, 2),
 -                                   userType(2, 1))));
++                       row(1, list(userType("a", 1),
++                                   userType("a", 2),
++                                   userType("a", 1, "b", 1),
++                                   userType("a", 1, "b", 2),
++                                   userType("a", 2, "b", 1))));
+         }
+         finally
+         {
+             enableCompaction();
+         }
+     }
+ 
+     @Test
+     public void testAlteringUserTypeNestedWithinSetWithView() throws Throwable
+     {
+         String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+ 
+         createTable("CREATE TABLE %s (pk int, c int, v int, s set<frozen<" + columnType + ">>, PRIMARY KEY (pk, c))");
+         execute("CREATE MATERIALIZED VIEW " + keyspace() + ".view1 AS SELECT c, pk, v FROM %s WHERE pk IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL PRIMARY KEY (c, pk)");
+ 
 -        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType(1), userType(2)));
++        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType("a", 1), userType("a", 2)));
+         flush();
+ 
+         execute("ALTER TYPE " + columnType + " ADD b int");
+         execute("UPDATE %s SET s = s + ?, v = ? WHERE pk = ? AND c = ?",
 -                set(userType(1, 1), userType(1, 2), userType(2, 1)), 2, 1, 1);
++                set(userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2, "b", 1)), 2, 1, 1);
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = ? AND c = ?", 1, 1),
 -                       row(1, 1,set(userType(1), userType(1, 1), userType(1, 2), userType(2), userType(2, 1)), 2));
++                       row(1, 1,set(userType("a", 1), userType("a", 1, "b", 1), userType("a", 1, "b", 2), userType("a", 2), userType("a", 2, "b", 1)), 2));
+     }
+ 
      private String typeWithKs(String type1)
      {
          return keyspace() + '.' + type1;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/8d98a992/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
index 0000000,ad0c05c..7170696
mode 000000,100644..100644
--- a/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
+++ b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
@@@ -1,0 -1,184 +1,188 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.cassandra.db.rows;
+ 
+ import java.nio.ByteBuffer;
+ import java.util.Set;
+ 
+ import org.junit.After;
+ import org.junit.Before;
+ import org.junit.Test;
+ 
+ import org.apache.cassandra.db.marshal.*;
+ 
+ import static java.util.Arrays.asList;
++import static org.apache.cassandra.cql3.FieldIdentifier.forUnquoted;
+ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+ import static org.junit.Assert.assertEquals;
+ import static org.junit.Assert.fail;
+ 
+ public class AbstractTypeVersionComparatorTest
+ {
+     private UserType udtWith2Fields;
+     private UserType udtWith3Fields;
+ 
+     @Before
+     public void setUp()
+     {
+         udtWith2Fields = new UserType("ks",
+                                       bytes("myType"),
 -                                      asList(bytes("a"), bytes("b")),
 -                                      asList(Int32Type.instance, Int32Type.instance));
++                                      asList(forUnquoted("a"), forUnquoted("b")),
++                                      asList(Int32Type.instance, Int32Type.instance),
++                                      false);
+         udtWith3Fields = new UserType("ks",
+                                       bytes("myType"),
 -                                      asList(bytes("a"), bytes("b"), bytes("c")),
 -                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance));
++                                      asList(forUnquoted("a"), forUnquoted("b"), forUnquoted("c")),
++                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance),
++                                      false);
+     }
+ 
+     @After
+     public void tearDown()
+     {
+         udtWith2Fields = null;
+         udtWith3Fields = null;
+     }
+ 
+     @Test
+     public void testWithTuples()
+     {
+         checkComparisonResults(new TupleType(asList(Int32Type.instance, Int32Type.instance)),
+                                new TupleType(asList(Int32Type.instance, Int32Type.instance, Int32Type.instance)));
+     }
+ 
+     @Test
+     public void testWithUDTs()
+     {
+         checkComparisonResults(udtWith2Fields, udtWith3Fields);
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinSet()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             SetType<ByteBuffer> set1 = SetType.getInstance(udtWith2Fields, isMultiCell);
+             SetType<ByteBuffer> set2 = SetType.getInstance(udtWith3Fields, isMultiCell);
+             checkComparisonResults(set1, set2);
+         }
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinList()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             ListType<ByteBuffer> list1 = ListType.getInstance(udtWith2Fields, isMultiCell);
+             ListType<ByteBuffer> list2 = ListType.getInstance(udtWith3Fields, isMultiCell);
+             checkComparisonResults(list1, list2);
+         }
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinMap()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             MapType<ByteBuffer, Integer> map1 = MapType.getInstance(udtWith2Fields, Int32Type.instance, isMultiCell);
+             MapType<ByteBuffer, Integer> map2 = MapType.getInstance(udtWith3Fields, Int32Type.instance, isMultiCell);
+             checkComparisonResults(map1, map2);
+         }
+ 
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             MapType<Integer, ByteBuffer> map1 = MapType.getInstance(Int32Type.instance, udtWith2Fields, isMultiCell);
+             MapType<Integer, ByteBuffer> map2 = MapType.getInstance(Int32Type.instance, udtWith3Fields, isMultiCell);
+             checkComparisonResults(map1, map2);
+         }
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinTuple()
+     {
+         TupleType tuple1 = new TupleType(asList(udtWith2Fields, Int32Type.instance));
+         TupleType tuple2 = new TupleType(asList(udtWith3Fields, Int32Type.instance));
+         checkComparisonResults(tuple1, tuple2);
+     }
+ 
+     @Test
+     public void testWithUDTsNestedWithinComposite()
+     {
+         CompositeType composite1 = CompositeType.getInstance(asList(udtWith2Fields, Int32Type.instance));
+         CompositeType composite2 = CompositeType.getInstance(asList(udtWith3Fields, Int32Type.instance));
+         checkComparisonResults(composite1, composite2);
+     }
+ 
+     @Test
+     public void testWithDeeplyNestedUDT()
+     {
+         for (boolean isMultiCell : new boolean[]{false, true})
+         {
+             ListType<Set<ByteBuffer>> list1 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith2Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+             ListType<Set<ByteBuffer>> list2 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith3Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+             checkComparisonResults(list1, list2);
+         }
+     }
+ 
+     @Test
+     public void testInvalidComparison()
+     {
 -        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type) and org.apache.cassandra.db.marshal.Int32Type",
++        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.FrozenType(org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type)) and org.apache.cassandra.db.marshal.Int32Type",
+                                 udtWith2Fields,
+                                 Int32Type.instance);
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 SetType.getInstance(UTF8Type.instance, true),
+                                 SetType.getInstance(InetAddressType.instance, true));
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 ListType.getInstance(UTF8Type.instance, true),
+                                 ListType.getInstance(InetAddressType.instance, true));
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 MapType.getInstance(UTF8Type.instance, IntegerType.instance, true),
+                                 MapType.getInstance(InetAddressType.instance, IntegerType.instance, true));
+         assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                 MapType.getInstance(IntegerType.instance, UTF8Type.instance, true),
+                                 MapType.getInstance(IntegerType.instance, InetAddressType.instance, true));
+     }
+ 
+     private void assertInvalidComparison(String expectedMessage, AbstractType<?> oldVersion, AbstractType<?> newVersion)
+     {
+         try
+         {
+             checkComparisonResults(oldVersion, newVersion);
+             fail("comparison doesn't throw expected IllegalArgumentException: " + expectedMessage);
+         }
+         catch (IllegalArgumentException e)
+         {
++            System.out.println(e.getMessage());
+             assertEquals(e.getMessage(), expectedMessage);
+         }
+     }
+ 
+     private void checkComparisonResults(AbstractType<?> oldVersion, AbstractType<?> newVersion)
+     {
+         assertEquals(0, compare(oldVersion, oldVersion));
+         assertEquals(0, compare(newVersion, newVersion));
+         assertEquals(-1, compare(oldVersion, newVersion));
+         assertEquals(1, compare(newVersion, oldVersion));
+     }
+ 
 -    private int compare(AbstractType<?> left, AbstractType<?> right)
++    private static int compare(AbstractType<?> left, AbstractType<?> right)
+     {
+         return AbstractTypeVersionComparator.INSTANCE.compare(left, right);
+     }
+ }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[8/8] cassandra git commit: Merge branch cassandra-3.11 into trunk

Posted by bl...@apache.org.
Merge branch cassandra-3.11 into trunk


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

Branch: refs/heads/trunk
Commit: 4838e81a64eb118d0f86463b61b43c450a199dc8
Parents: 41ef972 8d98a99
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Aug 24 18:23:02 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Aug 24 18:24:02 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/db/SerializationHeader.java       |  13 +-
 .../db/rows/AbstractTypeVersionComparator.java  | 121 ++++++++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  18 +-
 src/java/org/apache/cassandra/db/rows/Rows.java |  20 +-
 .../io/sstable/format/SSTableReader.java        |  20 +-
 .../org/apache/cassandra/cql3/CQLTester.java    |  20 +-
 .../cql3/validation/entities/UserTypesTest.java | 151 +++++++++++++++
 .../rows/AbstractTypeVersionComparatorTest.java | 188 +++++++++++++++++++
 9 files changed, 532 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/4838e81a/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4838e81a/src/java/org/apache/cassandra/db/SerializationHeader.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/SerializationHeader.java
index 1f937f8,ddf83b7..4266629
--- a/src/java/org/apache/cassandra/db/SerializationHeader.java
+++ b/src/java/org/apache/cassandra/db/SerializationHeader.java
@@@ -84,8 -84,9 +84,9 @@@ public class SerializationHeade
          // our stats merging on the compacted files headers, which as we just said can be somewhat inaccurate,
          // but rather on their stats stored in StatsMetadata that are fully accurate.
          EncodingStats.Collector stats = new EncodingStats.Collector();
 -        PartitionColumns.Builder columns = PartitionColumns.builder();
 -        // We need to order the SSTables by descending generation to be sure that we use latest column definitions.
 +        RegularAndStaticColumns.Builder columns = RegularAndStaticColumns.builder();
-         for (SSTableReader sstable : sstables)
++        // We need to order the SSTables by descending generation to be sure that we use latest column metadata.
+         for (SSTableReader sstable : orderByDescendingGeneration(sstables))
          {
              stats.updateTimestamp(sstable.getMinTimestamp());
              stats.updateLocalDeletionTime(sstable.getMinLocalDeletionTime());
@@@ -95,9 -99,19 +96,19 @@@
          return new SerializationHeader(true, metadata, columns.build(), stats.get());
      }
  
+     private static Collection<SSTableReader> orderByDescendingGeneration(Collection<SSTableReader> sstables)
+     {
+         if (sstables.size() < 2)
+             return sstables;
+ 
+         List<SSTableReader> readers = new ArrayList<>(sstables);
+         readers.sort(SSTableReader.generationReverseComparator);
+         return readers;
+     }
+ 
      public SerializationHeader(boolean isForSSTable,
 -                               CFMetaData metadata,
 -                               PartitionColumns columns,
 +                               TableMetadata metadata,
 +                               RegularAndStaticColumns columns,
                                 EncodingStats stats)
      {
          this(isForSSTable,

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4838e81a/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Row.java
index 7449e51,60ee4d4..5999864
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@@ -721,10 -721,25 +721,25 @@@ public interface Row extends Unfiltered
  
              public void reduce(int idx, ColumnData data)
              {
-                 column = data.column();
 -                if (useColumnDefinition(data.column()))
++                if (useColumnMetadata(data.column()))
+                     column = data.column();
+ 
                  versions.add(data);
              }
  
+             /**
 -             * Determines it the {@code ColumnDefinition} is the one that should be used.
 -             * @param dataColumn the {@code ColumnDefinition} to use.
 -             * @return {@code true} if the {@code ColumnDefinition} is the one that should be used, {@code false} otherwise.
++             * Determines it the {@code ColumnMetadata} is the one that should be used.
++             * @param dataColumn the {@code ColumnMetadata} to use.
++             * @return {@code true} if the {@code ColumnMetadata} is the one that should be used, {@code false} otherwise.
+              */
 -            private boolean useColumnDefinition(ColumnDefinition dataColumn)
++            private boolean useColumnMetadata(ColumnMetadata dataColumn)
+             {
+                 if (column == null)
+                     return true;
+ 
+                 return AbstractTypeVersionComparator.INSTANCE.compare(column.type, dataColumn.type) < 0;
+             }
+ 
              protected ColumnData getReduced()
              {
                  if (column.isSimple())

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4838e81a/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Rows.java
index 8c7143a,3331506..79c46d0
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@@ -297,7 -297,8 +297,7 @@@ public abstract class Row
              int comparison = nexta == null ? 1 : nextb == null ? -1 : nexta.column.compareTo(nextb.column);
              ColumnData cura = comparison <= 0 ? nexta : null;
              ColumnData curb = comparison >= 0 ? nextb : null;
-             ColumnMetadata column = (cura != null ? cura : curb).column;
 -            ColumnDefinition column = getColumnDefinition(cura, curb);
 -
++            ColumnMetadata column = getColumnMetadata(cura, curb);
              if (column.isSimple())
              {
                  timeDelta = Math.min(timeDelta, Cells.reconcile((Cell) cura, (Cell) curb, deletion, builder, nowInSec));
@@@ -403,4 -404,22 +403,22 @@@
          Row row = builder.build();
          return row != null && !row.isEmpty() ? row : null;
      }
+ 
+     /**
 -     * Returns the {@code ColumnDefinition} to use for merging the columns.
 -     * If the 2 column definitions are different the latest one will be returned.
++     * Returns the {@code ColumnMetadata} to use for merging the columns.
++     * If the 2 column metadata are different the latest one will be returned.
+      */
 -    private static ColumnDefinition getColumnDefinition(ColumnData cura, ColumnData curb)
++    private static ColumnMetadata getColumnMetadata(ColumnData cura, ColumnData curb)
+     {
+         if (cura == null)
+             return curb.column;
+ 
+         if (curb == null)
+             return cura.column;
+ 
+         if (AbstractTypeVersionComparator.INSTANCE.compare(cura.column.type, curb.column.type) >= 0)
+             return cura.column;
+ 
+         return curb.column;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4838e81a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
----------------------------------------------------------------------

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


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[3/8] cassandra git commit: Fix the merging of cells with different user type versions

Posted by bl...@apache.org.
Fix the merging of cells with different user type versions

patch by Benjamin Lerer; reviewed by Robert Stupp  for CASSANDRA-13776


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

Branch: refs/heads/cassandra-3.11
Commit: cf0b6d107bade419dada49a5da40d2579c80ade8
Parents: 1a2ad2e
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Aug 24 18:19:28 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Aug 24 18:19:28 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cassandra/db/SerializationHeader.java       |  13 +-
 .../cassandra/db/marshal/AbstractType.java      |  10 +
 .../apache/cassandra/db/marshal/TupleType.java  |   5 +
 .../apache/cassandra/db/marshal/UserType.java   |  10 +
 .../db/rows/AbstractTypeVersionComparator.java  | 121 ++++++++++++
 src/java/org/apache/cassandra/db/rows/Row.java  |  18 +-
 src/java/org/apache/cassandra/db/rows/Rows.java |  21 ++-
 .../io/sstable/format/SSTableReader.java        |  23 +--
 .../org/apache/cassandra/cql3/CQLTester.java    |  20 +-
 .../cql3/validation/entities/UserTypesTest.java | 151 +++++++++++++++
 .../rows/AbstractTypeVersionComparatorTest.java | 184 +++++++++++++++++++
 12 files changed, 555 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index a35cc1b..ba35152 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Fix the merging of cells with different user type versions (CASSANDRA-13776)
  * Copy session properties on cqlsh.py do_login (CASSANDRA-13640)
  * Potential AssertionError during ReadRepair of range tombstone and partition deletions (CASSANDRA-13719)
  * Don't let stress write warmup data if n=0 (CASSANDRA-13773)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/SerializationHeader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/SerializationHeader.java b/src/java/org/apache/cassandra/db/SerializationHeader.java
index 19dad95..494c2a3 100644
--- a/src/java/org/apache/cassandra/db/SerializationHeader.java
+++ b/src/java/org/apache/cassandra/db/SerializationHeader.java
@@ -108,7 +108,8 @@ public class SerializationHeader
         // but rather on their stats stored in StatsMetadata that are fully accurate.
         EncodingStats.Collector stats = new EncodingStats.Collector();
         PartitionColumns.Builder columns = PartitionColumns.builder();
-        for (SSTableReader sstable : sstables)
+        // We need to order the SSTables by descending generation to be sure that we use latest column definitions.
+        for (SSTableReader sstable : orderByDescendingGeneration(sstables))
         {
             stats.updateTimestamp(sstable.getMinTimestamp());
             stats.updateLocalDeletionTime(sstable.getMinLocalDeletionTime());
@@ -121,6 +122,16 @@ public class SerializationHeader
         return new SerializationHeader(true, metadata, columns.build(), stats.get());
     }
 
+    private static Collection<SSTableReader> orderByDescendingGeneration(Collection<SSTableReader> sstables)
+    {
+        if (sstables.size() < 2)
+            return sstables;
+
+        List<SSTableReader> readers = new ArrayList<>(sstables);
+        readers.sort(SSTableReader.generationReverseComparator);
+        return readers;
+    }
+
     public SerializationHeader(boolean isForSSTable,
                                CFMetaData metadata,
                                PartitionColumns columns,

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 77e0971..20062bd 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -325,6 +325,16 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
         return false;
     }
 
+    public boolean isTuple()
+    {
+        return false;
+    }
+
+    public boolean isUDT()
+    {
+        return false;
+    }
+
     public AbstractType<?> freeze()
     {
         return this;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java
index 2d6363e..5c74332 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -347,4 +347,9 @@ public class TupleType extends AbstractType<ByteBuffer>
     {
         return getClass().getName() + TypeParser.stringifyTypeParameters(types, true);
     }
+
+    public boolean isTuple()
+    {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UserType.java b/src/java/org/apache/cassandra/db/marshal/UserType.java
index b91dbf8..03545ca 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -232,4 +232,14 @@ public class UserType extends TupleType
     {
         return serializer;
     }
+
+    public boolean isTuple()
+    {
+        return false;
+    }
+
+    public boolean isUDT()
+    {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java b/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
new file mode 100644
index 0000000..e47f681
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/rows/AbstractTypeVersionComparator.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.db.rows;
+
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.cassandra.db.marshal.*;
+
+/**
+ * A {@code Comparator} use to determine which version of a type should be used.
+ * <p>In the case of UDTs it is possible to have 2 versions or more of the same type, if some fields has been added to
+ * the type. To avoid problems the latest type need to be used.</p>
+ */
+final class AbstractTypeVersionComparator implements Comparator<AbstractType<?>>
+{
+    public static final Comparator<AbstractType<?>> INSTANCE = new AbstractTypeVersionComparator();
+
+    private AbstractTypeVersionComparator()
+    {
+    }
+
+    @Override
+    public int compare(AbstractType<?> type, AbstractType<?> otherType)
+    {
+        if (!type.getClass().equals(otherType.getClass()))
+            throw new IllegalArgumentException(String.format("Trying to compare 2 different types: %s and %s",
+                                                             type,
+                                                             otherType));
+
+        if (type.equals(otherType))
+            return 0;
+
+        // The only case where 2 types can differ is if they contains some UDTs and one of them has more
+        // fields (due to an ALTER type ADD) than in the other type. In this case we need to pick the type with
+        // the bigger amount of fields.
+        if (type.isUDT())
+            return compareUserType((UserType) type, (UserType) otherType);
+
+        if (type.isTuple())
+            return compareTuple((TupleType) type, (TupleType) otherType);
+
+        if (type.isCollection())
+            return compareCollectionTypes(type, otherType);
+
+        if (type instanceof CompositeType)
+            return compareCompositeTypes((CompositeType) type, (CompositeType) otherType);
+
+        // In theory we should never reach that point but to be on the safe side we allow it.
+        return 0;
+    }
+
+    private int compareCompositeTypes(CompositeType type, CompositeType otherType)
+    {
+        List<AbstractType<?>> types = type.getComponents();
+        List<AbstractType<?>> otherTypes = otherType.getComponents();
+
+        if (types.size() != otherTypes.size())
+            return Integer.compare(types.size(), otherTypes.size());
+
+        for (int i = 0, m = type.componentsCount(); i < m ; i++)
+        {
+            int test = compare(types.get(i), otherTypes.get(i));
+            if (test != 0);
+                return test;
+        }
+        return 0;
+    }
+
+    private int compareCollectionTypes(AbstractType<?> type, AbstractType<?> otherType)
+    {
+        if (type instanceof MapType)
+            return compareMapType((MapType<?, ?>) type, (MapType<?, ?>) otherType);
+
+        if (type instanceof SetType)
+            return compare(((SetType<?>) type).getElementsType(), ((SetType<?>) otherType).getElementsType());
+
+        return compare(((ListType<?>) type).getElementsType(), ((ListType<?>) otherType).getElementsType());
+    }
+
+    private int compareMapType(MapType<?, ?> type, MapType<?, ?> otherType)
+    {
+        int test = compare(type.getKeysType(), otherType.getKeysType());
+        return test != 0 ? test : compare(type.getValuesType(), otherType.getValuesType());
+    }
+
+    private int compareUserType(UserType type, UserType otherType)
+    {
+        return compareTuple(type, otherType);
+    }
+
+    private int compareTuple(TupleType type, TupleType otherType)
+    {
+        if (type.size() != otherType.size())
+            return Integer.compare(type.size(), otherType.size());
+
+        int test = 0;
+        int i = 0;
+        while (test == 0 && i < type.size())
+        {
+            test = compare(type.type(i), otherType.type(i));
+            i++;
+        }
+        return test;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/Row.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Row.java b/src/java/org/apache/cassandra/db/rows/Row.java
index a61f365..9ab1f09 100644
--- a/src/java/org/apache/cassandra/db/rows/Row.java
+++ b/src/java/org/apache/cassandra/db/rows/Row.java
@@ -601,10 +601,25 @@ public interface Row extends Unfiltered, Collection<ColumnData>
 
             public void reduce(int idx, ColumnData data)
             {
-                column = data.column();
+                if (useColumnDefinition(data.column()))
+                    column = data.column();
+
                 versions.add(data);
             }
 
+            /**
+             * Determines it the {@code ColumnDefinition} is the one that should be used.
+             * @param dataColumn the {@code ColumnDefinition} to use.
+             * @return {@code true} if the {@code ColumnDefinition} is the one that should be used, {@code false} otherwise.
+             */
+            private boolean useColumnDefinition(ColumnDefinition dataColumn)
+            {
+                if (column == null)
+                    return true;
+
+                return AbstractTypeVersionComparator.INSTANCE.compare(column.type, dataColumn.type) < 0;
+            }
+
             protected ColumnData getReduced()
             {
                 if (column.isSimple())
@@ -654,6 +669,7 @@ public interface Row extends Unfiltered, Collection<ColumnData>
 
             protected void onKeyChange()
             {
+                column = null;
                 versions.clear();
             }
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/db/rows/Rows.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/rows/Rows.java b/src/java/org/apache/cassandra/db/rows/Rows.java
index e325091..09213a4 100644
--- a/src/java/org/apache/cassandra/db/rows/Rows.java
+++ b/src/java/org/apache/cassandra/db/rows/Rows.java
@@ -279,7 +279,8 @@ public abstract class Rows
             int comparison = nexta == null ? 1 : nextb == null ? -1 : nexta.column.compareTo(nextb.column);
             ColumnData cura = comparison <= 0 ? nexta : null;
             ColumnData curb = comparison >= 0 ? nextb : null;
-            ColumnDefinition column = (cura != null ? cura : curb).column;
+            ColumnDefinition column = getColumnDefinition(cura, curb);
+
             if (column.isSimple())
             {
                 timeDelta = Math.min(timeDelta, Cells.reconcile((Cell) cura, (Cell) curb, deletion, builder, nowInSec));
@@ -309,4 +310,22 @@ public abstract class Rows
         }
         return timeDelta;
     }
+
+    /**
+     * Returns the {@code ColumnDefinition} to use for merging the columns.
+     * If the 2 column definitions are different the latest one will be returned.
+     */
+    private static ColumnDefinition getColumnDefinition(ColumnData cura, ColumnData curb)
+    {
+        if (cura == null)
+            return curb.column;
+
+        if (curb == null)
+            return cura.column;
+
+        if (AbstractTypeVersionComparator.INSTANCE.compare(cura.column.type, curb.column.type) >= 0)
+            return cura.column;
+
+        return curb.column;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
index f38738d..cd41b5b 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
@@ -45,7 +45,6 @@ import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.commitlog.ReplayPosition;
 import org.apache.cassandra.db.filter.ColumnFilter;
 import org.apache.cassandra.db.rows.SliceableUnfilteredRowIterator;
 import org.apache.cassandra.dht.AbstractBounds;
@@ -141,26 +140,14 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
     }
     private static final RateLimiter meterSyncThrottle = RateLimiter.create(100.0);
 
-    public static final Comparator<SSTableReader> maxTimestampComparator = new Comparator<SSTableReader>()
-    {
-        public int compare(SSTableReader o1, SSTableReader o2)
-        {
-            long ts1 = o1.getMaxTimestamp();
-            long ts2 = o2.getMaxTimestamp();
-            return (ts1 > ts2 ? -1 : (ts1 == ts2 ? 0 : 1));
-        }
-    };
+    public static final Comparator<SSTableReader> maxTimestampComparator = (o1, o2) -> Long.compare(o1.getMaxTimestamp(), o2.getMaxTimestamp());
 
     // it's just an object, which we use regular Object equality on; we introduce a special class just for easy recognition
     public static final class UniqueIdentifier {}
 
-    public static final Comparator<SSTableReader> sstableComparator = new Comparator<SSTableReader>()
-    {
-        public int compare(SSTableReader o1, SSTableReader o2)
-        {
-            return o1.first.compareTo(o2.first);
-        }
-    };
+    public static final Comparator<SSTableReader> sstableComparator = (o1, o2) -> o1.first.compareTo(o2.first);
+
+    public static final Comparator<SSTableReader> generationReverseComparator = (o1, o2) -> -Integer.compare(o1.descriptor.generation, o2.descriptor.generation);
 
     public static final Ordering<SSTableReader> sstableOrdering = Ordering.from(sstableComparator);
 
@@ -1717,7 +1704,7 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
     /**
      * Direct I/O SSTableScanner over an iterator of bounds.
      *
-     * @param bounds the keys to cover
+     * @param rangeIterator the keys to cover
      * @return A Scanner for seeking over the rows of the SSTable.
      */
     public abstract ISSTableScanner getScanner(Iterator<AbstractBounds<PartitionPosition>> rangeIterator);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/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 ba23c67..40aec88 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -396,7 +396,8 @@ public abstract class CQLTester
     public void disableCompaction(String keyspace)
     {
         ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
-        store.disableAutoCompaction();
+        if (store != null)
+            store.disableAutoCompaction();
     }
 
     public void flush(boolean forceFlush)
@@ -437,6 +438,23 @@ public abstract class CQLTester
         }
     }
 
+    public void disableCompaction()
+    {
+        disableCompaction(KEYSPACE);
+    }
+
+    public void enableCompaction(String keyspace)
+    {
+        ColumnFamilyStore store = getCurrentColumnFamilyStore(keyspace);
+        if (store != null)
+            store.enableAutoCompaction();
+    }
+
+    public void enableCompaction()
+    {
+        enableCompaction(KEYSPACE);
+    }
+
     public void cleanupCache()
     {
         ColumnFamilyStore store = getCurrentColumnFamilyStore();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index c279e00..dfc2e5e 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -562,6 +562,157 @@ public class UserTypesTest extends CQLTester
         assertInvalidMessage("Cannot drop user type " + typeWithKs(t), "DROP TYPE " + typeWithKs(t) + ';');
     }
 
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinSet() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y set<frozen<" + columnType + ">>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType(1), userType(2)));
+            assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1), userType(2))));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, set(userType(1), userType(2))));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    set(userType(1, 1), userType(1, 2), userType(2, 1)));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, set(userType(1),
+                                      userType(1, 1),
+                                      userType(1, 2),
+                                      userType(2),
+                                      userType(2, 1))));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, set(userType(1),
+                                  userType(1, 1),
+                                  userType(1, 2),
+                                  userType(2),
+                                  userType(2, 1))));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinMap() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y map<frozen<" + columnType + ">, int>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", map(userType(1), 1, userType(2), 2));
+            assertRows(execute("SELECT * FROM %s"), row(1, map(userType(1), 1, userType(2), 2)));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, map(userType(1), 1, userType(2), 2)));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    map(userType(1, 1), 1, userType(1, 2), 1, userType(2, 1), 2));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, map(userType(1), 1,
+                                      userType(1, 1), 1,
+                                      userType(1, 2), 1,
+                                      userType(2), 2,
+                                      userType(2, 1), 2)));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, map(userType(1), 1,
+                                  userType(1, 1), 1,
+                                  userType(1, 2), 1,
+                                  userType(2), 2,
+                                  userType(2, 1), 2)));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testReadAfterAlteringUserTypeNestedWithinList() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        try
+        {
+            createTable("CREATE TABLE %s (x int PRIMARY KEY, y list<frozen<" + columnType + ">>)");
+            disableCompaction();
+
+            execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType(1), userType(2)));
+            assertRows(execute("SELECT * FROM %s"), row(1, list(userType(1), userType(2))));
+            flush();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, list(userType(1), userType(2))));
+
+            execute("ALTER TYPE " + columnType + " ADD b int");
+            execute("UPDATE %s SET y = y + ? WHERE x = 1",
+                    list(userType(1, 1), userType(1, 2), userType(2, 1)));
+
+            flush();
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                           row(1, list(userType(1),
+                                       userType(2),
+                                       userType(1, 1),
+                                       userType(1, 2),
+                                       userType(2, 1))));
+
+            compact();
+
+            assertRows(execute("SELECT * FROM %s WHERE x = 1"),
+                       row(1, list(userType(1),
+                                   userType(2),
+                                   userType(1, 1),
+                                   userType(1, 2),
+                                   userType(2, 1))));
+        }
+        finally
+        {
+            enableCompaction();
+        }
+    }
+
+    @Test
+    public void testAlteringUserTypeNestedWithinSetWithView() throws Throwable
+    {
+        String columnType = typeWithKs(createType("CREATE TYPE %s (a int)"));
+
+        createTable("CREATE TABLE %s (pk int, c int, v int, s set<frozen<" + columnType + ">>, PRIMARY KEY (pk, c))");
+        execute("CREATE MATERIALIZED VIEW " + keyspace() + ".view1 AS SELECT c, pk, v FROM %s WHERE pk IS NOT NULL AND c IS NOT NULL AND v IS NOT NULL PRIMARY KEY (c, pk)");
+
+        execute("INSERT INTO %s (pk, c, v, s) VALUES(?, ?, ?, ?)", 1, 1, 1, set(userType(1), userType(2)));
+        flush();
+
+        execute("ALTER TYPE " + columnType + " ADD b int");
+        execute("UPDATE %s SET s = s + ?, v = ? WHERE pk = ? AND c = ?",
+                set(userType(1, 1), userType(1, 2), userType(2, 1)), 2, 1, 1);
+
+        assertRows(execute("SELECT * FROM %s WHERE pk = ? AND c = ?", 1, 1),
+                       row(1, 1,set(userType(1), userType(1, 1), userType(1, 2), userType(2), userType(2, 1)), 2));
+    }
+
     private String typeWithKs(String type1)
     {
         return keyspace() + '.' + type1;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/cf0b6d10/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
new file mode 100644
index 0000000..ad0c05c
--- /dev/null
+++ b/test/unit/org/apache/cassandra/db/rows/AbstractTypeVersionComparatorTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.db.rows;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.*;
+
+import static java.util.Arrays.asList;
+import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class AbstractTypeVersionComparatorTest
+{
+    private UserType udtWith2Fields;
+    private UserType udtWith3Fields;
+
+    @Before
+    public void setUp()
+    {
+        udtWith2Fields = new UserType("ks",
+                                      bytes("myType"),
+                                      asList(bytes("a"), bytes("b")),
+                                      asList(Int32Type.instance, Int32Type.instance));
+        udtWith3Fields = new UserType("ks",
+                                      bytes("myType"),
+                                      asList(bytes("a"), bytes("b"), bytes("c")),
+                                      asList(Int32Type.instance, Int32Type.instance, Int32Type.instance));
+    }
+
+    @After
+    public void tearDown()
+    {
+        udtWith2Fields = null;
+        udtWith3Fields = null;
+    }
+
+    @Test
+    public void testWithTuples()
+    {
+        checkComparisonResults(new TupleType(asList(Int32Type.instance, Int32Type.instance)),
+                               new TupleType(asList(Int32Type.instance, Int32Type.instance, Int32Type.instance)));
+    }
+
+    @Test
+    public void testWithUDTs()
+    {
+        checkComparisonResults(udtWith2Fields, udtWith3Fields);
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinSet()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            SetType<ByteBuffer> set1 = SetType.getInstance(udtWith2Fields, isMultiCell);
+            SetType<ByteBuffer> set2 = SetType.getInstance(udtWith3Fields, isMultiCell);
+            checkComparisonResults(set1, set2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinList()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            ListType<ByteBuffer> list1 = ListType.getInstance(udtWith2Fields, isMultiCell);
+            ListType<ByteBuffer> list2 = ListType.getInstance(udtWith3Fields, isMultiCell);
+            checkComparisonResults(list1, list2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinMap()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            MapType<ByteBuffer, Integer> map1 = MapType.getInstance(udtWith2Fields, Int32Type.instance, isMultiCell);
+            MapType<ByteBuffer, Integer> map2 = MapType.getInstance(udtWith3Fields, Int32Type.instance, isMultiCell);
+            checkComparisonResults(map1, map2);
+        }
+
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            MapType<Integer, ByteBuffer> map1 = MapType.getInstance(Int32Type.instance, udtWith2Fields, isMultiCell);
+            MapType<Integer, ByteBuffer> map2 = MapType.getInstance(Int32Type.instance, udtWith3Fields, isMultiCell);
+            checkComparisonResults(map1, map2);
+        }
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinTuple()
+    {
+        TupleType tuple1 = new TupleType(asList(udtWith2Fields, Int32Type.instance));
+        TupleType tuple2 = new TupleType(asList(udtWith3Fields, Int32Type.instance));
+        checkComparisonResults(tuple1, tuple2);
+    }
+
+    @Test
+    public void testWithUDTsNestedWithinComposite()
+    {
+        CompositeType composite1 = CompositeType.getInstance(asList(udtWith2Fields, Int32Type.instance));
+        CompositeType composite2 = CompositeType.getInstance(asList(udtWith3Fields, Int32Type.instance));
+        checkComparisonResults(composite1, composite2);
+    }
+
+    @Test
+    public void testWithDeeplyNestedUDT()
+    {
+        for (boolean isMultiCell : new boolean[]{false, true})
+        {
+            ListType<Set<ByteBuffer>> list1 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith2Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+            ListType<Set<ByteBuffer>> list2 = ListType.getInstance(SetType.getInstance(new TupleType(asList(udtWith3Fields, Int32Type.instance)), isMultiCell), isMultiCell);
+            checkComparisonResults(list1, list2);
+        }
+    }
+
+    @Test
+    public void testInvalidComparison()
+    {
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UserType(ks,6d7954797065,61:org.apache.cassandra.db.marshal.Int32Type,62:org.apache.cassandra.db.marshal.Int32Type) and org.apache.cassandra.db.marshal.Int32Type",
+                                udtWith2Fields,
+                                Int32Type.instance);
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                SetType.getInstance(UTF8Type.instance, true),
+                                SetType.getInstance(InetAddressType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                ListType.getInstance(UTF8Type.instance, true),
+                                ListType.getInstance(InetAddressType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                MapType.getInstance(UTF8Type.instance, IntegerType.instance, true),
+                                MapType.getInstance(InetAddressType.instance, IntegerType.instance, true));
+        assertInvalidComparison("Trying to compare 2 different types: org.apache.cassandra.db.marshal.UTF8Type and org.apache.cassandra.db.marshal.InetAddressType",
+                                MapType.getInstance(IntegerType.instance, UTF8Type.instance, true),
+                                MapType.getInstance(IntegerType.instance, InetAddressType.instance, true));
+    }
+
+    private void assertInvalidComparison(String expectedMessage, AbstractType<?> oldVersion, AbstractType<?> newVersion)
+    {
+        try
+        {
+            checkComparisonResults(oldVersion, newVersion);
+            fail("comparison doesn't throw expected IllegalArgumentException: " + expectedMessage);
+        }
+        catch (IllegalArgumentException e)
+        {
+            assertEquals(e.getMessage(), expectedMessage);
+        }
+    }
+
+    private void checkComparisonResults(AbstractType<?> oldVersion, AbstractType<?> newVersion)
+    {
+        assertEquals(0, compare(oldVersion, oldVersion));
+        assertEquals(0, compare(newVersion, newVersion));
+        assertEquals(-1, compare(oldVersion, newVersion));
+        assertEquals(1, compare(newVersion, oldVersion));
+    }
+
+    private int compare(AbstractType<?> left, AbstractType<?> right)
+    {
+        return AbstractTypeVersionComparator.INSTANCE.compare(left, right);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org