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