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/07/07 11:43:21 UTC

[01/10] cassandra git commit: Fix nested Tuples/UDTs validation

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-2.2 0163b9744 -> b77e11cfd
  refs/heads/cassandra-3.0 af20226dc -> e51c85f2e
  refs/heads/cassandra-3.11 7afb97cb6 -> b5df3b4c3
  refs/heads/trunk 9fdec0a82 -> 10b397e2a


Fix nested Tuples/UDTs validation

patch by Benjamin Lerer; reviewed by Andrés de la Peña for CASSANDRA-13646


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

Branch: refs/heads/cassandra-2.2
Commit: b77e11cfd51ddb0f3ac07530399abe999df0573e
Parents: 0163b97
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:29:00 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:29:00 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 219 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index c966c92..08c6e48 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.11
+ * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index de3f545..6766d07 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -91,7 +91,7 @@ public abstract class UserTypes
         private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
         {
             if (!(receiver.type instanceof UserType))
-                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
+                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
 
             UserType ut = (UserType)receiver.type;
             for (int i = 0; i < ut.size(); i++)
@@ -103,7 +103,7 @@ public abstract class UserTypes
 
                 ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
                 if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
-                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver, field, fieldSpec.type.asCQL3Type()));
+                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver.name, field, fieldSpec.type.asCQL3Type()));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 0d08a52..bf7eae4 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -39,11 +39,23 @@ public class TupleType extends AbstractType<ByteBuffer>
 {
     protected final List<AbstractType<?>> types;
 
+    private final TupleSerializer serializer;
+
     public TupleType(List<AbstractType<?>> types)
     {
         for (int i = 0; i < types.size(); i++)
             types.set(i, types.get(i).freeze());
         this.types = types;
+        this.serializer = new TupleSerializer(fieldSerializers(types));
+    }
+
+    private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types)
+    {
+        int size = types.size();
+        List<TypeSerializer<?>> serializers = new ArrayList<>(size);
+        for (int i = 0; i < size; i++)
+            serializers.add(types.get(i).getSerializer());
+        return serializers;
     }
 
     public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -119,37 +131,6 @@ public class TupleType extends AbstractType<ByteBuffer>
         return 1;
     }
 
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of tuple value");
-    }
-
     /**
      * Split a tuple value into its component values.
      */
@@ -297,7 +278,7 @@ public class TupleType extends AbstractType<ByteBuffer>
 
     public TypeSerializer<ByteBuffer> getSerializer()
     {
-        return BytesSerializer.instance;
+        return serializer;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 187deeb..93059cd 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -28,6 +28,8 @@ import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.UserTypeSerializer;
 import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.Pair;
 
@@ -42,6 +44,7 @@ public class UserType extends TupleType
     public final ByteBuffer name;
     private final List<ByteBuffer> fieldNames;
     private final List<String> stringFieldNames;
+    private final UserTypeSerializer serializer;
 
     public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
     {
@@ -51,17 +54,22 @@ public class UserType extends TupleType
         this.name = name;
         this.fieldNames = fieldNames;
         this.stringFieldNames = new ArrayList<>(fieldNames.size());
-        for (ByteBuffer fieldName : fieldNames)
+        LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size());
+        for (int i = 0, m = fieldNames.size(); i < m; i++)
         {
+            ByteBuffer fieldName = fieldNames.get(i);
             try
             {
-                stringFieldNames.add(ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8));
+                String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8);
+                stringFieldNames.add(stringFieldName);
+                fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
             }
             catch (CharacterCodingException ex)
             {
                 throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex);
             }
         }
+        this.serializer = new UserTypeSerializer(fieldSerializers);
     }
 
     public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -109,38 +117,6 @@ public class UserType extends TupleType
         return UTF8Type.instance.compose(name);
     }
 
-    // Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i)));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i)));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of UDT value");
-    }
-
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
@@ -243,4 +219,10 @@ public class UserType extends TupleType
     {
         return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
     }
+
+    @Override
+    public TypeSerializer<ByteBuffer> getSerializer()
+    {
+        return serializer;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/TupleSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TupleSerializer.java b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
new file mode 100644
index 0000000..7cf71c6
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class TupleSerializer extends BytesSerializer
+{
+    public final List<TypeSerializer<?>> fields;
+
+    public TupleSerializer(List<TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        for (int i = 0; i < fields.size(); i++)
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            fields.get(i).validate(field);
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of tuple value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
new file mode 100644
index 0000000..472e39b
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
@@ -0,0 +1,67 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class UserTypeSerializer extends BytesSerializer
+{
+    public final LinkedHashMap<String, TypeSerializer<?>> fields;
+
+    public UserTypeSerializer(LinkedHashMap<String, TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        int i = 0;
+        for (Entry<String, TypeSerializer<?>> entry : fields.entrySet())
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, entry.getKey()));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, entry.getKey()));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            entry.getValue().validate(field);
+            i++;
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of UDT value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index 99d9695..69d5a5c 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@ -748,4 +748,40 @@ public class CollectionsTest extends CQLTester
         execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
         assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
     }
+
+    @Test
+    public void testInsertingCollectionsWithInvalidElements() throws Throwable
+    {
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, s) VALUES (0, ?)",
+                             set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, l) VALUES (0, ?)",
+                             list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+
+        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index 0783dd1..3b4fb40 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@ -123,6 +123,14 @@ public class TupleTypeTest extends CQLTester
 
         assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
                              "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             tuple(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
     }
 
     @Test
@@ -137,7 +145,7 @@ public class TupleTypeTest extends CQLTester
         // select using unset
         assertInvalidMessage("Invalid unset value for tuple field number 0", "SELECT * FROM %s WHERE k = ? and t = (?,?,?)", unset(), unset(), unset(), unset());
     }
-	
+
     /**
      * Test the syntax introduced by #4851,
      * migrated from cql_tests.py:TestCQL.tuple_notation_test()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 3803e5c..9bafe4a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -53,6 +53,15 @@ public class UserTypesTest extends CQLTester
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
         assertInvalidMessage("Not enough bytes to read 0th field f",
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+
+        String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             userType(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
     }
 
     @Test


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


[05/10] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

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


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

Branch: refs/heads/cassandra-3.11
Commit: e51c85f2e6045e077dad8575f7737d502fd50414
Parents: af20226 b77e11c
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:31:08 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:34:32 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +-
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 220 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 52b0027,08c6e48..c98ffd9
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,60 -1,15 +1,61 @@@
 -2.2.11
 - * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
 - * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 +3.0.15
 + * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 + * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
 + * Fix secondary index queries on COMPACT tables (CASSANDRA-13627)
 + * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
- 
++ Merged from 2.2:
++* Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  
 -
 -2.2.10
 +3.0.14
 + * Ensure int overflow doesn't occur when calculating large partition warning size (CASSANDRA-13172)
 + * Ensure consistent view of partition columns between coordinator and replica in ColumnFilter (CASSANDRA-13004)
 + * Failed unregistering mbean during drop keyspace (CASSANDRA-13346)
 + * nodetool scrub/cleanup/upgradesstables exit code is wrong (CASSANDRA-13542)
 + * Fix the reported number of sstable data files accessed per read (CASSANDRA-13120)
 + * Fix schema digest mismatch during rolling upgrades from versions before 3.0.12 (CASSANDRA-13559)
 + * Upgrade JNA version to 4.4.0 (CASSANDRA-13072)
 + * Interned ColumnIdentifiers should use minimal ByteBuffers (CASSANDRA-13533)
 + * ReverseIndexedReader may drop rows during 2.1 to 3.0 upgrade (CASSANDRA-13525)
 + * Fix repair process violating start/end token limits for small ranges (CASSANDRA-13052)
 + * Add storage port options to sstableloader (CASSANDRA-13518)
 + * Properly handle quoted index names in cqlsh DESCRIBE output (CASSANDRA-12847)
 + * Avoid reading static row twice from old format sstables (CASSANDRA-13236)
 + * Fix NPE in StorageService.excise() (CASSANDRA-13163)
 + * Expire OutboundTcpConnection messages by a single Thread (CASSANDRA-13265)
 + * Fail repair if insufficient responses received (CASSANDRA-13397)
 + * Fix SSTableLoader fail when the loaded table contains dropped columns (CASSANDRA-13276)
 + * Avoid name clashes in CassandraIndexTest (CASSANDRA-13427)
 + * Handling partially written hint files (CASSANDRA-12728)
 + * Interrupt replaying hints on decommission (CASSANDRA-13308)
 + * Fix schema version calculation for rolling upgrades (CASSANDRA-13441)
 +Merged from 2.2:
   * Nodes started with join_ring=False should be able to serve requests when authentication is enabled (CASSANDRA-11381)
   * cqlsh COPY FROM: increment error count only for failures, not for attempts (CASSANDRA-13209)
 - * nodetool upgradesstables should upgrade system tables (CASSANDRA-13119)
 +
 +3.0.13
 + * Make reading of range tombstones more reliable (CASSANDRA-12811)
 + * Fix startup problems due to schema tables not completely flushed (CASSANDRA-12213)
 + * Fix view builder bug that can filter out data on restart (CASSANDRA-13405)
 + * Fix 2i page size calculation when there are no regular columns (CASSANDRA-13400)
 + * Fix the conversion of 2.X expired rows without regular column data (CASSANDRA-13395)
 + * Fix hint delivery when using ext+internal IPs with prefer_local enabled (CASSANDRA-13020)
 + * Fix possible NPE on upgrade to 3.0/3.X in case of IO errors (CASSANDRA-13389)
 + * Legacy deserializer can create empty range tombstones (CASSANDRA-13341)
 + * Use the Kernel32 library to retrieve the PID on Windows and fix startup checks (CASSANDRA-13333)
 + * Fix code to not exchange schema across major versions (CASSANDRA-13274)
 + * Dropping column results in "corrupt" SSTable (CASSANDRA-13337)
 + * Bugs handling range tombstones in the sstable iterators (CASSANDRA-13340)
 + * Fix CONTAINS filtering for null collections (CASSANDRA-13246)
 + * Applying: Use a unique metric reservoir per test run when using Cassandra-wide metrics residing in MBeans (CASSANDRA-13216)
 + * Propagate row deletions in 2i tables on upgrade (CASSANDRA-13320)
 + * Slice.isEmpty() returns false for some empty slices (CASSANDRA-13305)
 + * Add formatted row output to assertEmpty in CQL Tester (CASSANDRA-13238)
 + * Legacy caching options can prevent 3.0 upgrade (CASSANDRA-13384)
 + * Nodetool upgradesstables/scrub/compact ignores system tables (CASSANDRA-13410)
 + * Fix NPE issue in StorageService (CASSANDRA-13060)
 +Merged from 2.2:
   * Avoid starting gossiper in RemoveTest (CASSANDRA-13407)
   * Fix weightedSize() for row-cache reported by JMX and NodeTool (CASSANDRA-13393)
 - * Fix JVM metric paths (CASSANDRA-13103)
   * Honor truststore-password parameter in cassandra-stress (CASSANDRA-12773)
   * Discard in-flight shadow round responses (CASSANDRA-12653)
   * Don't anti-compact repaired data to avoid inconsistencies (CASSANDRA-13153)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/TupleType.java
index 5486183,bf7eae4..3af1054
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@@ -39,9 -39,10 +39,11 @@@ public class TupleType extends Abstract
  {
      protected final List<AbstractType<?>> types;
  
+     private final TupleSerializer serializer;
+ 
      public TupleType(List<AbstractType<?>> types)
      {
 +        super(ComparisonType.CUSTOM);
          for (int i = 0; i < types.size(); i++)
              types.set(i, types.get(i).freeze());
          this.types = types;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index e5cb480,69d5a5c..c00d707
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@@ -1006,4 -748,40 +1006,40 @@@ public class CollectionsTest extends CQ
          execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
          assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
      }
+ 
+     @Test
+     public void testInsertingCollectionsWithInvalidElements() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, s) VALUES (0, ?)",
+                              set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, l) VALUES (0, ?)",
+                              list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+ 
 -        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index c369ecd,3b4fb40..cbe4a15
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@@ -121,8 -121,16 +121,16 @@@ public class TupleTypeTest extends CQLT
  
          assertInvalidSyntax("INSERT INTO %s (k, t) VALUES (0, ())");
  
 -        assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
 +        assertInvalidMessage("Invalid tuple literal for t: too many elements. Type frozen<tuple<int, text, double>> expects 3 but got 4",
                               "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
+                              tuple(1, tuple(1, "1", 1.0, 1)));
+ 
 -        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
      }
  
      @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index e776f0e,9bafe4a..c279e00
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -54,6 -53,15 +54,15 @@@ public class UserTypesTest extends CQLT
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
          assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+ 
+         String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
+                              userType(1, tuple(1, "1", 1.0, 1)));
+ 
 -        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid user type literal for t: field b is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
      }
  
      @Test


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


[09/10] 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/b5df3b4c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b5df3b4c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b5df3b4c

Branch: refs/heads/trunk
Commit: b5df3b4c367c2927f309ca16a540b7be83636556
Parents: 7afb97c e51c85f
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:36:52 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:38:22 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +-
 .../org/apache/cassandra/cql3/UserTypes.java    | 12 +---
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 51 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 64 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java | 15 ++++-
 9 files changed, 223 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 096dfa4,c98ffd9..679d55b
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,51 -1,12 +1,52 @@@
 -3.0.15
 +3.11.1
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
   * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 - * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
   * Fix secondary index queries on COMPACT tables (CASSANDRA-13627)
   * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
- 
 - Merged from 2.2:
 -* Fix nested Tuples/UDTs validation (CASSANDRA-13646)
++Merged from 2.2:
++ * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  
 -3.0.14
 +3.11.0
 + * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
 + * Replace string comparison with regex/number checks in MessagingService test (CASSANDRA-13216)
 + * Fix formatting of duration columns in CQLSH (CASSANDRA-13549)
 + * Fix the problem with duplicated rows when using paging with SASI (CASSANDRA-13302)
 + * Allow CONTAINS statements filtering on the partition key and it’s parts (CASSANDRA-13275)
 + * Fall back to even ranges calculation in clusters with vnodes when tokens are distributed unevenly (CASSANDRA-13229)
 + * Fix duration type validation to prevent overflow (CASSANDRA-13218)
 + * Forbid unsupported creation of SASI indexes over partition key columns (CASSANDRA-13228)
 + * Reject multiple values for a key in CQL grammar. (CASSANDRA-13369)
 + * UDA fails without input rows (CASSANDRA-13399)
 + * Fix compaction-stress by using daemonInitialization (CASSANDRA-13188)
 + * V5 protocol flags decoding broken (CASSANDRA-13443)
 + * Use write lock not read lock for removing sstables from compaction strategies. (CASSANDRA-13422)
 + * Use corePoolSize equal to maxPoolSize in JMXEnabledThreadPoolExecutors (CASSANDRA-13329)
 + * Avoid rebuilding SASI indexes containing no values (CASSANDRA-12962)
 + * Add charset to Analyser input stream (CASSANDRA-13151)
 + * Fix testLimitSSTables flake caused by concurrent flush (CASSANDRA-12820)
 + * cdc column addition strikes again (CASSANDRA-13382)
 + * Fix static column indexes (CASSANDRA-13277)
 + * DataOutputBuffer.asNewBuffer broken (CASSANDRA-13298)
 + * unittest CipherFactoryTest failed on MacOS (CASSANDRA-13370)
 + * Forbid SELECT restrictions and CREATE INDEX over non-frozen UDT columns (CASSANDRA-13247)
 + * Default logging we ship will incorrectly print "?:?" for "%F:%L" pattern (CASSANDRA-13317)
 + * Possible AssertionError in UnfilteredRowIteratorWithLowerBound (CASSANDRA-13366)
 + * Support unaligned memory access for AArch64 (CASSANDRA-13326)
 + * Improve SASI range iterator efficiency on intersection with an empty range (CASSANDRA-12915).
 + * Fix equality comparisons of columns using the duration type (CASSANDRA-13174)
 + * Obfuscate password in stress-graphs (CASSANDRA-12233)
 + * Move to FastThreadLocalThread and FastThreadLocal (CASSANDRA-13034)
 + * nodetool stopdaemon errors out (CASSANDRA-13030)
 + * Tables in system_distributed should not use gcgs of 0 (CASSANDRA-12954)
 + * Fix primary index calculation for SASI (CASSANDRA-12910)
 + * More fixes to the TokenAllocator (CASSANDRA-12990)
 + * NoReplicationTokenAllocator should work with zero replication factor (CASSANDRA-12983)
 + * Address message coalescing regression (CASSANDRA-12676)
 + * Delete illegal character from StandardTokenizerImpl.jflex (CASSANDRA-13417)
 + * Fix cqlsh automatic protocol downgrade regression (CASSANDRA-13307)
 + * Tracing payload not passed from QueryMessage to tracing session (CASSANDRA-12835)
 +Merged from 3.0:
   * Ensure int overflow doesn't occur when calculating large partition warning size (CASSANDRA-13172)
   * Ensure consistent view of partition columns between coordinator and replica in ColumnFilter (CASSANDRA-13004)
   * Failed unregistering mbean during drop keyspace (CASSANDRA-13346)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/UserTypes.java
index e867179,68a0513..4edd27f
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@@ -95,8 -88,8 +95,8 @@@ public abstract class UserType
  
          private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
          {
 -            if (!(receiver.type instanceof UserType))
 +            if (!receiver.type.isUDT())
-                 throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
+                 throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
  
              UserType ut = (UserType)receiver.type;
              for (int i = 0; i < ut.size(); i++)
@@@ -108,10 -101,7 +108,10 @@@
  
                  ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
                  if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
 -                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver.name, field, fieldSpec.type.asCQL3Type()));
 +                {
 +                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s",
-                             receiver, field, fieldSpec.type.asCQL3Type()));
++                            receiver.name, field, fieldSpec.type.asCQL3Type()));
 +                }
              }
          }
  
@@@ -150,52 -135,7 +150,46 @@@
          }
      }
  
 -    // Same purpose than Lists.DelayedValue, except we do handle bind marker in that case
 +    public static class Value extends Term.MultiItemTerminal
 +    {
 +        private final UserType type;
 +        public final ByteBuffer[] elements;
 +
 +        public Value(UserType type, ByteBuffer[] elements)
 +        {
 +            this.type = type;
 +            this.elements = elements;
 +        }
 +
 +        public static Value fromSerialized(ByteBuffer bytes, UserType type)
 +        {
-             ByteBuffer[] values = type.split(bytes);
-             if (values.length > type.size())
-             {
-                 throw new InvalidRequestException(String.format(
-                         "UDT value contained too many fields (expected %s, got %s)", type.size(), values.length));
-             }
- 
++            type.validate(bytes);
 +            return new Value(type, type.split(bytes));
 +        }
 +
 +        public ByteBuffer get(ProtocolVersion protocolVersion)
 +        {
 +            return TupleType.buildValue(elements);
 +        }
 +
 +        public boolean equals(UserType userType, Value v)
 +        {
 +            if (elements.length != v.elements.length)
 +                return false;
 +
 +            for (int i = 0; i < elements.length; i++)
 +                if (userType.fieldType(i).compare(elements[i], v.elements[i]) != 0)
 +                    return false;
 +
 +            return true;
 +        }
 +
 +        public List<ByteBuffer> getElements()
 +        {
 +            return Arrays.asList(elements);
 +        }
 +    }
 +
      public static class DelayedValue extends Term.NonTerminal
      {
          private final UserType type;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/TupleType.java
index 5a90ae9,3af1054..034ad53
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@@ -41,29 -37,26 +41,41 @@@ import org.apache.cassandra.utils.ByteB
   */
  public class TupleType extends AbstractType<ByteBuffer>
  {
 +    private static final String COLON = ":";
 +    private static final Pattern COLON_PAT = Pattern.compile(COLON);
 +    private static final String ESCAPED_COLON = "\\\\:";
 +    private static final Pattern ESCAPED_COLON_PAT = Pattern.compile(ESCAPED_COLON);
 +    private static final String AT = "@";
 +    private static final Pattern AT_PAT = Pattern.compile(AT);
 +    private static final String ESCAPED_AT = "\\\\@";
 +    private static final Pattern ESCAPED_AT_PAT = Pattern.compile(ESCAPED_AT);
 +    
      protected final List<AbstractType<?>> types;
  
+     private final TupleSerializer serializer;
+ 
      public TupleType(List<AbstractType<?>> types)
      {
 +        this(types, true);
 +    }
 +
 +    protected TupleType(List<AbstractType<?>> types, boolean freezeInner)
 +    {
          super(ComparisonType.CUSTOM);
 -        for (int i = 0; i < types.size(); i++)
 -            types.set(i, types.get(i).freeze());
 -        this.types = types;
 +        if (freezeInner)
 +            this.types = types.stream().map(AbstractType::freeze).collect(Collectors.toList());
 +        else
 +            this.types = types;
+         this.serializer = new TupleSerializer(fieldSerializers(types));
+     }
+ 
+     private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types)
+     {
+         int size = types.size();
+         List<TypeSerializer<?>> serializers = new ArrayList<>(size);
+         for (int i = 0; i < size; i++)
+             serializers.add(types.get(i).getSerializer());
+         return serializers;
      }
  
      public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@@ -133,55 -120,13 +145,24 @@@
                  return cmp;
          }
  
 -        if (bb1.remaining() == 0)
 -            return bb2.remaining() == 0 ? 0 : -1;
 +        // handle trailing nulls
 +        while (bb1.remaining() > 0)
 +        {
 +            int size = bb1.getInt();
 +            if (size > 0) // non-null
 +                return 1;
 +        }
 +
 +        while (bb2.remaining() > 0)
 +        {
 +            int size = bb2.getInt();
 +            if (size > 0) // non-null
 +                return -1;
 +        }
  
 -        // bb1.remaining() > 0 && bb2.remaining() == 0
 -        return 1;
 +        return 0;
      }
  
