Ability to freeze UDT
patch by slebresne; reviewed by iamaleksey for CASSANDRA-7857
diff --git a/CHANGES.txt b/CHANGES.txt
index 477a332..b33bec4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.0-rc7
+ * Add frozen keyword and require UDT to be frozen (CASSANDRA-7857)
* Track added sstable size correctly (CASSANDRA-7239)
* (cqlsh) Fix case insensitivity (CASSANDRA-7834)
* Fix failure to stream ranges when moving (CASSANDRA-7836)
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java
index a26a903..1589d6a 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Type.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java
@@ -284,6 +284,8 @@
// actual type used, so Raw is a "not yet prepared" CQL3Type.
public abstract class Raw
{
+ protected boolean frozen;
+
public boolean isCollection()
{
return false;
@@ -294,6 +296,12 @@
return false;
}
+ public Raw freeze()
+ {
+ frozen = true;
+ return this;
+ }
+
public abstract CQL3Type prepare(String keyspace) throws InvalidRequestException;
public static Raw from(CQL3Type type)
@@ -345,6 +353,16 @@
return new RawTuple(ts);
}
+ public static Raw frozen(CQL3Type.Raw t) throws InvalidRequestException
+ {
+ if (t instanceof RawUT)
+ return ((RawUT)t).freeze();
+ if (t instanceof RawTuple)
+ return ((RawTuple)t).freeze();
+
+ throw new InvalidRequestException("frozen<> is only currently only allowed on User-Defined and tuple types");
+ }
+
private static class RawType extends Raw
{
private CQL3Type type;
@@ -384,6 +402,13 @@
this.values = values;
}
+ public Raw freeze()
+ {
+ keys.freeze();
+ values.freeze();
+ return super.freeze();
+ }
+
public boolean isCollection()
{
return true;
@@ -445,9 +470,17 @@
if (type == null)
throw new InvalidRequestException("Unknown type " + name);
+ if (!frozen)
+ throw new InvalidRequestException("Non-frozen User-Defined types are not supported, please use frozen<>");
+
return new UserDefined(name.toString(), type);
}
+ public boolean isUDT()
+ {
+ return true;
+ }
+
@Override
public String toString()
{
@@ -464,6 +497,13 @@
this.types = types;
}
+ public Raw freeze()
+ {
+ for (CQL3Type.Raw t : types)
+ t.freeze();
+ return super.freeze();
+ }
+
public boolean isCollection()
{
return false;
@@ -474,6 +514,10 @@
List<AbstractType<?>> ts = new ArrayList<>(types.size());
for (CQL3Type.Raw t : types)
ts.add(t.prepare(keyspace).getType());
+
+ if (!frozen)
+ throw new InvalidRequestException("Non-frozen tuples are not supported, please use frozen<>");
+
return new Tuple(new TupleType(ts));
}
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 1ff6ab2..6247a03 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -1071,6 +1071,14 @@
| c=collection_type { $t = c; }
| tt=tuple_type { $t = tt; }
| id=userTypeName { $t = CQL3Type.Raw.userType(id); }
+ | K_FROZEN '<' f=comparatorType '>'
+ {
+ try {
+ $t = CQL3Type.Raw.frozen(f);
+ } catch (InvalidRequestException e) {
+ addRecognitionError(e.getMessage());
+ }
+ }
| s=STRING_LITERAL
{
try {
@@ -1277,6 +1285,7 @@
K_TRIGGER: T R I G G E R;
K_STATIC: S T A T I C;
+K_FROZEN: F R O Z E N;
// Case-insensitive alpha characters
fragment A: ('a'|'A');
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index c412fe3..d56b5df 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -204,12 +204,9 @@
protected UntypedResultSet execute(String query, Object... values) throws Throwable
{
- if (currentTable == null)
- throw new RuntimeException("You must create a table first with createTable");
-
try
{
- query = String.format(query, KEYSPACE + "." + currentTable);
+ query = currentTable == null ? query : String.format(query, KEYSPACE + "." + currentTable);
UntypedResultSet rs;
if (USE_PREPARED_VALUES)
diff --git a/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java b/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
index 84512a5..4ce24f8 100644
--- a/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
+++ b/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java
@@ -24,7 +24,7 @@
@Test
public void testTuplePutAndGet() throws Throwable
{
- createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple<int, text, double>)");
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, text, double>>)");
execute("INSERT INTO %s (k, t) VALUES (?, ?)", 0, tuple(3, "foo", 3.4));
execute("INSERT INTO %s (k, t) VALUES (?, ?)", 1, tuple(8, "bar", 0.2));
@@ -49,7 +49,7 @@
@Test
public void testNestedTuple() throws Throwable
{
- createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple<int, tuple<text, double>>)");
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, tuple<text, double>>>)");
execute("INSERT INTO %s (k, t) VALUES (?, ?)", 0, tuple(3, tuple("foo", 3.4)));
execute("INSERT INTO %s (k, t) VALUES (?, ?)", 1, tuple(8, tuple("bar", 0.2)));
@@ -62,7 +62,7 @@
@Test
public void testTupleInPartitionKey() throws Throwable
{
- createTable("CREATE TABLE %s (t tuple<int, text> PRIMARY KEY)");
+ createTable("CREATE TABLE %s (t frozen<tuple<int, text>> PRIMARY KEY)");
execute("INSERT INTO %s (t) VALUES (?)", tuple(3, "foo"));
assertAllRows(row(tuple(3, "foo")));
@@ -71,7 +71,7 @@
@Test
public void testTupleInClusteringKey() throws Throwable
{
- createTable("CREATE TABLE %s (k int, t tuple<int, text>, PRIMARY KEY (k, t))");
+ createTable("CREATE TABLE %s (k int, t frozen<tuple<int, text>>, PRIMARY KEY (k, t))");
execute("INSERT INTO %s (k, t) VALUES (?, ?)", 0, tuple(5, "bar"));
execute("INSERT INTO %s (k, t) VALUES (?, ?)", 0, tuple(3, "foo"));
@@ -89,9 +89,15 @@
@Test
public void testInvalidQueries() throws Throwable
{
- createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple<int, text, double>)");
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<tuple<int, text, double>>)");
assertInvalid("INSERT INTO %s (k, t) VALUES (0, ())");
assertInvalid("INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
}
+
+ @Test
+ public void testNonFrozenTuple() throws Throwable
+ {
+ assertInvalid("CREATE TABLE wrong (k int PRIMARY KEY, v tuple<int, text>)");
+ }
}
diff --git a/test/unit/org/apache/cassandra/cql3/UserTypesTest.java b/test/unit/org/apache/cassandra/cql3/UserTypesTest.java
index c7f1851..ca84102 100644
--- a/test/unit/org/apache/cassandra/cql3/UserTypesTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UserTypesTest.java
@@ -25,32 +25,37 @@
public void testInvalidField() throws Throwable
{
String myType = createType("CREATE TYPE %s (f int)");
- createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + myType + ")");
+ createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<" + myType + ">)");
// 's' is not a field of myType
assertInvalid("INSERT INTO %s (k, v) VALUES (?, {s : ?})", 0, 1);
}
-
@Test
public void testFor7684() throws Throwable
{
String myType = createType("CREATE TYPE %s (x double)");
- createTable("CREATE TABLE %s (k int, v " + myType + ", b boolean static, PRIMARY KEY (k, v))");
+ createTable("CREATE TABLE %s (k int, v frozen<" + myType + ">, b boolean static, PRIMARY KEY (k, v))");
execute("INSERT INTO %s(k, v) VALUES (?, {x:?})", 1, -104.99251);
execute("UPDATE %s SET b = ? WHERE k = ?", true, 1);
- System.out.println("-- First query");
assertRows(execute("SELECT v.x FROM %s WHERE k = ? AND v = {x:?}", 1, -104.99251),
row(-104.99251)
);
flush();
- System.out.println("-- 2nd query");
assertRows(execute("SELECT v.x FROM %s WHERE k = ? AND v = {x:?}", 1, -104.99251),
row(-104.99251)
);
}
+
+ @Test
+ public void testNonFrozenUDT() throws Throwable
+ {
+ // Using a UDT without frozen shouldn't work
+ String myType = createType("CREATE TYPE %s (f int)");
+ assertInvalid("CREATE TABLE wrong (k int PRIMARY KEY, v " + myType + ")");
+ }
}