diff --git a/CHANGES.txt b/CHANGES.txt index bd81431f77..a58af8f6f1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -49,6 +49,7 @@ Merged from 1.2: 2.1.0 + * 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/bin/cassandra-shuffle b/bin/cassandra-shuffle deleted file mode 100755 index 10a409d8bc..0000000000 --- a/bin/cassandra-shuffle +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh -# 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. - -if [ "x$CASSANDRA_INCLUDE" = "x" ]; then - for include in /usr/share/cassandra/cassandra.in.sh \ - /usr/local/share/cassandra/cassandra.in.sh \ - /opt/cassandra/cassandra.in.sh \ - "`dirname "$0"`/cassandra.in.sh"; do - if [ -r "$include" ]; then - . "$include" - break - fi - done -elif [ -r "$CASSANDRA_INCLUDE" ]; then - . "$CASSANDRA_INCLUDE" -fi - -# Use JAVA_HOME if set, otherwise look for java in PATH -if [ -x "$JAVA_HOME/bin/java" ]; then - JAVA="$JAVA_HOME/bin/java" -else - JAVA="`which java`" -fi - -if [ -z "$CASSANDRA_CONF" -o -z "$CLASSPATH" ]; then - echo "You must set the CASSANDRA_CONF and CLASSPATH vars" >&2 - exit 1 -fi - -# Special-case path variables. -case "`uname`" in - CYGWIN*) - CLASSPATH="`cygpath -p -w "$CLASSPATH"`" - CASSANDRA_CONF="`cygpath -p -w "$CASSANDRA_CONF"`" - ;; -esac - -"$JAVA" -cp "$CLASSPATH" \ - -Xmx32m \ - -Dcassandra.storagedir="$cassandra_storagedir" \ - -Dlogback.configurationFile=logback-tools.xml \ - org.apache.cassandra.tools.Shuffle $@ - -# vi:ai sw=4 ts=4 tw=0 et diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java index a26a9032e8..1589d6aa95 100644 --- a/src/java/org/apache/cassandra/cql3/CQL3Type.java +++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java @@ -284,6 +284,8 @@ public interface CQL3Type // 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 @@ public interface CQL3Type 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 @@ public interface CQL3Type 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 @@ public interface CQL3Type this.values = values; } + public Raw freeze() + { + keys.freeze(); + values.freeze(); + return super.freeze(); + } + public boolean isCollection() { return true; @@ -445,9 +470,17 @@ public interface CQL3Type 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 @@ public interface CQL3Type 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 @@ public interface CQL3Type List> 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 6bfda151fb..9d54866673 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -1086,6 +1086,14 @@ comparatorType returns [CQL3Type.Raw t] | 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 { @@ -1292,6 +1300,7 @@ K_TUPLE: T U P L E; 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 c8401d26e8..e776fc7d9d 100644 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@ -220,12 +220,9 @@ public abstract class CQLTester 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 354a8f99ce..53e7e71bd5 100644 --- a/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java +++ b/test/unit/org/apache/cassandra/cql3/TupleTypeTest.java @@ -24,7 +24,7 @@ public class TupleTypeTest extends CQLTester @Test public void testTuplePutAndGet() throws Throwable { - createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple)"); + createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen>)"); 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 @@ public class TupleTypeTest extends CQLTester @Test public void testNestedTuple() throws Throwable { - createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple>)"); + createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen>>)"); 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 @@ public class TupleTypeTest extends CQLTester @Test public void testTupleInPartitionKey() throws Throwable { - createTable("CREATE TABLE %s (t tuple PRIMARY KEY)"); + createTable("CREATE TABLE %s (t frozen> PRIMARY KEY)"); execute("INSERT INTO %s (t) VALUES (?)", tuple(3, "foo")); assertAllRows(row(tuple(3, "foo"))); @@ -71,7 +71,7 @@ public class TupleTypeTest extends CQLTester @Test public void testTupleInClusteringKey() throws Throwable { - createTable("CREATE TABLE %s (k int, t tuple, PRIMARY KEY (k, t))"); + createTable("CREATE TABLE %s (k int, t frozen>, 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 @@ public class TupleTypeTest extends CQLTester @Test public void testInvalidQueries() throws Throwable { - createTable("CREATE TABLE %s (k int PRIMARY KEY, t tuple)"); + createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen>)"); assertInvalidSyntax("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)"); + } } diff --git a/test/unit/org/apache/cassandra/cql3/UserTypesTest.java b/test/unit/org/apache/cassandra/cql3/UserTypesTest.java index 0dfb6f7605..ca84102c1f 100644 --- a/test/unit/org/apache/cassandra/cql3/UserTypesTest.java +++ b/test/unit/org/apache/cassandra/cql3/UserTypesTest.java @@ -25,18 +25,17 @@ public class UserTypesTest extends CQLTester 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); @@ -51,4 +50,12 @@ public class UserTypesTest extends CQLTester 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 + ")"); + } }