-     @Override
-     public void validate(ByteBuffer bytes) throws MarshalException
-     {
-         ByteBuffer input = bytes.duplicate();
-         for (int i = 0; i < size(); i++)
-         {
-             // we allow the input to have less fields than declared so as to support field addition.
-             if (!input.hasRemaining())
-                 return;
- 
-             if (input.remaining() < Integer.BYTES)
-                 throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
- 
-             int size = input.getInt();
- 
-             // size < 0 means null value
-             if (size < 0)
-                 continue;
- 
-             if (input.remaining() < size)
-                 throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
- 
-             ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-             types.get(i).validate(field);
-         }
- 
-         // We're allowed to get less fields than declared, but not more
-         if (input.hasRemaining())
-             throw new MarshalException("Invalid remaining data after end of tuple value");
-     }
- 
      /**
       * Split a tuple value into its component values.
       */

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/UserType.java
index a59000a,b91dbf8..febd91c
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@@ -29,11 -28,10 +29,13 @@@ import org.apache.cassandra.db.rows.Cel
  import org.apache.cassandra.exceptions.ConfigurationException;
  import org.apache.cassandra.exceptions.SyntaxException;
  import org.apache.cassandra.serializers.MarshalException;
 +import org.apache.cassandra.transport.ProtocolVersion;
+ import org.apache.cassandra.serializers.TypeSerializer;
+ import org.apache.cassandra.serializers.UserTypeSerializer;
  import org.apache.cassandra.utils.ByteBufferUtil;
  import org.apache.cassandra.utils.Pair;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
  
  /**
   * A user defined type.
@@@ -42,26 -40,36 +44,33 @@@
   */
  public class UserType extends TupleType
  {
 +    private static final Logger logger = LoggerFactory.getLogger(UserType.class);
 +
      public final String keyspace;
      public final ByteBuffer name;
 -    private final List<ByteBuffer> fieldNames;
 +    private final List<FieldIdentifier> fieldNames;
      private final List<String> stringFieldNames;
 +    private final boolean isMultiCell;
+     private final UserTypeSerializer serializer;
  
 -    public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
 +    public UserType(String keyspace, ByteBuffer name, List<FieldIdentifier> fieldNames, List<AbstractType<?>> fieldTypes, boolean isMultiCell)
      {
 -        super(fieldTypes);
 +        super(fieldTypes, false);
          assert fieldNames.size() == fieldTypes.size();
          this.keyspace = keyspace;
          this.name = name;
          this.fieldNames = fieldNames;
          this.stringFieldNames = new ArrayList<>(fieldNames.size());
 +        this.isMultiCell = isMultiCell;
 +
-         for (FieldIdentifier fieldName : fieldNames)
-             stringFieldNames.add(fieldName.toString());
+         LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size());
+         for (int i = 0, m = fieldNames.size(); i < m; i++)
+         {
 -            ByteBuffer fieldName = fieldNames.get(i);
 -            try
 -            {
 -                String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8);
 -                stringFieldNames.add(stringFieldName);
 -                fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
 -            }
 -            catch (CharacterCodingException ex)
 -            {
 -                throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex);
 -            }
++            String stringFieldName = fieldNames.get(i).toString();
++            stringFieldNames.add(stringFieldName);
++            fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
+         }
+         this.serializer = new UserTypeSerializer(fieldSerializers);
      }
  
      public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@@ -128,94 -117,6 +137,62 @@@
          return UTF8Type.instance.compose(name);
      }
  
 +    public int fieldPosition(FieldIdentifier fieldName)
 +    {
 +        return fieldNames.indexOf(fieldName);
 +    }
 +
 +    public CellPath cellPathForField(FieldIdentifier fieldName)
 +    {
 +        // we use the field position instead of the field name to allow for field renaming in ALTER TYPE statements
 +        return CellPath.create(ByteBufferUtil.bytes((short)fieldPosition(fieldName)));
 +    }
 +
 +    public ShortType nameComparator()
 +    {
 +        return ShortType.instance;
 +    }
 +
 +    public ByteBuffer serializeForNativeProtocol(Iterator<Cell> cells, ProtocolVersion protocolVersion)
 +    {
 +        assert isMultiCell;
 +
 +        ByteBuffer[] components = new ByteBuffer[size()];
 +        short fieldPosition = 0;
 +        while (cells.hasNext())
 +        {
 +            Cell cell = cells.next();
 +
 +            // handle null fields that aren't at the end
 +            short fieldPositionOfCell = ByteBufferUtil.toShort(cell.path().get(0));
 +            while (fieldPosition < fieldPositionOfCell)
 +                components[fieldPosition++] = null;
 +
 +            components[fieldPosition++] = cell.value();
 +        }
 +
 +        // append trailing nulls for missing cells
 +        while (fieldPosition < size())
 +            components[fieldPosition++] = null;
 +
 +        return TupleType.buildValue(components);
 +    }
 +
 +    public void validateCell(Cell cell) throws MarshalException
 +    {
 +        if (isMultiCell)
 +        {
 +            ByteBuffer path = cell.path().get(0);
 +            nameComparator().validate(path);
 +            Short fieldPosition = nameComparator().getSerializer().deserialize(path);
 +            fieldType(fieldPosition).validate(cell.value());
 +        }
 +        else
 +        {
 +            validate(cell.value());
 +        }
 +    }
 +
-     // Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
-     @Override
-     public void validate(ByteBuffer bytes) throws MarshalException
-     {
-         ByteBuffer input = bytes.duplicate();
-         for (int i = 0; i < size(); i++)
-         {
-             // we allow the input to have less fields than declared so as to support field addition.
-             if (!input.hasRemaining())
-                 return;
- 
-             if (input.remaining() < 4)
-                 throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i)));
- 
-             int size = input.getInt();
- 
-             // size < 0 means null value
-             if (size < 0)
-                 continue;
- 
-             if (input.remaining() < size)
-                 throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i)));
- 
-             ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-             types.get(i).validate(field);
-         }
- 
-         // We're allowed to get less fields than declared, but not more
-         if (input.hasRemaining())
-             throw new MarshalException("Invalid remaining data after end of UDT value");
-     }
- 
      @Override
      public Term fromJSONObject(Object parsed) throws MarshalException
      {
@@@ -395,35 -222,14 +372,41 @@@
      }
  
      @Override
 +    public boolean referencesDuration()
 +    {
 +        return fieldTypes().stream().anyMatch(f -> f.referencesDuration());
 +    }
 +
 +    @Override
      public String toString()
      {
 -        return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
 +        return this.toString(false);
 +    }
 +
 +    @Override
 +    public boolean isTuple()
 +    {
 +        return false;
 +    }
 +
 +    @Override
 +    public String toString(boolean ignoreFreezing)
 +    {
 +        boolean includeFrozenType = !ignoreFreezing && !isMultiCell();
 +
 +        StringBuilder sb = new StringBuilder();
 +        if (includeFrozenType)
 +            sb.append(FrozenType.class.getName()).append("(");
 +        sb.append(getClass().getName());
 +        sb.append(TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types, ignoreFreezing || !isMultiCell));
 +        if (includeFrozenType)
 +            sb.append(")");
 +        return sb.toString();
      }
+ 
+     @Override
+     public TypeSerializer<ByteBuffer> getSerializer()
+     {
+         return serializer;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/serializers/TupleSerializer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/serializers/TupleSerializer.java
index 0000000,7cf71c6..65e6654
mode 000000,100644..100644
--- a/src/java/org/apache/cassandra/serializers/TupleSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
@@@ -1,0 -1,66 +1,64 @@@
+ /*
+  * 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.serializers;
+ 
+ import java.nio.ByteBuffer;
 -import java.util.HashMap;
+ import java.util.List;
 -import java.util.Map;
+ 
+ import org.apache.cassandra.utils.ByteBufferUtil;
+ 
+ public class TupleSerializer extends BytesSerializer
+ {
+     public final List<TypeSerializer<?>> fields;
+ 
+     public TupleSerializer(List<TypeSerializer<?>> fields)
+     {
+         this.fields = fields;
+     }
+ 
+     @Override
+     public void validate(ByteBuffer bytes) throws MarshalException
+     {
+         ByteBuffer input = bytes.duplicate();
+         for (int i = 0; i < fields.size(); i++)
+         {
+             // we allow the input to have less fields than declared so as to support field addition.
+             if (!input.hasRemaining())
+                 return;
+ 
 -            if (input.remaining() < 4)
++            if (input.remaining() < Integer.BYTES)
+                 throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+ 
+             int size = input.getInt();
+ 
+             // size < 0 means null value
+             if (size < 0)
+                 continue;
+ 
+             if (input.remaining() < size)
+                 throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+ 
+             ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+             fields.get(i).validate(field);
+         }
+ 
+         // We're allowed to get less fields than declared, but not more
+         if (input.hasRemaining())
+             throw new MarshalException("Invalid remaining data after end of tuple value");
+     }
+ }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index e295d82,c279e00..b78eb3e
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -52,10 -50,19 +52,19 @@@ public class UserTypesTest extends CQLT
      {
          String myType = createType("CREATE TYPE %s (f int)");
          createTable("CREATE TABLE %s(pk int PRIMARY KEY, t frozen<" + myType + ">)");
-         assertInvalidMessage("Not enough bytes to read 0th component",
+         assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
-         assertInvalidMessage("Not enough bytes to read 0th component",
+         assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+ 
+         String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
 -                             userType(1, tuple(1, "1", 1.0, 1)));
++                             userType("a", 1, "b", tuple(1, "1", 1.0, 1)));
+ 
+         assertInvalidMessage("Invalid user type literal for t: field b is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
      }
  
      @Test
@@@ -128,44 -117,8 +137,44 @@@
                               " user types can only be used in the keyspace they are defined in",
                               "CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v frozen<otherKeyspace.myType>)");
  
 +        // referencing an unknown UDT
          assertInvalidMessage("Unknown type " + KEYSPACE + ".unknowntype",
                               "CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v frozen<" + KEYSPACE + '.' + "unknownType>)");
 +
 +        // bad deletions on frozen UDTs
 +        createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + myType + ">, c int)");
 +        assertInvalidMessage("Frozen UDT column b does not support field deletion", "DELETE b.a FROM %s WHERE a = 0");
 +        assertInvalidMessage("Invalid field deletion operation for non-UDT column c", "DELETE c.a FROM %s WHERE a = 0");
 +
 +        // bad updates on frozen UDTs
 +        assertInvalidMessage("Invalid operation (b.a = 0) for frozen UDT column b", "UPDATE %s SET b.a = 0 WHERE a = 0");
 +        assertInvalidMessage("Invalid operation (c.a = 0) for non-UDT column c", "UPDATE %s SET c.a = 0 WHERE a = 0");
 +
 +        // bad deletions on non-frozen UDTs
 +        createTable("CREATE TABLE %s (a int PRIMARY KEY, b " + myType + ", c int)");
 +        assertInvalidMessage("UDT column b does not have a field named foo", "DELETE b.foo FROM %s WHERE a = 0");
 +
 +        // bad updates on non-frozen UDTs
 +        assertInvalidMessage("UDT column b does not have a field named foo", "UPDATE %s SET b.foo = 0 WHERE a = 0");
 +
 +        // bad insert on non-frozen UDTs
 +        assertInvalidMessage("Unknown field 'foo' in value of user defined type", "INSERT INTO %s (a, b, c) VALUES (0, {a: 0, foo: 0}, 0)");
 +        if (usePrepared())
 +        {
-             assertInvalidMessage("Expected 1 value for " + typename + " column, but got more",
++            assertInvalidMessage("Invalid remaining data after end of UDT value",
 +                    "INSERT INTO %s (a, b, c) VALUES (0, ?, 0)", userType("a", 0, "foo", 0));
 +        }
 +        else
 +        {
 +            assertInvalidMessage("Unknown field 'foo' in value of user defined type " + typename,
 +                    "INSERT INTO %s (a, b, c) VALUES (0, ?, 0)", userType("a", 0, "foo", 0));
 +        }
 +
 +        // non-frozen UDT with non-frozen nested collection
 +        String typename2 = createType("CREATE TYPE %s (bar int, foo list<int>)");
 +        String myType2 = KEYSPACE + '.' + typename2;
 +        assertInvalidMessage("Non-frozen UDTs with nested non-frozen collections are not supported",
 +                "CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v " + myType2 + ")");
      }
  
      @Test


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


[03/10] cassandra git commit: Fix nested Tuples/UDTs validation

Posted by bl...@apache.org.
Fix nested Tuples/UDTs validation

patch by Benjamin Lerer; reviewed by Andrés de la Peña for CASSANDRA-13646


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

Branch: refs/heads/cassandra-3.11
Commit: b77e11cfd51ddb0f3ac07530399abe999df0573e
Parents: 0163b97
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:29:00 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:29:00 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 219 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index c966c92..08c6e48 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.11
+ * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index de3f545..6766d07 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -91,7 +91,7 @@ public abstract class UserTypes
         private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
         {
             if (!(receiver.type instanceof UserType))
-                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
+                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
 
             UserType ut = (UserType)receiver.type;
             for (int i = 0; i < ut.size(); i++)
@@ -103,7 +103,7 @@ public abstract class UserTypes
 
                 ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
                 if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
-                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver, field, fieldSpec.type.asCQL3Type()));
+                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver.name, field, fieldSpec.type.asCQL3Type()));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 0d08a52..bf7eae4 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -39,11 +39,23 @@ public class TupleType extends AbstractType<ByteBuffer>
 {
     protected final List<AbstractType<?>> types;
 
+    private final TupleSerializer serializer;
+
     public TupleType(List<AbstractType<?>> types)
     {
         for (int i = 0; i < types.size(); i++)
             types.set(i, types.get(i).freeze());
         this.types = types;
+        this.serializer = new TupleSerializer(fieldSerializers(types));
+    }
+
+    private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types)
+    {
+        int size = types.size();
+        List<TypeSerializer<?>> serializers = new ArrayList<>(size);
+        for (int i = 0; i < size; i++)
+            serializers.add(types.get(i).getSerializer());
+        return serializers;
     }
 
     public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -119,37 +131,6 @@ public class TupleType extends AbstractType<ByteBuffer>
         return 1;
     }
 
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of tuple value");
-    }
-
     /**
      * Split a tuple value into its component values.
      */
@@ -297,7 +278,7 @@ public class TupleType extends AbstractType<ByteBuffer>
 
     public TypeSerializer<ByteBuffer> getSerializer()
     {
-        return BytesSerializer.instance;
+        return serializer;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 187deeb..93059cd 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -28,6 +28,8 @@ import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.UserTypeSerializer;
 import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.Pair;
 
@@ -42,6 +44,7 @@ public class UserType extends TupleType
     public final ByteBuffer name;
     private final List<ByteBuffer> fieldNames;
     private final List<String> stringFieldNames;
+    private final UserTypeSerializer serializer;
 
     public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
     {
@@ -51,17 +54,22 @@ public class UserType extends TupleType
         this.name = name;
         this.fieldNames = fieldNames;
         this.stringFieldNames = new ArrayList<>(fieldNames.size());
-        for (ByteBuffer fieldName : fieldNames)
+        LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size());
+        for (int i = 0, m = fieldNames.size(); i < m; i++)
         {
+            ByteBuffer fieldName = fieldNames.get(i);
             try
             {
-                stringFieldNames.add(ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8));
+                String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8);
+                stringFieldNames.add(stringFieldName);
+                fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
             }
             catch (CharacterCodingException ex)
             {
                 throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex);
             }
         }
+        this.serializer = new UserTypeSerializer(fieldSerializers);
     }
 
     public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -109,38 +117,6 @@ public class UserType extends TupleType
         return UTF8Type.instance.compose(name);
     }
 
-    // Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i)));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i)));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of UDT value");
-    }
-
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
@@ -243,4 +219,10 @@ public class UserType extends TupleType
     {
         return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
     }
+
+    @Override
+    public TypeSerializer<ByteBuffer> getSerializer()
+    {
+        return serializer;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/TupleSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TupleSerializer.java b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
new file mode 100644
index 0000000..7cf71c6
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class TupleSerializer extends BytesSerializer
+{
+    public final List<TypeSerializer<?>> fields;
+
+    public TupleSerializer(List<TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        for (int i = 0; i < fields.size(); i++)
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            fields.get(i).validate(field);
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of tuple value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
new file mode 100644
index 0000000..472e39b
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
@@ -0,0 +1,67 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class UserTypeSerializer extends BytesSerializer
+{
+    public final LinkedHashMap<String, TypeSerializer<?>> fields;
+
+    public UserTypeSerializer(LinkedHashMap<String, TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        int i = 0;
+        for (Entry<String, TypeSerializer<?>> entry : fields.entrySet())
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, entry.getKey()));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, entry.getKey()));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            entry.getValue().validate(field);
+            i++;
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of UDT value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index 99d9695..69d5a5c 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@ -748,4 +748,40 @@ public class CollectionsTest extends CQLTester
         execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
         assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
     }
+
+    @Test
+    public void testInsertingCollectionsWithInvalidElements() throws Throwable
+    {
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, s) VALUES (0, ?)",
+                             set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, l) VALUES (0, ?)",
+                             list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+
+        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index 0783dd1..3b4fb40 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@ -123,6 +123,14 @@ public class TupleTypeTest extends CQLTester
 
         assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
                              "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             tuple(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
     }
 
     @Test
@@ -137,7 +145,7 @@ public class TupleTypeTest extends CQLTester
         // select using unset
         assertInvalidMessage("Invalid unset value for tuple field number 0", "SELECT * FROM %s WHERE k = ? and t = (?,?,?)", unset(), unset(), unset(), unset());
     }
-	
+
     /**
      * Test the syntax introduced by #4851,
      * migrated from cql_tests.py:TestCQL.tuple_notation_test()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 3803e5c..9bafe4a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -53,6 +53,15 @@ public class UserTypesTest extends CQLTester
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
         assertInvalidMessage("Not enough bytes to read 0th field f",
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+
+        String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             userType(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
     }
 
     @Test


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


[02/10] cassandra git commit: Fix nested Tuples/UDTs validation

Posted by bl...@apache.org.
Fix nested Tuples/UDTs validation

patch by Benjamin Lerer; reviewed by Andrés de la Peña for CASSANDRA-13646


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

Branch: refs/heads/cassandra-3.0
Commit: b77e11cfd51ddb0f3ac07530399abe999df0573e
Parents: 0163b97
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:29:00 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:29:00 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 219 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index c966c92..08c6e48 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.11
+ * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index de3f545..6766d07 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -91,7 +91,7 @@ public abstract class UserTypes
         private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
         {
             if (!(receiver.type instanceof UserType))
-                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
+                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
 
             UserType ut = (UserType)receiver.type;
             for (int i = 0; i < ut.size(); i++)
@@ -103,7 +103,7 @@ public abstract class UserTypes
 
                 ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
                 if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
-                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver, field, fieldSpec.type.asCQL3Type()));
+                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver.name, field, fieldSpec.type.asCQL3Type()));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 0d08a52..bf7eae4 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -39,11 +39,23 @@ public class TupleType extends AbstractType<ByteBuffer>
 {
     protected final List<AbstractType<?>> types;
 
+    private final TupleSerializer serializer;
+
     public TupleType(List<AbstractType<?>> types)
     {
         for (int i = 0; i < types.size(); i++)
             types.set(i, types.get(i).freeze());
         this.types = types;
+        this.serializer = new TupleSerializer(fieldSerializers(types));
+    }
+
+    private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types)
+    {
+        int size = types.size();
+        List<TypeSerializer<?>> serializers = new ArrayList<>(size);
+        for (int i = 0; i < size; i++)
+            serializers.add(types.get(i).getSerializer());
+        return serializers;
     }
 
     public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -119,37 +131,6 @@ public class TupleType extends AbstractType<ByteBuffer>
         return 1;
     }
 
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of tuple value");
-    }
-
     /**
      * Split a tuple value into its component values.
      */
@@ -297,7 +278,7 @@ public class TupleType extends AbstractType<ByteBuffer>
 
     public TypeSerializer<ByteBuffer> getSerializer()
     {
-        return BytesSerializer.instance;
+        return serializer;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 187deeb..93059cd 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -28,6 +28,8 @@ import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.UserTypeSerializer;
 import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.Pair;
 
@@ -42,6 +44,7 @@ public class UserType extends TupleType
     public final ByteBuffer name;
     private final List<ByteBuffer> fieldNames;
     private final List<String> stringFieldNames;
+    private final UserTypeSerializer serializer;
 
     public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
     {
@@ -51,17 +54,22 @@ public class UserType extends TupleType
         this.name = name;
         this.fieldNames = fieldNames;
         this.stringFieldNames = new ArrayList<>(fieldNames.size());
-        for (ByteBuffer fieldName : fieldNames)
+        LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size());
+        for (int i = 0, m = fieldNames.size(); i < m; i++)
         {
+            ByteBuffer fieldName = fieldNames.get(i);
             try
             {
-                stringFieldNames.add(ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8));
+                String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8);
+                stringFieldNames.add(stringFieldName);
+                fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
             }
             catch (CharacterCodingException ex)
             {
                 throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex);
             }
         }
+        this.serializer = new UserTypeSerializer(fieldSerializers);
     }
 
     public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -109,38 +117,6 @@ public class UserType extends TupleType
         return UTF8Type.instance.compose(name);
     }
 
-    // Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i)));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i)));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of UDT value");
-    }
-
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
@@ -243,4 +219,10 @@ public class UserType extends TupleType
     {
         return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
     }
+
+    @Override
+    public TypeSerializer<ByteBuffer> getSerializer()
+    {
+        return serializer;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/TupleSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TupleSerializer.java b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
new file mode 100644
index 0000000..7cf71c6
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class TupleSerializer extends BytesSerializer
+{
+    public final List<TypeSerializer<?>> fields;
+
+    public TupleSerializer(List<TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        for (int i = 0; i < fields.size(); i++)
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            fields.get(i).validate(field);
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of tuple value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
new file mode 100644
index 0000000..472e39b
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
@@ -0,0 +1,67 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class UserTypeSerializer extends BytesSerializer
+{
+    public final LinkedHashMap<String, TypeSerializer<?>> fields;
+
+    public UserTypeSerializer(LinkedHashMap<String, TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        int i = 0;
+        for (Entry<String, TypeSerializer<?>> entry : fields.entrySet())
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, entry.getKey()));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, entry.getKey()));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            entry.getValue().validate(field);
+            i++;
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of UDT value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index 99d9695..69d5a5c 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@ -748,4 +748,40 @@ public class CollectionsTest extends CQLTester
         execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
         assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
     }
+
+    @Test
+    public void testInsertingCollectionsWithInvalidElements() throws Throwable
+    {
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, s) VALUES (0, ?)",
+                             set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, l) VALUES (0, ?)",
+                             list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+
+        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index 0783dd1..3b4fb40 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@ -123,6 +123,14 @@ public class TupleTypeTest extends CQLTester
 
         assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
                              "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             tuple(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
     }
 
     @Test
@@ -137,7 +145,7 @@ public class TupleTypeTest extends CQLTester
         // select using unset
         assertInvalidMessage("Invalid unset value for tuple field number 0", "SELECT * FROM %s WHERE k = ? and t = (?,?,?)", unset(), unset(), unset(), unset());
     }
-	
+
     /**
      * Test the syntax introduced by #4851,
      * migrated from cql_tests.py:TestCQL.tuple_notation_test()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 3803e5c..9bafe4a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -53,6 +53,15 @@ public class UserTypesTest extends CQLTester
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
         assertInvalidMessage("Not enough bytes to read 0th field f",
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+
+        String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             userType(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
     }
 
     @Test


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


[04/10] cassandra git commit: Fix nested Tuples/UDTs validation

Posted by bl...@apache.org.
Fix nested Tuples/UDTs validation

patch by Benjamin Lerer; reviewed by Andrés de la Peña for CASSANDRA-13646


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

Branch: refs/heads/trunk
Commit: b77e11cfd51ddb0f3ac07530399abe999df0573e
Parents: 0163b97
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:29:00 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:29:00 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 219 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index c966c92..08c6e48 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.11
+ * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java
index de3f545..6766d07 100644
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@ -91,7 +91,7 @@ public abstract class UserTypes
         private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
         {
             if (!(receiver.type instanceof UserType))
-                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
+                throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
 
             UserType ut = (UserType)receiver.type;
             for (int i = 0; i < ut.size(); i++)
@@ -103,7 +103,7 @@ public abstract class UserTypes
 
                 ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
                 if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
-                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver, field, fieldSpec.type.asCQL3Type()));
+                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver.name, field, fieldSpec.type.asCQL3Type()));
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 0d08a52..bf7eae4 100644
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@ -39,11 +39,23 @@ public class TupleType extends AbstractType<ByteBuffer>
 {
     protected final List<AbstractType<?>> types;
 
+    private final TupleSerializer serializer;
+
     public TupleType(List<AbstractType<?>> types)
     {
         for (int i = 0; i < types.size(); i++)
             types.set(i, types.get(i).freeze());
         this.types = types;
+        this.serializer = new TupleSerializer(fieldSerializers(types));
+    }
+
+    private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types)
+    {
+        int size = types.size();
+        List<TypeSerializer<?>> serializers = new ArrayList<>(size);
+        for (int i = 0; i < size; i++)
+            serializers.add(types.get(i).getSerializer());
+        return serializers;
     }
 
     public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -119,37 +131,6 @@ public class TupleType extends AbstractType<ByteBuffer>
         return 1;
     }
 
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of tuple value");
-    }
-
     /**
      * Split a tuple value into its component values.
      */
@@ -297,7 +278,7 @@ public class TupleType extends AbstractType<ByteBuffer>
 
     public TypeSerializer<ByteBuffer> getSerializer()
     {
-        return BytesSerializer.instance;
+        return serializer;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 187deeb..93059cd 100644
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@ -28,6 +28,8 @@ import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.exceptions.ConfigurationException;
 import org.apache.cassandra.exceptions.SyntaxException;
 import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.UserTypeSerializer;
 import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.Pair;
 
@@ -42,6 +44,7 @@ public class UserType extends TupleType
     public final ByteBuffer name;
     private final List<ByteBuffer> fieldNames;
     private final List<String> stringFieldNames;
+    private final UserTypeSerializer serializer;
 
     public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
     {
@@ -51,17 +54,22 @@ public class UserType extends TupleType
         this.name = name;
         this.fieldNames = fieldNames;
         this.stringFieldNames = new ArrayList<>(fieldNames.size());
-        for (ByteBuffer fieldName : fieldNames)
+        LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size());
+        for (int i = 0, m = fieldNames.size(); i < m; i++)
         {
+            ByteBuffer fieldName = fieldNames.get(i);
             try
             {
-                stringFieldNames.add(ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8));
+                String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8);
+                stringFieldNames.add(stringFieldName);
+                fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
             }
             catch (CharacterCodingException ex)
             {
                 throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex);
             }
         }
+        this.serializer = new UserTypeSerializer(fieldSerializers);
     }
 
     public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@ -109,38 +117,6 @@ public class UserType extends TupleType
         return UTF8Type.instance.compose(name);
     }
 
-    // Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
-    @Override
-    public void validate(ByteBuffer bytes) throws MarshalException
-    {
-        ByteBuffer input = bytes.duplicate();
-        for (int i = 0; i < size(); i++)
-        {
-            // we allow the input to have less fields than declared so as to support field addition.
-            if (!input.hasRemaining())
-                return;
-
-            if (input.remaining() < 4)
-                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i)));
-
-            int size = input.getInt();
-
-            // size < 0 means null value
-            if (size < 0)
-                continue;
-
-            if (input.remaining() < size)
-                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i)));
-
-            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-            types.get(i).validate(field);
-        }
-
-        // We're allowed to get less fields than declared, but not more
-        if (input.hasRemaining())
-            throw new MarshalException("Invalid remaining data after end of UDT value");
-    }
-
     @Override
     public Term fromJSONObject(Object parsed) throws MarshalException
     {
@@ -243,4 +219,10 @@ public class UserType extends TupleType
     {
         return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
     }
+
+    @Override
+    public TypeSerializer<ByteBuffer> getSerializer()
+    {
+        return serializer;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/TupleSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TupleSerializer.java b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
new file mode 100644
index 0000000..7cf71c6
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class TupleSerializer extends BytesSerializer
+{
+    public final List<TypeSerializer<?>> fields;
+
+    public TupleSerializer(List<TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        for (int i = 0; i < fields.size(); i++)
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            fields.get(i).validate(field);
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of tuple value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
new file mode 100644
index 0000000..472e39b
--- /dev/null
+++ b/src/java/org/apache/cassandra/serializers/UserTypeSerializer.java
@@ -0,0 +1,67 @@
+/*
+ * 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.serializers;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class UserTypeSerializer extends BytesSerializer
+{
+    public final LinkedHashMap<String, TypeSerializer<?>> fields;
+
+    public UserTypeSerializer(LinkedHashMap<String, TypeSerializer<?>> fields)
+    {
+        this.fields = fields;
+    }
+
+    @Override
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer input = bytes.duplicate();
+        int i = 0;
+        for (Entry<String, TypeSerializer<?>> entry : fields.entrySet())
+        {
+            // we allow the input to have less fields than declared so as to support field addition.
+            if (!input.hasRemaining())
+                return;
+
+            if (input.remaining() < 4)
+                throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, entry.getKey()));
+
+            int size = input.getInt();
+
+            // size < 0 means null value
+            if (size < 0)
+                continue;
+
+            if (input.remaining() < size)
+                throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, entry.getKey()));
+
+            ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+            entry.getValue().validate(field);
+            i++;
+        }
+
+        // We're allowed to get less fields than declared, but not more
+        if (input.hasRemaining())
+            throw new MarshalException("Invalid remaining data after end of UDT value");
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index 99d9695..69d5a5c 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@ -748,4 +748,40 @@ public class CollectionsTest extends CQLTester
         execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
         assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
     }
+
+    @Test
+    public void testInsertingCollectionsWithInvalidElements() throws Throwable
+    {
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, s) VALUES (0, ?)",
+                             set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, l) VALUES (0, ?)",
+                             list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+
+        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, m) VALUES (0, ?)",
+                             map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+
+        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index 0783dd1..3b4fb40 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@ -123,6 +123,14 @@ public class TupleTypeTest extends CQLTester
 
         assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
                              "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             tuple(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
     }
 
     @Test
@@ -137,7 +145,7 @@ public class TupleTypeTest extends CQLTester
         // select using unset
         assertInvalidMessage("Invalid unset value for tuple field number 0", "SELECT * FROM %s WHERE k = ? and t = (?,?,?)", unset(), unset(), unset(), unset());
     }
-	
+
     /**
      * Test the syntax introduced by #4851,
      * migrated from cql_tests.py:TestCQL.tuple_notation_test()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b77e11cf/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 3803e5c..9bafe4a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@ -53,6 +53,15 @@ public class UserTypesTest extends CQLTester
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
         assertInvalidMessage("Not enough bytes to read 0th field f",
                              "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+
+        String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+        createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+        assertInvalidMessage("Invalid remaining data after end of tuple value",
+                             "INSERT INTO %s (k, t) VALUES (0, ?)",
+                             userType(1, tuple(1, "1", 1.0, 1)));
+
+        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
+                             "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
     }
 
     @Test


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


[08/10] 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/b5df3b4c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b5df3b4c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b5df3b4c

Branch: refs/heads/cassandra-3.11
Commit: b5df3b4c367c2927f309ca16a540b7be83636556
Parents: 7afb97c e51c85f
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:36:52 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:38:22 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +-
 .../org/apache/cassandra/cql3/UserTypes.java    | 12 +---
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 51 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 64 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java | 15 ++++-
 9 files changed, 223 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 096dfa4,c98ffd9..679d55b
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,51 -1,12 +1,52 @@@
 -3.0.15
 +3.11.1
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
   * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 - * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
   * Fix secondary index queries on COMPACT tables (CASSANDRA-13627)
   * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
- 
 - Merged from 2.2:
 -* Fix nested Tuples/UDTs validation (CASSANDRA-13646)
++Merged from 2.2:
++ * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  
 -3.0.14
 +3.11.0
 + * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
 + * Replace string comparison with regex/number checks in MessagingService test (CASSANDRA-13216)
 + * Fix formatting of duration columns in CQLSH (CASSANDRA-13549)
 + * Fix the problem with duplicated rows when using paging with SASI (CASSANDRA-13302)
 + * Allow CONTAINS statements filtering on the partition key and it’s parts (CASSANDRA-13275)
 + * Fall back to even ranges calculation in clusters with vnodes when tokens are distributed unevenly (CASSANDRA-13229)
 + * Fix duration type validation to prevent overflow (CASSANDRA-13218)
 + * Forbid unsupported creation of SASI indexes over partition key columns (CASSANDRA-13228)
 + * Reject multiple values for a key in CQL grammar. (CASSANDRA-13369)
 + * UDA fails without input rows (CASSANDRA-13399)
 + * Fix compaction-stress by using daemonInitialization (CASSANDRA-13188)
 + * V5 protocol flags decoding broken (CASSANDRA-13443)
 + * Use write lock not read lock for removing sstables from compaction strategies. (CASSANDRA-13422)
 + * Use corePoolSize equal to maxPoolSize in JMXEnabledThreadPoolExecutors (CASSANDRA-13329)
 + * Avoid rebuilding SASI indexes containing no values (CASSANDRA-12962)
 + * Add charset to Analyser input stream (CASSANDRA-13151)
 + * Fix testLimitSSTables flake caused by concurrent flush (CASSANDRA-12820)
 + * cdc column addition strikes again (CASSANDRA-13382)
 + * Fix static column indexes (CASSANDRA-13277)
 + * DataOutputBuffer.asNewBuffer broken (CASSANDRA-13298)
 + * unittest CipherFactoryTest failed on MacOS (CASSANDRA-13370)
 + * Forbid SELECT restrictions and CREATE INDEX over non-frozen UDT columns (CASSANDRA-13247)
 + * Default logging we ship will incorrectly print "?:?" for "%F:%L" pattern (CASSANDRA-13317)
 + * Possible AssertionError in UnfilteredRowIteratorWithLowerBound (CASSANDRA-13366)
 + * Support unaligned memory access for AArch64 (CASSANDRA-13326)
 + * Improve SASI range iterator efficiency on intersection with an empty range (CASSANDRA-12915).
 + * Fix equality comparisons of columns using the duration type (CASSANDRA-13174)
 + * Obfuscate password in stress-graphs (CASSANDRA-12233)
 + * Move to FastThreadLocalThread and FastThreadLocal (CASSANDRA-13034)
 + * nodetool stopdaemon errors out (CASSANDRA-13030)
 + * Tables in system_distributed should not use gcgs of 0 (CASSANDRA-12954)
 + * Fix primary index calculation for SASI (CASSANDRA-12910)
 + * More fixes to the TokenAllocator (CASSANDRA-12990)
 + * NoReplicationTokenAllocator should work with zero replication factor (CASSANDRA-12983)
 + * Address message coalescing regression (CASSANDRA-12676)
 + * Delete illegal character from StandardTokenizerImpl.jflex (CASSANDRA-13417)
 + * Fix cqlsh automatic protocol downgrade regression (CASSANDRA-13307)
 + * Tracing payload not passed from QueryMessage to tracing session (CASSANDRA-12835)
 +Merged from 3.0:
   * Ensure int overflow doesn't occur when calculating large partition warning size (CASSANDRA-13172)
   * Ensure consistent view of partition columns between coordinator and replica in ColumnFilter (CASSANDRA-13004)
   * Failed unregistering mbean during drop keyspace (CASSANDRA-13346)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/UserTypes.java
index e867179,68a0513..4edd27f
--- a/src/java/org/apache/cassandra/cql3/UserTypes.java
+++ b/src/java/org/apache/cassandra/cql3/UserTypes.java
@@@ -95,8 -88,8 +95,8 @@@ public abstract class UserType
  
          private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
          {
 -            if (!(receiver.type instanceof UserType))
 +            if (!receiver.type.isUDT())
-                 throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
+                 throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()));
  
              UserType ut = (UserType)receiver.type;
              for (int i = 0; i < ut.size(); i++)
@@@ -108,10 -101,7 +108,10 @@@
  
                  ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
                  if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
 -                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver.name, field, fieldSpec.type.asCQL3Type()));
 +                {
 +                    throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s",
-                             receiver, field, fieldSpec.type.asCQL3Type()));
++                            receiver.name, field, fieldSpec.type.asCQL3Type()));
 +                }
              }
          }
  
@@@ -150,52 -135,7 +150,46 @@@
          }
      }
  
 -    // Same purpose than Lists.DelayedValue, except we do handle bind marker in that case
 +    public static class Value extends Term.MultiItemTerminal
 +    {
 +        private final UserType type;
 +        public final ByteBuffer[] elements;
 +
 +        public Value(UserType type, ByteBuffer[] elements)
 +        {
 +            this.type = type;
 +            this.elements = elements;
 +        }
 +
 +        public static Value fromSerialized(ByteBuffer bytes, UserType type)
 +        {
-             ByteBuffer[] values = type.split(bytes);
-             if (values.length > type.size())
-             {
-                 throw new InvalidRequestException(String.format(
-                         "UDT value contained too many fields (expected %s, got %s)", type.size(), values.length));
-             }
- 
++            type.validate(bytes);
 +            return new Value(type, type.split(bytes));
 +        }
 +
 +        public ByteBuffer get(ProtocolVersion protocolVersion)
 +        {
 +            return TupleType.buildValue(elements);
 +        }
 +
 +        public boolean equals(UserType userType, Value v)
 +        {
 +            if (elements.length != v.elements.length)
 +                return false;
 +
 +            for (int i = 0; i < elements.length; i++)
 +                if (userType.fieldType(i).compare(elements[i], v.elements[i]) != 0)
 +                    return false;
 +
 +            return true;
 +        }
 +
 +        public List<ByteBuffer> getElements()
 +        {
 +            return Arrays.asList(elements);
 +        }
 +    }
 +
      public static class DelayedValue extends Term.NonTerminal
      {
          private final UserType type;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/TupleType.java
index 5a90ae9,3af1054..034ad53
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@@ -41,29 -37,26 +41,41 @@@ import org.apache.cassandra.utils.ByteB
   */
  public class TupleType extends AbstractType<ByteBuffer>
  {
 +    private static final String COLON = ":";
 +    private static final Pattern COLON_PAT = Pattern.compile(COLON);
 +    private static final String ESCAPED_COLON = "\\\\:";
 +    private static final Pattern ESCAPED_COLON_PAT = Pattern.compile(ESCAPED_COLON);
 +    private static final String AT = "@";
 +    private static final Pattern AT_PAT = Pattern.compile(AT);
 +    private static final String ESCAPED_AT = "\\\\@";
 +    private static final Pattern ESCAPED_AT_PAT = Pattern.compile(ESCAPED_AT);
 +    
      protected final List<AbstractType<?>> types;
  
+     private final TupleSerializer serializer;
+ 
      public TupleType(List<AbstractType<?>> types)
      {
 +        this(types, true);
 +    }
 +
 +    protected TupleType(List<AbstractType<?>> types, boolean freezeInner)
 +    {
          super(ComparisonType.CUSTOM);
 -        for (int i = 0; i < types.size(); i++)
 -            types.set(i, types.get(i).freeze());
 -        this.types = types;
 +        if (freezeInner)
 +            this.types = types.stream().map(AbstractType::freeze).collect(Collectors.toList());
 +        else
 +            this.types = types;
+         this.serializer = new TupleSerializer(fieldSerializers(types));
+     }
+ 
+     private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types)
+     {
+         int size = types.size();
+         List<TypeSerializer<?>> serializers = new ArrayList<>(size);
+         for (int i = 0; i < size; i++)
+             serializers.add(types.get(i).getSerializer());
+         return serializers;
      }
  
      public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@@ -133,55 -120,13 +145,24 @@@
                  return cmp;
          }
  
 -        if (bb1.remaining() == 0)
 -            return bb2.remaining() == 0 ? 0 : -1;
 +        // handle trailing nulls
 +        while (bb1.remaining() > 0)
 +        {
 +            int size = bb1.getInt();
 +            if (size > 0) // non-null
 +                return 1;
 +        }
 +
 +        while (bb2.remaining() > 0)
 +        {
 +            int size = bb2.getInt();
 +            if (size > 0) // non-null
 +                return -1;
 +        }
  
 -        // bb1.remaining() > 0 && bb2.remaining() == 0
 -        return 1;
 +        return 0;
      }
  
-     @Override
-     public void validate(ByteBuffer bytes) throws MarshalException
-     {
-         ByteBuffer input = bytes.duplicate();
-         for (int i = 0; i < size(); i++)
-         {
-             // we allow the input to have less fields than declared so as to support field addition.
-             if (!input.hasRemaining())
-                 return;
- 
-             if (input.remaining() < Integer.BYTES)
-                 throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
- 
-             int size = input.getInt();
- 
-             // size < 0 means null value
-             if (size < 0)
-                 continue;
- 
-             if (input.remaining() < size)
-                 throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
- 
-             ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-             types.get(i).validate(field);
-         }
- 
-         // We're allowed to get less fields than declared, but not more
-         if (input.hasRemaining())
-             throw new MarshalException("Invalid remaining data after end of tuple value");
-     }
- 
      /**
       * Split a tuple value into its component values.
       */

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/UserType.java
index a59000a,b91dbf8..febd91c
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@@ -29,11 -28,10 +29,13 @@@ import org.apache.cassandra.db.rows.Cel
  import org.apache.cassandra.exceptions.ConfigurationException;
  import org.apache.cassandra.exceptions.SyntaxException;
  import org.apache.cassandra.serializers.MarshalException;
 +import org.apache.cassandra.transport.ProtocolVersion;
+ import org.apache.cassandra.serializers.TypeSerializer;
+ import org.apache.cassandra.serializers.UserTypeSerializer;
  import org.apache.cassandra.utils.ByteBufferUtil;
  import org.apache.cassandra.utils.Pair;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
  
  /**
   * A user defined type.
@@@ -42,26 -40,36 +44,33 @@@
   */
  public class UserType extends TupleType
  {
 +    private static final Logger logger = LoggerFactory.getLogger(UserType.class);
 +
      public final String keyspace;
      public final ByteBuffer name;
 -    private final List<ByteBuffer> fieldNames;
 +    private final List<FieldIdentifier> fieldNames;
      private final List<String> stringFieldNames;
 +    private final boolean isMultiCell;
+     private final UserTypeSerializer serializer;
  
 -    public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
 +    public UserType(String keyspace, ByteBuffer name, List<FieldIdentifier> fieldNames, List<AbstractType<?>> fieldTypes, boolean isMultiCell)
      {
 -        super(fieldTypes);
 +        super(fieldTypes, false);
          assert fieldNames.size() == fieldTypes.size();
          this.keyspace = keyspace;
          this.name = name;
          this.fieldNames = fieldNames;
          this.stringFieldNames = new ArrayList<>(fieldNames.size());
 +        this.isMultiCell = isMultiCell;
 +
-         for (FieldIdentifier fieldName : fieldNames)
-             stringFieldNames.add(fieldName.toString());
+         LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size());
+         for (int i = 0, m = fieldNames.size(); i < m; i++)
+         {
 -            ByteBuffer fieldName = fieldNames.get(i);
 -            try
 -            {
 -                String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8);
 -                stringFieldNames.add(stringFieldName);
 -                fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
 -            }
 -            catch (CharacterCodingException ex)
 -            {
 -                throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex);
 -            }
++            String stringFieldName = fieldNames.get(i).toString();
++            stringFieldNames.add(stringFieldName);
++            fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer());
+         }
+         this.serializer = new UserTypeSerializer(fieldSerializers);
      }
  
      public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
@@@ -128,94 -117,6 +137,62 @@@
          return UTF8Type.instance.compose(name);
      }
  
 +    public int fieldPosition(FieldIdentifier fieldName)
 +    {
 +        return fieldNames.indexOf(fieldName);
 +    }
 +
 +    public CellPath cellPathForField(FieldIdentifier fieldName)
 +    {
 +        // we use the field position instead of the field name to allow for field renaming in ALTER TYPE statements
 +        return CellPath.create(ByteBufferUtil.bytes((short)fieldPosition(fieldName)));
 +    }
 +
 +    public ShortType nameComparator()
 +    {
 +        return ShortType.instance;
 +    }
 +
 +    public ByteBuffer serializeForNativeProtocol(Iterator<Cell> cells, ProtocolVersion protocolVersion)
 +    {
 +        assert isMultiCell;
 +
 +        ByteBuffer[] components = new ByteBuffer[size()];
 +        short fieldPosition = 0;
 +        while (cells.hasNext())
 +        {
 +            Cell cell = cells.next();
 +
 +            // handle null fields that aren't at the end
 +            short fieldPositionOfCell = ByteBufferUtil.toShort(cell.path().get(0));
 +            while (fieldPosition < fieldPositionOfCell)
 +                components[fieldPosition++] = null;
 +
 +            components[fieldPosition++] = cell.value();
 +        }
 +
 +        // append trailing nulls for missing cells
 +        while (fieldPosition < size())
 +            components[fieldPosition++] = null;
 +
 +        return TupleType.buildValue(components);
 +    }
 +
 +    public void validateCell(Cell cell) throws MarshalException
 +    {
 +        if (isMultiCell)
 +        {
 +            ByteBuffer path = cell.path().get(0);
 +            nameComparator().validate(path);
 +            Short fieldPosition = nameComparator().getSerializer().deserialize(path);
 +            fieldType(fieldPosition).validate(cell.value());
 +        }
 +        else
 +        {
 +            validate(cell.value());
 +        }
 +    }
 +
-     // Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
-     @Override
-     public void validate(ByteBuffer bytes) throws MarshalException
-     {
-         ByteBuffer input = bytes.duplicate();
-         for (int i = 0; i < size(); i++)
-         {
-             // we allow the input to have less fields than declared so as to support field addition.
-             if (!input.hasRemaining())
-                 return;
- 
-             if (input.remaining() < 4)
-                 throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i)));
- 
-             int size = input.getInt();
- 
-             // size < 0 means null value
-             if (size < 0)
-                 continue;
- 
-             if (input.remaining() < size)
-                 throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i)));
- 
-             ByteBuffer field = ByteBufferUtil.readBytes(input, size);
-             types.get(i).validate(field);
-         }
- 
-         // We're allowed to get less fields than declared, but not more
-         if (input.hasRemaining())
-             throw new MarshalException("Invalid remaining data after end of UDT value");
-     }
- 
      @Override
      public Term fromJSONObject(Object parsed) throws MarshalException
      {
@@@ -395,35 -222,14 +372,41 @@@
      }
  
      @Override
 +    public boolean referencesDuration()
 +    {
 +        return fieldTypes().stream().anyMatch(f -> f.referencesDuration());
 +    }
 +
 +    @Override
      public String toString()
      {
 -        return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
 +        return this.toString(false);
 +    }
 +
 +    @Override
 +    public boolean isTuple()
 +    {
 +        return false;
 +    }
 +
 +    @Override
 +    public String toString(boolean ignoreFreezing)
 +    {
 +        boolean includeFrozenType = !ignoreFreezing && !isMultiCell();
 +
 +        StringBuilder sb = new StringBuilder();
 +        if (includeFrozenType)
 +            sb.append(FrozenType.class.getName()).append("(");
 +        sb.append(getClass().getName());
 +        sb.append(TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types, ignoreFreezing || !isMultiCell));
 +        if (includeFrozenType)
 +            sb.append(")");
 +        return sb.toString();
      }
+ 
+     @Override
+     public TypeSerializer<ByteBuffer> getSerializer()
+     {
+         return serializer;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/serializers/TupleSerializer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/serializers/TupleSerializer.java
index 0000000,7cf71c6..65e6654
mode 000000,100644..100644
--- a/src/java/org/apache/cassandra/serializers/TupleSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java
@@@ -1,0 -1,66 +1,64 @@@
+ /*
+  * 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.serializers;
+ 
+ import java.nio.ByteBuffer;
 -import java.util.HashMap;
+ import java.util.List;
 -import java.util.Map;
+ 
+ import org.apache.cassandra.utils.ByteBufferUtil;
+ 
+ public class TupleSerializer extends BytesSerializer
+ {
+     public final List<TypeSerializer<?>> fields;
+ 
+     public TupleSerializer(List<TypeSerializer<?>> fields)
+     {
+         this.fields = fields;
+     }
+ 
+     @Override
+     public void validate(ByteBuffer bytes) throws MarshalException
+     {
+         ByteBuffer input = bytes.duplicate();
+         for (int i = 0; i < fields.size(); i++)
+         {
+             // we allow the input to have less fields than declared so as to support field addition.
+             if (!input.hasRemaining())
+                 return;
+ 
 -            if (input.remaining() < 4)
++            if (input.remaining() < Integer.BYTES)
+                 throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+ 
+             int size = input.getInt();
+ 
+             // size < 0 means null value
+             if (size < 0)
+                 continue;
+ 
+             if (input.remaining() < size)
+                 throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+ 
+             ByteBuffer field = ByteBufferUtil.readBytes(input, size);
+             fields.get(i).validate(field);
+         }
+ 
+         // We're allowed to get less fields than declared, but not more
+         if (input.hasRemaining())
+             throw new MarshalException("Invalid remaining data after end of tuple value");
+     }
+ }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index e295d82,c279e00..b78eb3e
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -52,10 -50,19 +52,19 @@@ public class UserTypesTest extends CQLT
      {
          String myType = createType("CREATE TYPE %s (f int)");
          createTable("CREATE TABLE %s(pk int PRIMARY KEY, t frozen<" + myType + ">)");
-         assertInvalidMessage("Not enough bytes to read 0th component",
+         assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
-         assertInvalidMessage("Not enough bytes to read 0th component",
+         assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+ 
+         String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
 -                             userType(1, tuple(1, "1", 1.0, 1)));
++                             userType("a", 1, "b", tuple(1, "1", 1.0, 1)));
+ 
+         assertInvalidMessage("Invalid user type literal for t: field b is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
      }
  
      @Test
@@@ -128,44 -117,8 +137,44 @@@
                               " user types can only be used in the keyspace they are defined in",
                               "CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v frozen<otherKeyspace.myType>)");
  
 +        // referencing an unknown UDT
          assertInvalidMessage("Unknown type " + KEYSPACE + ".unknowntype",
                               "CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v frozen<" + KEYSPACE + '.' + "unknownType>)");
 +
 +        // bad deletions on frozen UDTs
 +        createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + myType + ">, c int)");
 +        assertInvalidMessage("Frozen UDT column b does not support field deletion", "DELETE b.a FROM %s WHERE a = 0");
 +        assertInvalidMessage("Invalid field deletion operation for non-UDT column c", "DELETE c.a FROM %s WHERE a = 0");
 +
 +        // bad updates on frozen UDTs
 +        assertInvalidMessage("Invalid operation (b.a = 0) for frozen UDT column b", "UPDATE %s SET b.a = 0 WHERE a = 0");
 +        assertInvalidMessage("Invalid operation (c.a = 0) for non-UDT column c", "UPDATE %s SET c.a = 0 WHERE a = 0");
 +
 +        // bad deletions on non-frozen UDTs
 +        createTable("CREATE TABLE %s (a int PRIMARY KEY, b " + myType + ", c int)");
 +        assertInvalidMessage("UDT column b does not have a field named foo", "DELETE b.foo FROM %s WHERE a = 0");
 +
 +        // bad updates on non-frozen UDTs
 +        assertInvalidMessage("UDT column b does not have a field named foo", "UPDATE %s SET b.foo = 0 WHERE a = 0");
 +
 +        // bad insert on non-frozen UDTs
 +        assertInvalidMessage("Unknown field 'foo' in value of user defined type", "INSERT INTO %s (a, b, c) VALUES (0, {a: 0, foo: 0}, 0)");
 +        if (usePrepared())
 +        {
-             assertInvalidMessage("Expected 1 value for " + typename + " column, but got more",
++            assertInvalidMessage("Invalid remaining data after end of UDT value",
 +                    "INSERT INTO %s (a, b, c) VALUES (0, ?, 0)", userType("a", 0, "foo", 0));
 +        }
 +        else
 +        {
 +            assertInvalidMessage("Unknown field 'foo' in value of user defined type " + typename,
 +                    "INSERT INTO %s (a, b, c) VALUES (0, ?, 0)", userType("a", 0, "foo", 0));
 +        }
 +
 +        // non-frozen UDT with non-frozen nested collection
 +        String typename2 = createType("CREATE TYPE %s (bar int, foo list<int>)");
 +        String myType2 = KEYSPACE + '.' + typename2;
 +        assertInvalidMessage("Non-frozen UDTs with nested non-frozen collections are not supported",
 +                "CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v " + myType2 + ")");
      }
  
      @Test


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


[10/10] 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/10b397e2
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/10b397e2
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/10b397e2

Branch: refs/heads/trunk
Commit: 10b397e2a9c90e0fe3a229659952a56a03795a22
Parents: 9fdec0a b5df3b4
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:40:48 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:42:19 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +-
 .../org/apache/cassandra/cql3/UserTypes.java    | 27 +++++---
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 51 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 64 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java | 15 ++++-
 9 files changed, 239 insertions(+), 79 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/10b397e2/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 4f2d2a1,679d55b..569a807
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -96,10 -1,11 +96,11 @@@
  3.11.1
   * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
  Merged from 3.0:
 - * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 - * Fix secondary index queries on COMPACT tables (CASSANDRA-13627)
 - * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
 +  * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 +  * Fix secondary index queries on COMPACT tables (CASSANDRA-13627) 
 +  * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
- 
+ Merged from 2.2:
 - * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
++  * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  
  3.11.0
   * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/10b397e2/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/10b397e2/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/10b397e2/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/UserType.java
index f139edd,febd91c..6149e94
--- a/src/java/org/apache/cassandra/db/marshal/UserType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UserType.java
@@@ -426,8 -404,9 +403,14 @@@ public class UserType extends TupleTyp
          return sb.toString();
      }
  
 +    public String toCQLString()
 +    {
 +        return String.format("%s.%s", ColumnIdentifier.maybeQuote(keyspace), ColumnIdentifier.maybeQuote(getNameAsString()));
 +    }
++
+     @Override
+     public TypeSerializer<ByteBuffer> getSerializer()
+     {
+         return serializer;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/10b397e2/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index dad5d68,22e6e66..6f67609
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@@ -1107,786 -1037,38 +1107,822 @@@ public class CollectionsTest extends CQ
      }
  
      @Test
 +    public void testMapOperation() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int, c int, l text, " +
 +                    "m map<text, text>, " +
 +                    "fm frozen<map<text, text>>, " +
 +                    "sm map<text, text> STATIC, " +
 +                    "fsm frozen<map<text, text>> STATIC, " +
 +                    "o int, PRIMARY KEY (k, c))");
 +
 +        execute("INSERT INTO %s(k, c, l, m, fm, sm, fsm, o) VALUES (0, 0, 'foobar', ?, ?, ?, ?, 42)",
 +                map("22", "value22", "333", "value333"),
 +                map("1", "fvalue1", "22", "fvalue22", "333", "fvalue333"),
 +                map("22", "svalue22", "333", "svalue333"),
 +                map("1", "fsvalue1", "22", "fsvalue22", "333", "fsvalue333"));
 +
 +        execute("INSERT INTO %s(k, c, l, m, fm, sm, fsm, o) VALUES (2, 0, 'row2', ?, ?, ?, ?, 88)",
 +                map("22", "2value22", "333", "2value333"),
 +                map("1", "2fvalue1", "22", "2fvalue22", "333", "2fvalue333"),
 +                map("22", "2svalue22", "333", "2svalue333"),
 +                map("1", "2fsvalue1", "22", "2fsvalue22", "333", "2fsvalue333"));
 +
 +        flush();
 +
 +        execute("UPDATE %s SET m = m + ? WHERE k = 0 AND c = 0",
 +                map("1", "value1"));
 +
 +        execute("UPDATE %s SET sm = sm + ? WHERE k = 0",
 +                map("1", "svalue1"));
 +
 +        flush();
 +
 +        assertRows(execute("SELECT m['22'] FROM %s WHERE k = 0 AND c = 0"),
 +                   row("value22")
 +        );
 +        assertRows(execute("SELECT m['1'], m['22'], m['333'] FROM %s WHERE k = 0 AND c = 0"),
 +                   row("value1", "value22", "value333")
 +        );
 +        assertRows(execute("SELECT m['2'..'3'] FROM %s WHERE k = 0 AND c = 0"),
 +                   row(map("22", "value22"))
 +        );
 +
 +        execute("INSERT INTO %s(k, c, l, m, fm, o) VALUES (0, 1, 'foobar', ?, ?, 42)",
 +                map("1", "value1_2", "333", "value333_2"),
 +                map("1", "fvalue1_2", "333", "fvalue333_2"));
 +
 +        assertRows(execute("SELECT c, m['1'], fm['1'] FROM %s WHERE k = 0"),
 +                   row(0, "value1", "fvalue1"),
 +                   row(1, "value1_2", "fvalue1_2")
 +        );
 +        assertRows(execute("SELECT c, sm['1'], fsm['1'] FROM %s WHERE k = 0"),
 +                   row(0, "svalue1", "fsvalue1"),
 +                   row(1, "svalue1", "fsvalue1")
 +        );
 +
 +        assertRows(execute("SELECT c, m['1'], fm['1'] FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "value1", "fvalue1")
 +        );
 +
 +        assertRows(execute("SELECT c, m['1'], fm['1'] FROM %s WHERE k = 0"),
 +                   row(0, "value1", "fvalue1"),
 +                   row(1, "value1_2", "fvalue1_2")
 +        );
 +
 +        assertColumnNames(execute("SELECT k, l, m['1'] as mx, o FROM %s WHERE k = 0"),
 +                          "k", "l", "mx", "o");
 +        assertColumnNames(execute("SELECT k, l, m['1'], o FROM %s WHERE k = 0"),
 +                          "k", "l", "m['1']", "o");
 +
 +        assertRows(execute("SELECT k, l, m['22'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", "value22", 42),
 +                   row(0, "foobar", null, 42)
 +        );
 +        assertColumnNames(execute("SELECT k, l, m['22'], o FROM %s WHERE k = 0"),
 +                          "k", "l", "m['22']", "o");
 +
 +        assertRows(execute("SELECT k, l, m['333'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", "value333", 42),
 +                   row(0, "foobar", "value333_2", 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m['foobar'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", null, 42),
 +                   row(0, "foobar", null, 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m['1'..'22'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42),
 +                   row(0, "foobar", map("1", "value1_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m[''..'23'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42),
 +                   row(0, "foobar", map("1", "value1_2"), 42)
 +        );
 +        assertColumnNames(execute("SELECT k, l, m[''..'23'], o FROM %s WHERE k = 0"),
 +                          "k", "l", "m[''..'23']", "o");
 +
 +        assertRows(execute("SELECT k, l, m['2'..'3'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("22", "value22"), 42),
 +                   row(0, "foobar", map(), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m['22'..], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("22", "value22",
 +                                        "333", "value333"), 42),
 +                   row(0, "foobar", map("333", "value333_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m[..'22'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42),
 +                   row(0, "foobar", map("1", "value1_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m, o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22",
 +                                        "333", "value333"), 42),
 +                   row(0, "foobar", map("1", "value1_2",
 +                                        "333", "value333_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, m, m as m2, o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22",
 +                                        "333", "value333"),
 +                       map("1", "value1",
 +                           "22", "value22",
 +                           "333", "value333"), 42),
 +                   row(0, "foobar", map("1", "value1_2",
 +                                        "333", "value333_2"),
 +                       map("1", "value1_2",
 +                           "333", "value333_2"), 42)
 +        );
 +
 +        // with UDF as slice arg
 +
 +        String f = createFunction(KEYSPACE, "text",
 +                                  "CREATE FUNCTION %s(arg text) " +
 +                                  "CALLED ON NULL INPUT " +
 +                                  "RETURNS TEXT " +
 +                                  "LANGUAGE java AS 'return arg;'");
 +
 +        assertRows(execute("SELECT k, c, l, m[" + f +"('1').." + f +"('22')], o FROM %s WHERE k = 0"),
 +                   row(0, 0, "foobar", map("1", "value1",
 +                                           "22", "value22"), 42),
 +                   row(0, 1, "foobar", map("1", "value1_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, c, l, m[" + f +"(?).." + f +"(?)], o FROM %s WHERE k = 0", "1", "22"),
 +                   row(0, 0, "foobar", map("1", "value1",
 +                                           "22", "value22"), 42),
 +                   row(0, 1, "foobar", map("1", "value1_2"), 42)
 +        );
 +
 +        // with UDF taking a map
 +
 +        f = createFunction(KEYSPACE, "map<text,text>",
 +                           "CREATE FUNCTION %s(m text) " +
 +                           "CALLED ON NULL INPUT " +
 +                           "RETURNS TEXT " +
 +                           "LANGUAGE java AS $$return m;$$");
 +
 +        assertRows(execute("SELECT k, c, " + f + "(m['1']) FROM %s WHERE k = 0"),
 +                   row(0, 0, "value1"),
 +                   row(0, 1, "value1_2"));
 +
 +        // with UDF taking multiple cols
 +
 +        f = createFunction(KEYSPACE, "map<text,text>,map<text,text>,int,int",
 +                           "CREATE FUNCTION %s(m1 map<text,text>, m2 text, k int, c int) " +
 +                           "CALLED ON NULL INPUT " +
 +                           "RETURNS TEXT " +
 +                           "LANGUAGE java AS $$return m1.get(\"1\") + ':' + m2 + ':' + k + ':' + c;$$");
 +
 +        assertRows(execute("SELECT " + f + "(m, m['1'], k, c) FROM %s WHERE k = 0"),
 +                   row("value1:value1:0:0"),
 +                   row("value1_2:value1_2:0:1"));
 +
 +        // with nested UDF + aggregation and multiple cols
 +
 +        f = createFunction(KEYSPACE, "int,int",
 +                           "CREATE FUNCTION %s(k int, c int) " +
 +                           "CALLED ON NULL INPUT " +
 +                           "RETURNS int " +
 +                           "LANGUAGE java AS $$return k + c;$$");
 +
 +        assertColumnNames(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s WHERE k = 0"),
 +                          "sel1", "system.max(" + f + "(k, c))");
 +        assertRows(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s WHERE k = 0"),
 +                   row(1, 1));
 +
 +        assertColumnNames(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s"),
 +                          "sel1", "system.max(" + f + "(k, c))");
 +        assertRows(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s"),
 +                   row(2, 2));
 +
 +        // prepared parameters
 +
 +        assertRows(execute("SELECT c, m[?], fm[?] FROM %s WHERE k = 0", "1", "1"),
 +                   row(0, "value1", "fvalue1"),
 +                   row(1, "value1_2", "fvalue1_2")
 +        );
 +        assertRows(execute("SELECT c, sm[?], fsm[?] FROM %s WHERE k = 0", "1", "1"),
 +                   row(0, "svalue1", "fsvalue1"),
 +                   row(1, "svalue1", "fsvalue1")
 +        );
 +        assertRows(execute("SELECT k, l, m[?..?], o FROM %s WHERE k = 0", "1", "22"),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42),
 +                   row(0, "foobar", map("1", "value1_2"), 42)
 +        );
 +    }
 +
 +    @Test
 +    public void testMapOperationWithIntKey() throws Throwable
 +    {
 +        // used type "int" as map key intentionally since CQL parsing relies on "BigInteger"
 +
 +        createTable("CREATE TABLE %s (k int, c int, l text, " +
 +                    "m map<int, text>, " +
 +                    "fm frozen<map<int, text>>, " +
 +                    "sm map<int, text> STATIC, " +
 +                    "fsm frozen<map<int, text>> STATIC, " +
 +                    "o int, PRIMARY KEY (k, c))");
 +
 +        execute("INSERT INTO %s(k, c, l, m, fm, sm, fsm, o) VALUES (0, 0, 'foobar', ?, ?, ?, ?, 42)",
 +                map(22, "value22", 333, "value333"),
 +                map(1, "fvalue1", 22, "fvalue22", 333, "fvalue333"),
 +                map(22, "svalue22", 333, "svalue333"),
 +                map(1, "fsvalue1", 22, "fsvalue22", 333, "fsvalue333"));
 +
 +        execute("INSERT INTO %s(k, c, l, m, fm, sm, fsm, o) VALUES (2, 0, 'row2', ?, ?, ?, ?, 88)",
 +                map(22, "2value22", 333, "2value333"),
 +                map(1, "2fvalue1", 22, "2fvalue22", 333, "2fvalue333"),
 +                map(22, "2svalue22", 333, "2svalue333"),
 +                map(1, "2fsvalue1", 22, "2fsvalue22", 333, "2fsvalue333"));
 +
 +        flush();
 +
 +        execute("UPDATE %s SET m = m + ? WHERE k = 0 AND c = 0",
 +                map(1, "value1"));
 +
 +        execute("UPDATE %s SET sm = sm + ? WHERE k = 0",
 +                map(1, "svalue1"));
 +
 +        flush();
 +
 +        assertRows(execute("SELECT m[22] FROM %s WHERE k = 0 AND c = 0"),
 +                   row("value22")
 +        );
 +        assertRows(execute("SELECT m[1], m[22], m[333] FROM %s WHERE k = 0 AND c = 0"),
 +                   row("value1", "value22", "value333")
 +        );
 +        assertRows(execute("SELECT m[20 .. 25] FROM %s WHERE k = 0 AND c = 0"),
 +                   row(map(22, "value22"))
 +        );
 +
 +        execute("INSERT INTO %s(k, c, l, m, fm, o) VALUES (0, 1, 'foobar', ?, ?, 42)",
 +                map(1, "value1_2", 333, "value333_2"),
 +                map(1, "fvalue1_2", 333, "fvalue333_2"));
 +
 +        assertRows(execute("SELECT c, m[1], fm[1] FROM %s WHERE k = 0"),
 +                   row(0, "value1", "fvalue1"),
 +                   row(1, "value1_2", "fvalue1_2")
 +        );
 +        assertRows(execute("SELECT c, sm[1], fsm[1] FROM %s WHERE k = 0"),
 +                   row(0, "svalue1", "fsvalue1"),
 +                   row(1, "svalue1", "fsvalue1")
 +        );
 +
 +        // with UDF as slice arg
 +
 +        String f = createFunction(KEYSPACE, "int",
 +                                  "CREATE FUNCTION %s(arg int) " +
 +                                  "CALLED ON NULL INPUT " +
 +                                  "RETURNS int " +
 +                                  "LANGUAGE java AS 'return arg;'");
 +
 +        assertRows(execute("SELECT k, c, l, m[" + f +"(1).." + f +"(22)], o FROM %s WHERE k = 0"),
 +                   row(0, 0, "foobar", map(1, "value1",
 +                                           22, "value22"), 42),
 +                   row(0, 1, "foobar", map(1, "value1_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, c, l, m[" + f +"(?).." + f +"(?)], o FROM %s WHERE k = 0", 1, 22),
 +                   row(0, 0, "foobar", map(1, "value1",
 +                                           22, "value22"), 42),
 +                   row(0, 1, "foobar", map(1, "value1_2"), 42)
 +        );
 +
 +        // with UDF taking a map
 +
 +        f = createFunction(KEYSPACE, "map<int,text>",
 +                           "CREATE FUNCTION %s(m text) " +
 +                           "CALLED ON NULL INPUT " +
 +                           "RETURNS TEXT " +
 +                           "LANGUAGE java AS $$return m;$$");
 +
 +        assertRows(execute("SELECT k, c, " + f + "(m[1]) FROM %s WHERE k = 0"),
 +                   row(0, 0, "value1"),
 +                   row(0, 1, "value1_2"));
 +
 +        // with UDF taking multiple cols
 +
 +        f = createFunction(KEYSPACE, "map<int,text>,map<int,text>,int,int",
 +                           "CREATE FUNCTION %s(m1 map<int,text>, m2 text, k int, c int) " +
 +                           "CALLED ON NULL INPUT " +
 +                           "RETURNS TEXT " +
 +                           "LANGUAGE java AS $$return m1.get(1) + ':' + m2 + ':' + k + ':' + c;$$");
 +
 +        assertRows(execute("SELECT " + f + "(m, m[1], k, c) FROM %s WHERE k = 0"),
 +                   row("value1:value1:0:0"),
 +                   row("value1_2:value1_2:0:1"));
 +
 +        // with nested UDF + aggregation and multiple cols
 +
 +        f = createFunction(KEYSPACE, "int,int",
 +                           "CREATE FUNCTION %s(k int, c int) " +
 +                           "CALLED ON NULL INPUT " +
 +                           "RETURNS int " +
 +                           "LANGUAGE java AS $$return k + c;$$");
 +
 +        assertColumnNames(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s WHERE k = 0"),
 +                          "sel1", "system.max(" + f + "(k, c))");
 +        assertRows(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s WHERE k = 0"),
 +                   row(1, 1));
 +
 +        assertColumnNames(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s"),
 +                          "sel1", "system.max(" + f + "(k, c))");
 +        assertRows(execute("SELECT max(" + f + "(k, c)) as sel1, max(" + f + "(k, c)) FROM %s"),
 +                   row(2, 2));
 +
 +        // prepared parameters
 +
 +        assertRows(execute("SELECT c, m[?], fm[?] FROM %s WHERE k = 0", 1, 1),
 +                   row(0, "value1", "fvalue1"),
 +                   row(1, "value1_2", "fvalue1_2")
 +        );
 +        assertRows(execute("SELECT c, sm[?], fsm[?] FROM %s WHERE k = 0", 1, 1),
 +                   row(0, "svalue1", "fsvalue1"),
 +                   row(1, "svalue1", "fsvalue1")
 +        );
 +        assertRows(execute("SELECT k, l, m[?..?], o FROM %s WHERE k = 0", 1, 22),
 +                   row(0, "foobar", map(1, "value1",
 +                                        22, "value22"), 42),
 +                   row(0, "foobar", map(1, "value1_2"), 42)
 +        );
 +    }
 +
 +    @Test
 +    public void testMapOperationOnPartKey() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k frozen<map<text, text>> PRIMARY KEY, l text, o int)");
 +
 +        execute("INSERT INTO %s(k, l, o) VALUES (?, 'foobar', 42)", map("1", "value1", "22", "value22", "333", "value333"));
 +
 +        assertRows(execute("SELECT l, k['1'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", "value1", 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k['22'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", "value22", 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k['333'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", "value333", 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k['foobar'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", null, 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k['1'..'22'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", map("1", "value1",
 +                                     "22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k[''..'23'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", map("1", "value1",
 +                                     "22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k['2'..'3'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", map("22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k['22'..], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", map("22", "value22",
 +                                     "333", "value333"), 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k[..'22'], o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", map("1", "value1",
 +                                     "22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT l, k, o FROM %s WHERE k = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row("foobar", map("1", "value1",
 +                                     "22", "value22",
 +                                     "333", "value333"), 42)
 +        );
 +    }
 +
 +    @Test
 +    public void testMapOperationOnClustKey() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int, c frozen<map<text, text>>, l text, o int, PRIMARY KEY (k, c))");
 +
 +        execute("INSERT INTO %s(k, c, l, o) VALUES (0, ?, 'foobar', 42)", map("1", "value1", "22", "value22", "333", "value333"));
 +
 +        assertRows(execute("SELECT k, l, c['1'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", "value1", 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c['22'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", "value22", 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c['333'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", "value333", 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c['foobar'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", null, 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c['1'..'22'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c[''..'23'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c['2'..'3'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", map("22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c['22'..], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", map("22", "value22",
 +                                        "333", "value333"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c[..'22'], o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, c, o FROM %s WHERE k = 0 AND c = ?", map("1", "value1", "22", "value22", "333", "value333")),
 +                   row(0, "foobar", map("1", "value1",
 +                                        "22", "value22",
 +                                        "333", "value333"), 42)
 +        );
 +    }
 +
 +    @Test
 +    public void testSetOperation() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int, c int, l text, " +
 +                    "s set<text>, " +
 +                    "fs frozen<set<text>>, " +
 +                    "ss set<text> STATIC, " +
 +                    "fss frozen<set<text>> STATIC, " +
 +                    "o int, PRIMARY KEY (k, c))");
 +
 +        execute("INSERT INTO %s(k, c, l, s, fs, ss, fss, o) VALUES (0, 0, 'foobar', ?, ?, ?, ?, 42)",
 +                set("1", "22", "333"),
 +                set("f1", "f22", "f333"),
 +                set("s1", "s22", "s333"),
 +                set("fs1", "fs22", "fs333"));
 +
 +        flush();
 +
 +        execute("UPDATE %s SET s = s + ? WHERE k = 0 AND c = 0", set("22_2"));
 +
 +        execute("UPDATE %s SET ss = ss + ? WHERE k = 0", set("s22_2"));
 +
 +        flush();
 +
 +        execute("INSERT INTO %s(k, c, l, s, o) VALUES (0, 1, 'foobar', ?, 42)",
 +                set("22", "333"));
 +
 +        assertRows(execute("SELECT c, s, fs, ss, fss FROM %s WHERE k = 0"),
 +                   row(0, set("1", "22", "22_2", "333"), set("f1", "f22", "f333"), set("s1", "s22", "s22_2", "s333"), set("fs1", "fs22", "fs333")),
 +                   row(1, set("22", "333"), null, set("s1", "s22", "s22_2", "s333"), set("fs1", "fs22", "fs333"))
 +        );
 +
 +        assertRows(execute("SELECT c, s['1'], fs['f1'], ss['s1'], fss['fs1'] FROM %s WHERE k = 0"),
 +                   row(0, "1", "f1", "s1", "fs1"),
 +                   row(1, null, null, "s1", "fs1")
 +        );
 +
 +        assertRows(execute("SELECT s['1'], fs['f1'], ss['s1'], fss['fs1'] FROM %s WHERE k = 0 AND c = 0"),
 +                   row("1", "f1", "s1", "fs1")
 +        );
 +
 +        assertRows(execute("SELECT k, c, l, s['1'], fs['f1'], ss['s1'], fss['fs1'], o FROM %s WHERE k = 0"),
 +                   row(0, 0, "foobar", "1", "f1", "s1", "fs1", 42),
 +                   row(0, 1, "foobar", null, null, "s1", "fs1", 42)
 +        );
 +
 +        assertColumnNames(execute("SELECT k, l, s['1'], o FROM %s WHERE k = 0"),
 +                          "k", "l", "s['1']", "o");
 +
 +        assertRows(execute("SELECT k, l, s['22'], o FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "foobar", "22", 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s['333'], o FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "foobar", "333", 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s['foobar'], o FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "foobar", null, 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s['1'..'22'], o FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "foobar", set("1", "22"), 42)
 +        );
 +        assertColumnNames(execute("SELECT k, l, s[''..'22'], o FROM %s WHERE k = 0"),
 +                          "k", "l", "s[''..'22']", "o");
 +
 +        assertRows(execute("SELECT k, l, s[''..'23'], o FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "foobar", set("1", "22", "22_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s['2'..'3'], o FROM %s WHERE k = 0 AND c = 0"),
 +                   row(0, "foobar", set("22", "22_2"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s['22'..], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", set("22", "22_2", "333"), 42),
 +                   row(0, "foobar", set("22", "333"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s[..'22'], o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", set("1", "22"), 42),
 +                   row(0, "foobar", set("22"), 42)
 +        );
 +
 +        assertRows(execute("SELECT k, l, s, o FROM %s WHERE k = 0"),
 +                   row(0, "foobar", set("1", "22", "22_2", "333"), 42),
 +                   row(0, "foobar", set("22", "333"), 42)
 +        );
 +    }
 +
 +    @Test
 +    public void testCollectionSliceOnMV() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int, c int, l text, m map<text, text>, o int, PRIMARY KEY (k, c))");
 +        assertInvalidMessage("Cannot use collection element selection when defining a materialized view",
 +                             "CREATE MATERIALIZED VIEW " + KEYSPACE + ".view1 AS SELECT m['abc'] FROM %s WHERE k IS NOT NULL AND c IS NOT NULL AND m IS NOT NULL PRIMARY KEY (c, k)");
 +        assertInvalidMessage("Cannot use collection slice selection when defining a materialized view",
 +                             "CREATE MATERIALIZED VIEW " + KEYSPACE + ".view1 AS SELECT m['abc'..'def'] FROM %s WHERE k IS NOT NULL AND c IS NOT NULL AND m IS NOT NULL PRIMARY KEY (c, k)");
 +    }
 +
 +    @Test
 +    public void testElementAccessOnList() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (pk int PRIMARY KEY, l list<int>)");
 +        execute("INSERT INTO %s (pk, l) VALUES (1, [1, 2, 3])");
 +
 +        assertInvalidMessage("Element selection is only allowed on sets and maps, but l is a list",
 +                             "SELECT pk, l[0] FROM %s");
 +
 +        assertInvalidMessage("Slice selection is only allowed on sets and maps, but l is a list",
 +                "SELECT pk, l[1..3] FROM %s");
 +    }
 +
 +    @Test
 +    public void testCollectionOperationResultSetMetadata() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY," +
 +                    "m map<text, text>," +
 +                    "fm frozen<map<text, text>>," +
 +                    "s set<text>," +
 +                    "fs frozen<set<text>>)");
 +
 +        execute("INSERT INTO %s (k, m, fm, s, fs) VALUES (?, ?, ?, ?, ?)",
 +                0,
 +                map("1", "one", "2", "two"),
 +                map("1", "one", "2", "two"),
 +                set("1", "2", "3"),
 +                set("1", "2", "3"));
 +
 +        String cql = "SELECT k, " +
 +                     "m, m['2'], m['2'..'3'], m[..'2'], m['3'..], " +
 +                     "fm, fm['2'], fm['2'..'3'], fm[..'2'], fm['3'..], " +
 +                     "s, s['2'], s['2'..'3'], s[..'2'], s['3'..], " +
 +                     "fs, fs['2'], fs['2'..'3'], fs[..'2'], fs['3'..] " +
 +                     "FROM " + KEYSPACE + '.' + currentTable() + " WHERE k = 0";
 +        UntypedResultSet result = execute(cql);
 +        Iterator<ColumnSpecification> meta = result.metadata().iterator();
 +        meta.next();
 +        for (int i = 0; i < 4; i++)
 +        {
 +            // take the "full" collection selection
 +            ColumnSpecification ref = meta.next();
 +            ColumnSpecification selSingle = meta.next();
 +            assertEquals(ref.toString(), UTF8Type.instance, selSingle.type);
 +            for (int selOrSlice = 0; selOrSlice < 3; selOrSlice++)
 +            {
 +                ColumnSpecification selSlice = meta.next();
 +                assertEquals(ref.toString(), ref.type, selSlice.type);
 +            }
 +        }
 +
 +        assertRows(result,
 +                   row(0,
 +                       map("1", "one", "2", "two"), "two", map("2", "two"), map("1", "one", "2", "two"), map(),
 +                       map("1", "one", "2", "two"), "two", map("2", "two"), map("1", "one", "2", "two"), map(),
 +                       set("1", "2", "3"), "2", set("2", "3"), set("1", "2"), set("3"),
 +                       set("1", "2", "3"), "2", set("2", "3"), set("1", "2"), set("3")));
 +
 +        Session session = sessionNet();
 +        ResultSet rset = session.execute(cql);
 +        ColumnDefinitions colDefs = rset.getColumnDefinitions();
 +        Iterator<ColumnDefinitions.Definition> colDefIter = colDefs.asList().iterator();
 +        colDefIter.next();
 +        for (int i = 0; i < 4; i++)
 +        {
 +            // take the "full" collection selection
 +            ColumnDefinitions.Definition ref = colDefIter.next();
 +            ColumnDefinitions.Definition selSingle = colDefIter.next();
 +            assertEquals(ref.getName(), DataType.NativeType.text(), selSingle.getType());
 +            for (int selOrSlice = 0; selOrSlice < 3; selOrSlice++)
 +            {
 +                ColumnDefinitions.Definition selSlice = colDefIter.next();
 +                assertEquals(ref.getName() + ' ' + ref.getType(), ref.getType(), selSlice.getType());
 +            }
 +        }
 +    }
 +
 +    @Test
 +    public void testFrozenCollectionNestedAccess() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<text, frozen<map<text, set<int>>>>)");
 +
 +        execute("INSERT INTO %s(k, m) VALUES (0, ?)", map("1", map("a", set(1, 2, 4), "b", set(3)), "2", map("a", set(2, 4))));
 +
 +        assertRows(execute("SELECT m[?] FROM %s WHERE k = 0", "1"), row(map("a", set(1, 2, 4), "b", set(3))));
 +        assertRows(execute("SELECT m[?][?] FROM %s WHERE k = 0", "1", "a"), row(set(1, 2, 4)));
 +        assertRows(execute("SELECT m[?][?][?] FROM %s WHERE k = 0", "1", "a", 2), row(2));
 +        assertRows(execute("SELECT m[?][?][?..?] FROM %s WHERE k = 0", "1", "a", 2, 3), row(set(2)));
 +
 +        // Checks it still work after flush
 +        flush();
 +
 +        assertRows(execute("SELECT m[?] FROM %s WHERE k = 0", "1"), row(map("a", set(1, 2, 4), "b", set(3))));
 +        assertRows(execute("SELECT m[?][?] FROM %s WHERE k = 0", "1", "a"), row(set(1, 2, 4)));
 +        assertRows(execute("SELECT m[?][?][?] FROM %s WHERE k = 0", "1", "a", 2), row(2));
 +        assertRows(execute("SELECT m[?][?][?..?] FROM %s WHERE k = 0", "1", "a", 2, 3), row(set(2)));
 +    }
 +
 +    @Test
 +    public void testUDTAndCollectionNestedAccess() throws Throwable
 +    {
 +        String type = createType("CREATE TYPE %s (s set<int>, m map<text, text>)");
 +
 +        assertInvalidMessage("Non-frozen UDTs are not allowed inside collections",
 +                             "CREATE TABLE " + KEYSPACE + "t (k int PRIMARY KEY, v map<text, " + type + ">)");
 +
 +        String mapType = "map<text, frozen<" + type + ">>";
 +        for (boolean frozen : new boolean[]{false, true})
 +        {
 +            mapType = frozen ? "frozen<" + mapType + ">" : mapType;
 +
 +            createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + mapType + ")");
 +
 +            execute("INSERT INTO %s(k, v) VALUES (0, ?)", map("abc", userType("s", set(2, 4, 6), "m", map("a", "v1", "d", "v2"))));
 +
 +            beforeAndAfterFlush(() ->
 +            {
 +                assertRows(execute("SELECT v[?].s FROM %s WHERE k = 0", "abc"), row(set(2, 4, 6)));
 +                assertRows(execute("SELECT v[?].m[..?] FROM %s WHERE k = 0", "abc", "b"), row(map("a", "v1")));
 +                assertRows(execute("SELECT v[?].m[?] FROM %s WHERE k = 0", "abc", "d"), row("v2"));
 +            });
 +        }
 +
 +        assertInvalidMessage("Non-frozen UDTs with nested non-frozen collections are not supported",
 +                             "CREATE TABLE " + KEYSPACE + ".t (k int PRIMARY KEY, v " + type + ")");
 +
 +        type = createType("CREATE TYPE %s (s frozen<set<int>>, m frozen<map<text, text>>)");
 +
 +        for (boolean frozen : new boolean[]{false, true})
 +        {
 +            type = frozen ? "frozen<" + type + ">" : type;
 +
 +            createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + type + ")");
 +
 +            execute("INSERT INTO %s(k, v) VALUES (0, ?)", userType("s", set(2, 4, 6), "m", map("a", "v1", "d", "v2")));
 +
 +            beforeAndAfterFlush(() ->
 +            {
 +                assertRows(execute("SELECT v.s[?] FROM %s WHERE k = 0", 2), row(2));
 +                assertRows(execute("SELECT v.s[?..?] FROM %s WHERE k = 0", 2, 5), row(set(2, 4)));
 +                assertRows(execute("SELECT v.s[..?] FROM %s WHERE k = 0", 3), row(set(2)));
 +                assertRows(execute("SELECT v.m[..?] FROM %s WHERE k = 0", "b"), row(map("a", "v1")));
 +                assertRows(execute("SELECT v.m[?] FROM %s WHERE k = 0", "d"), row("v2"));
 +            });
 +        }
 +    }
 +
 +    @Test
 +    public void testMapOverlappingSlices() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<int, int>)");
 +
 +        execute("INSERT INTO %s(k, m) VALUES (?, ?)", 0, map(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5));
 +
 +        flush();
 +
 +        assertRows(execute("SELECT m[7..8] FROM %s WHERE k=?", 0),
 +                   row(map()));
 +
 +        assertRows(execute("SELECT m[0..3] FROM %s WHERE k=?", 0),
 +                   row(map(0, 0, 1, 1, 2, 2, 3, 3)));
 +
 +        assertRows(execute("SELECT m[0..3], m[2..4] FROM %s WHERE k=?", 0),
 +                   row(map(0, 0, 1, 1, 2, 2, 3, 3), map(2, 2, 3, 3, 4, 4)));
 +
 +        assertRows(execute("SELECT m, m[2..4] FROM %s WHERE k=?", 0),
 +                   row(map(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5), map(2, 2, 3, 3, 4, 4)));
 +
 +        assertRows(execute("SELECT m[..3], m[3..4] FROM %s WHERE k=?", 0),
 +                   row(map(0, 0, 1, 1, 2, 2, 3, 3), map(3, 3, 4, 4)));
 +
 +        assertRows(execute("SELECT m[1..3], m[2] FROM %s WHERE k=?", 0),
 +                   row(map(1, 1, 2, 2, 3, 3), 2));
 +    }
 +
 +    @Test
 +    public void testMapOverlappingSlicesWithDoubles() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<double, double>)");
 +
 +        execute("INSERT INTO %s(k, m) VALUES (?, ?)", 0, map(0.0, 0.0, 1.1, 1.1, 2.2, 2.2, 3.0, 3.0, 4.4, 4.4, 5.5, 5.5));
 +
 +        flush();
 +
 +        assertRows(execute("SELECT m[0.0..3.0] FROM %s WHERE k=?", 0),
 +                   row(map(0.0, 0.0, 1.1, 1.1, 2.2, 2.2, 3.0, 3.0)));
 +
 +        assertRows(execute("SELECT m[0...3.], m[2.2..4.4] FROM %s WHERE k=?", 0),
 +                   row(map(0.0, 0.0, 1.1, 1.1, 2.2, 2.2, 3.0, 3.0), map(2.2, 2.2, 3.0, 3.0, 4.4, 4.4)));
 +
 +        assertRows(execute("SELECT m, m[2.2..4.4] FROM %s WHERE k=?", 0),
 +                   row(map(0.0, 0.0, 1.1, 1.1, 2.2, 2.2, 3.0, 3.0, 4.4, 4.4, 5.5, 5.5), map(2.2, 2.2, 3.0, 3.0, 4.4, 4.4)));
 +
 +        assertRows(execute("SELECT m[..3.], m[3...4.4] FROM %s WHERE k=?", 0),
 +                   row(map(0.0, 0.0, 1.1, 1.1, 2.2, 2.2, 3.0, 3.0), map(3.0, 3.0, 4.4, 4.4)));
 +
 +        assertRows(execute("SELECT m[1.1..3.0], m[2.2] FROM %s WHERE k=?", 0),
 +                   row(map(1.1, 1.1, 2.2, 2.2, 3.0, 3.0), 2.2));
 +    }
 +
 +    @Test
 +    public void testNestedAccessWithNestedMap() throws Throwable
 +    {
 +        createTable("CREATE TABLE %s (id text PRIMARY KEY, m map<float, frozen<map<int, text>>>)");
 +
 +        execute("INSERT INTO %s (id,m) VALUES ('1', {1: {2: 'one-two'}})");
 +
 +        flush();
 +
 +        assertRows(execute("SELECT m[1][2] FROM %s WHERE id = '1'"),
 +                   row("one-two"));
 +
 +        assertRows(execute("SELECT m[1..][2] FROM %s WHERE id = '1'"),
 +                   row((Map) null));
 +
 +        assertRows(execute("SELECT m[1][..2] FROM %s WHERE id = '1'"),
 +                   row(map(2, "one-two")));
 +
 +        assertRows(execute("SELECT m[1..][..2] FROM %s WHERE id = '1'"),
 +                   row(map(1F, map(2, "one-two"))));
 +    }
++
++    @Test
+     public void testInsertingCollectionsWithInvalidElements() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, s) VALUES (0, ?)",
+                              set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
+         assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, l) VALUES (0, ?)",
+                              list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
+         assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+ 
+         assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+ 
+         assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/10b397e2/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------


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


[06/10] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

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


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

Branch: refs/heads/cassandra-3.0
Commit: e51c85f2e6045e077dad8575f7737d502fd50414
Parents: af20226 b77e11c
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:31:08 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:34:32 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +-
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 220 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 52b0027,08c6e48..c98ffd9
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,60 -1,15 +1,61 @@@
 -2.2.11
 - * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
 - * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 +3.0.15
 + * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 + * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
 + * Fix secondary index queries on COMPACT tables (CASSANDRA-13627)
 + * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
- 
++ Merged from 2.2:
++* Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  
 -
 -2.2.10
 +3.0.14
 + * Ensure int overflow doesn't occur when calculating large partition warning size (CASSANDRA-13172)
 + * Ensure consistent view of partition columns between coordinator and replica in ColumnFilter (CASSANDRA-13004)
 + * Failed unregistering mbean during drop keyspace (CASSANDRA-13346)
 + * nodetool scrub/cleanup/upgradesstables exit code is wrong (CASSANDRA-13542)
 + * Fix the reported number of sstable data files accessed per read (CASSANDRA-13120)
 + * Fix schema digest mismatch during rolling upgrades from versions before 3.0.12 (CASSANDRA-13559)
 + * Upgrade JNA version to 4.4.0 (CASSANDRA-13072)
 + * Interned ColumnIdentifiers should use minimal ByteBuffers (CASSANDRA-13533)
 + * ReverseIndexedReader may drop rows during 2.1 to 3.0 upgrade (CASSANDRA-13525)
 + * Fix repair process violating start/end token limits for small ranges (CASSANDRA-13052)
 + * Add storage port options to sstableloader (CASSANDRA-13518)
 + * Properly handle quoted index names in cqlsh DESCRIBE output (CASSANDRA-12847)
 + * Avoid reading static row twice from old format sstables (CASSANDRA-13236)
 + * Fix NPE in StorageService.excise() (CASSANDRA-13163)
 + * Expire OutboundTcpConnection messages by a single Thread (CASSANDRA-13265)
 + * Fail repair if insufficient responses received (CASSANDRA-13397)
 + * Fix SSTableLoader fail when the loaded table contains dropped columns (CASSANDRA-13276)
 + * Avoid name clashes in CassandraIndexTest (CASSANDRA-13427)
 + * Handling partially written hint files (CASSANDRA-12728)
 + * Interrupt replaying hints on decommission (CASSANDRA-13308)
 + * Fix schema version calculation for rolling upgrades (CASSANDRA-13441)
 +Merged from 2.2:
   * Nodes started with join_ring=False should be able to serve requests when authentication is enabled (CASSANDRA-11381)
   * cqlsh COPY FROM: increment error count only for failures, not for attempts (CASSANDRA-13209)
 - * nodetool upgradesstables should upgrade system tables (CASSANDRA-13119)
 +
 +3.0.13
 + * Make reading of range tombstones more reliable (CASSANDRA-12811)
 + * Fix startup problems due to schema tables not completely flushed (CASSANDRA-12213)
 + * Fix view builder bug that can filter out data on restart (CASSANDRA-13405)
 + * Fix 2i page size calculation when there are no regular columns (CASSANDRA-13400)
 + * Fix the conversion of 2.X expired rows without regular column data (CASSANDRA-13395)
 + * Fix hint delivery when using ext+internal IPs with prefer_local enabled (CASSANDRA-13020)
 + * Fix possible NPE on upgrade to 3.0/3.X in case of IO errors (CASSANDRA-13389)
 + * Legacy deserializer can create empty range tombstones (CASSANDRA-13341)
 + * Use the Kernel32 library to retrieve the PID on Windows and fix startup checks (CASSANDRA-13333)
 + * Fix code to not exchange schema across major versions (CASSANDRA-13274)
 + * Dropping column results in "corrupt" SSTable (CASSANDRA-13337)
 + * Bugs handling range tombstones in the sstable iterators (CASSANDRA-13340)
 + * Fix CONTAINS filtering for null collections (CASSANDRA-13246)
 + * Applying: Use a unique metric reservoir per test run when using Cassandra-wide metrics residing in MBeans (CASSANDRA-13216)
 + * Propagate row deletions in 2i tables on upgrade (CASSANDRA-13320)
 + * Slice.isEmpty() returns false for some empty slices (CASSANDRA-13305)
 + * Add formatted row output to assertEmpty in CQL Tester (CASSANDRA-13238)
 + * Legacy caching options can prevent 3.0 upgrade (CASSANDRA-13384)
 + * Nodetool upgradesstables/scrub/compact ignores system tables (CASSANDRA-13410)
 + * Fix NPE issue in StorageService (CASSANDRA-13060)
 +Merged from 2.2:
   * Avoid starting gossiper in RemoveTest (CASSANDRA-13407)
   * Fix weightedSize() for row-cache reported by JMX and NodeTool (CASSANDRA-13393)
 - * Fix JVM metric paths (CASSANDRA-13103)
   * Honor truststore-password parameter in cassandra-stress (CASSANDRA-12773)
   * Discard in-flight shadow round responses (CASSANDRA-12653)
   * Don't anti-compact repaired data to avoid inconsistencies (CASSANDRA-13153)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/TupleType.java
index 5486183,bf7eae4..3af1054
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@@ -39,9 -39,10 +39,11 @@@ public class TupleType extends Abstract
  {
      protected final List<AbstractType<?>> types;
  
+     private final TupleSerializer serializer;
+ 
      public TupleType(List<AbstractType<?>> types)
      {
 +        super(ComparisonType.CUSTOM);
          for (int i = 0; i < types.size(); i++)
              types.set(i, types.get(i).freeze());
          this.types = types;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index e5cb480,69d5a5c..c00d707
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@@ -1006,4 -748,40 +1006,40 @@@ public class CollectionsTest extends CQ
          execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
          assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
      }
+ 
+     @Test
+     public void testInsertingCollectionsWithInvalidElements() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, s) VALUES (0, ?)",
+                              set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, l) VALUES (0, ?)",
+                              list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+ 
 -        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index c369ecd,3b4fb40..cbe4a15
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@@ -121,8 -121,16 +121,16 @@@ public class TupleTypeTest extends CQLT
  
          assertInvalidSyntax("INSERT INTO %s (k, t) VALUES (0, ())");
  
 -        assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
 +        assertInvalidMessage("Invalid tuple literal for t: too many elements. Type frozen<tuple<int, text, double>> expects 3 but got 4",
                               "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
+                              tuple(1, tuple(1, "1", 1.0, 1)));
+ 
 -        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
      }
  
      @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index e776f0e,9bafe4a..c279e00
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -54,6 -53,15 +54,15 @@@ public class UserTypesTest extends CQLT
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
          assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+ 
+         String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
+                              userType(1, tuple(1, "1", 1.0, 1)));
+ 
 -        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid user type literal for t: field b is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
      }
  
      @Test


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


[07/10] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

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


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

Branch: refs/heads/trunk
Commit: e51c85f2e6045e077dad8575f7737d502fd50414
Parents: af20226 b77e11c
Author: Benjamin Lerer <b....@gmail.com>
Authored: Fri Jul 7 13:31:08 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Fri Jul 7 13:34:32 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +-
 .../org/apache/cassandra/cql3/UserTypes.java    |  4 +-
 .../apache/cassandra/db/marshal/TupleType.java  | 45 ++++---------
 .../apache/cassandra/db/marshal/UserType.java   | 50 +++++----------
 .../cassandra/serializers/TupleSerializer.java  | 66 +++++++++++++++++++
 .../serializers/UserTypeSerializer.java         | 67 ++++++++++++++++++++
 .../validation/entities/CollectionsTest.java    | 36 +++++++++++
 .../cql3/validation/entities/TupleTypeTest.java | 10 ++-
 .../cql3/validation/entities/UserTypesTest.java |  9 +++
 9 files changed, 220 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 52b0027,08c6e48..c98ffd9
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,60 -1,15 +1,61 @@@
 -2.2.11
 - * Fix nested Tuples/UDTs validation (CASSANDRA-13646)
 - * Remove unused max_value_size_in_mb config setting from yaml (CASSANDRA-13625
 +3.0.15
 + * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557)
 + * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606)
 + * Fix secondary index queries on COMPACT tables (CASSANDRA-13627)
 + * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568)
- 
++ Merged from 2.2:
++* Fix nested Tuples/UDTs validation (CASSANDRA-13646)
  
 -
 -2.2.10
 +3.0.14
 + * Ensure int overflow doesn't occur when calculating large partition warning size (CASSANDRA-13172)
 + * Ensure consistent view of partition columns between coordinator and replica in ColumnFilter (CASSANDRA-13004)
 + * Failed unregistering mbean during drop keyspace (CASSANDRA-13346)
 + * nodetool scrub/cleanup/upgradesstables exit code is wrong (CASSANDRA-13542)
 + * Fix the reported number of sstable data files accessed per read (CASSANDRA-13120)
 + * Fix schema digest mismatch during rolling upgrades from versions before 3.0.12 (CASSANDRA-13559)
 + * Upgrade JNA version to 4.4.0 (CASSANDRA-13072)
 + * Interned ColumnIdentifiers should use minimal ByteBuffers (CASSANDRA-13533)
 + * ReverseIndexedReader may drop rows during 2.1 to 3.0 upgrade (CASSANDRA-13525)
 + * Fix repair process violating start/end token limits for small ranges (CASSANDRA-13052)
 + * Add storage port options to sstableloader (CASSANDRA-13518)
 + * Properly handle quoted index names in cqlsh DESCRIBE output (CASSANDRA-12847)
 + * Avoid reading static row twice from old format sstables (CASSANDRA-13236)
 + * Fix NPE in StorageService.excise() (CASSANDRA-13163)
 + * Expire OutboundTcpConnection messages by a single Thread (CASSANDRA-13265)
 + * Fail repair if insufficient responses received (CASSANDRA-13397)
 + * Fix SSTableLoader fail when the loaded table contains dropped columns (CASSANDRA-13276)
 + * Avoid name clashes in CassandraIndexTest (CASSANDRA-13427)
 + * Handling partially written hint files (CASSANDRA-12728)
 + * Interrupt replaying hints on decommission (CASSANDRA-13308)
 + * Fix schema version calculation for rolling upgrades (CASSANDRA-13441)
 +Merged from 2.2:
   * Nodes started with join_ring=False should be able to serve requests when authentication is enabled (CASSANDRA-11381)
   * cqlsh COPY FROM: increment error count only for failures, not for attempts (CASSANDRA-13209)
 - * nodetool upgradesstables should upgrade system tables (CASSANDRA-13119)
 +
 +3.0.13
 + * Make reading of range tombstones more reliable (CASSANDRA-12811)
 + * Fix startup problems due to schema tables not completely flushed (CASSANDRA-12213)
 + * Fix view builder bug that can filter out data on restart (CASSANDRA-13405)
 + * Fix 2i page size calculation when there are no regular columns (CASSANDRA-13400)
 + * Fix the conversion of 2.X expired rows without regular column data (CASSANDRA-13395)
 + * Fix hint delivery when using ext+internal IPs with prefer_local enabled (CASSANDRA-13020)
 + * Fix possible NPE on upgrade to 3.0/3.X in case of IO errors (CASSANDRA-13389)
 + * Legacy deserializer can create empty range tombstones (CASSANDRA-13341)
 + * Use the Kernel32 library to retrieve the PID on Windows and fix startup checks (CASSANDRA-13333)
 + * Fix code to not exchange schema across major versions (CASSANDRA-13274)
 + * Dropping column results in "corrupt" SSTable (CASSANDRA-13337)
 + * Bugs handling range tombstones in the sstable iterators (CASSANDRA-13340)
 + * Fix CONTAINS filtering for null collections (CASSANDRA-13246)
 + * Applying: Use a unique metric reservoir per test run when using Cassandra-wide metrics residing in MBeans (CASSANDRA-13216)
 + * Propagate row deletions in 2i tables on upgrade (CASSANDRA-13320)
 + * Slice.isEmpty() returns false for some empty slices (CASSANDRA-13305)
 + * Add formatted row output to assertEmpty in CQL Tester (CASSANDRA-13238)
 + * Legacy caching options can prevent 3.0 upgrade (CASSANDRA-13384)
 + * Nodetool upgradesstables/scrub/compact ignores system tables (CASSANDRA-13410)
 + * Fix NPE issue in StorageService (CASSANDRA-13060)
 +Merged from 2.2:
   * Avoid starting gossiper in RemoveTest (CASSANDRA-13407)
   * Fix weightedSize() for row-cache reported by JMX and NodeTool (CASSANDRA-13393)
 - * Fix JVM metric paths (CASSANDRA-13103)
   * Honor truststore-password parameter in cassandra-stress (CASSANDRA-12773)
   * Discard in-flight shadow round responses (CASSANDRA-12653)
   * Don't anti-compact repaired data to avoid inconsistencies (CASSANDRA-13153)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/cql3/UserTypes.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/db/marshal/TupleType.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/marshal/TupleType.java
index 5486183,bf7eae4..3af1054
--- a/src/java/org/apache/cassandra/db/marshal/TupleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java
@@@ -39,9 -39,10 +39,11 @@@ public class TupleType extends Abstract
  {
      protected final List<AbstractType<?>> types;
  
+     private final TupleSerializer serializer;
+ 
      public TupleType(List<AbstractType<?>> types)
      {
 +        super(ComparisonType.CUSTOM);
          for (int i = 0; i < types.size(); i++)
              types.set(i, types.get(i).freeze());
          this.types = types;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/src/java/org/apache/cassandra/db/marshal/UserType.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
index e5cb480,69d5a5c..c00d707
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java
@@@ -1006,4 -748,40 +1006,40 @@@ public class CollectionsTest extends CQ
          execute("UPDATE %s SET s = s - ? , s = s + ?  WHERE pk = ?", set(3), set(3, 4), 1);
          assertRows(execute("SELECT * FROM %s WHERE pk = 1") , row(1, set(0, 1, 2, 4)));
      }
+ 
+     @Test
+     public void testInsertingCollectionsWithInvalidElements() throws Throwable
+     {
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, s frozen<set<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, s) VALUES (0, ?)",
+                              set(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid set literal for s: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, s) VALUES (0, {(1, '1', 1.0, 1)})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, l frozen<list<tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, l) VALUES (0, ?)",
+                              list(tuple(1, "1", 1.0, 1), tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid list literal for l: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, l) VALUES (0, [(1, '1', 1.0, 1)])");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<tuple<int, text, double>, int>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(tuple(1, "1", 1.0, 1), 1, tuple(2, "2", 2.0, 2), 2));
+ 
 -        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid map literal for m: key (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {(1, '1', 1.0, 1) : 1})");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, m) VALUES (0, ?)",
+                              map(1, tuple(1, "1", 1.0, 1), 2, tuple(2, "2", 2.0, 2)));
+ 
 -        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})");
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
index c369ecd,3b4fb40..cbe4a15
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java
@@@ -121,8 -121,16 +121,16 @@@ public class TupleTypeTest extends CQLT
  
          assertInvalidSyntax("INSERT INTO %s (k, t) VALUES (0, ())");
  
 -        assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
 +        assertInvalidMessage("Invalid tuple literal for t: too many elements. Type frozen<tuple<int, text, double>> expects 3 but got 4",
                               "INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
+ 
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<int, text, double>>>)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
+                              tuple(1, tuple(1, "1", 1.0, 1)));
+ 
 -        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid tuple literal for t: component 1 is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, (1, (1, '1', 1.0, 1)))");
      }
  
      @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e51c85f2/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
index e776f0e,9bafe4a..c279e00
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
@@@ -54,6 -53,15 +54,15 @@@ public class UserTypesTest extends CQLT
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test");
          assertInvalidMessage("Not enough bytes to read 0th field f",
                               "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE);
+ 
+         String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)");
+         createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)");
+         assertInvalidMessage("Invalid remaining data after end of tuple value",
+                              "INSERT INTO %s (k, t) VALUES (0, ?)",
+                              userType(1, tuple(1, "1", 1.0, 1)));
+ 
 -        assertInvalidMessage("Invalid user type literal for t: field b is not of type tuple<int, text, double>",
++        assertInvalidMessage("Invalid user type literal for t: field b is not of type frozen<tuple<int, text, double>>",
+                              "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})");
      }
  
      @Test


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