diff --git a/CHANGES.txt b/CHANGES.txt index 028b0ff3a4..5f08069bda 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,8 @@ * Frozen RawTuple is not annotated with frozen in the toString method (CASSANDRA-15857) Merged from 3.0: * Fix empty/null json string representation (CASSANDRA-15896) +Merged from 2.2: + * Fix CQL parsing of collections when the column type is reversed (CASSANDRA-15814) 3.11.7 diff --git a/src/java/org/apache/cassandra/cql3/Lists.java b/src/java/org/apache/cassandra/cql3/Lists.java index 6a2a5a504b..b8fb825579 100644 --- a/src/java/org/apache/cassandra/cql3/Lists.java +++ b/src/java/org/apache/cassandra/cql3/Lists.java @@ -34,6 +34,7 @@ import org.apache.cassandra.db.rows.*; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.ListType; +import org.apache.cassandra.db.marshal.ReversedType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.CollectionSerializer; import org.apache.cassandra.serializers.MarshalException; @@ -55,7 +56,17 @@ public abstract class Lists public static ColumnSpecification valueSpecOf(ColumnSpecification column) { - return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((ListType)column.type).getElementsType()); + return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), elementsType(column.type)); + } + + private static AbstractType unwrap(AbstractType type) + { + return type.isReversed() ? unwrap(((ReversedType) type).baseType) : type; + } + + private static AbstractType elementsType(AbstractType type) + { + return ((ListType) unwrap(type)).getElementsType(); } public static class Literal extends Term.Raw @@ -92,7 +103,9 @@ public abstract class Lists private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - if (!(receiver.type instanceof ListType)) + AbstractType type = unwrap(receiver.type); + + if (!(type instanceof ListType)) throw new InvalidRequestException(String.format("Invalid list literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); ColumnSpecification valueSpec = Lists.valueSpecOf(receiver); diff --git a/src/java/org/apache/cassandra/cql3/Maps.java b/src/java/org/apache/cassandra/cql3/Maps.java index 5faa8cc9c5..c26da0ff24 100644 --- a/src/java/org/apache/cassandra/cql3/Maps.java +++ b/src/java/org/apache/cassandra/cql3/Maps.java @@ -44,12 +44,27 @@ public abstract class Maps public static ColumnSpecification keySpecOf(ColumnSpecification column) { - return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("key(" + column.name + ")", true), ((MapType)column.type).getKeysType()); + return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("key(" + column.name + ")", true), keysType(column.type)); } public static ColumnSpecification valueSpecOf(ColumnSpecification column) { - return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((MapType)column.type).getValuesType()); + return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), valuesType(column.type)); + } + + private static AbstractType unwrap(AbstractType type) + { + return type.isReversed() ? unwrap(((ReversedType) type).baseType) : type; + } + + private static AbstractType keysType(AbstractType type) + { + return ((MapType) unwrap(type)).getKeysType(); + } + + private static AbstractType valuesType(AbstractType type) + { + return ((MapType) unwrap(type)).getValuesType(); } public static class Literal extends Term.Raw @@ -82,13 +97,15 @@ public abstract class Maps values.put(k, v); } - DelayedValue value = new DelayedValue(((MapType)receiver.type).getKeysType(), values); + DelayedValue value = new DelayedValue(keysType(receiver.type), values); return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; } private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - if (!(receiver.type instanceof MapType)) + AbstractType type = unwrap(receiver.type); + + if (!(type instanceof MapType)) throw new InvalidRequestException(String.format("Invalid map literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); ColumnSpecification keySpec = Maps.keySpecOf(receiver); diff --git a/src/java/org/apache/cassandra/cql3/Sets.java b/src/java/org/apache/cassandra/cql3/Sets.java index d17a771855..92457cf7cd 100644 --- a/src/java/org/apache/cassandra/cql3/Sets.java +++ b/src/java/org/apache/cassandra/cql3/Sets.java @@ -43,7 +43,17 @@ public abstract class Sets public static ColumnSpecification valueSpecOf(ColumnSpecification column) { - return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((SetType)column.type).getElementsType()); + return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), elementsType(column.type)); + } + + private static AbstractType unwrap(AbstractType type) + { + return type.isReversed() ? unwrap(((ReversedType) type).baseType) : type; + } + + private static AbstractType elementsType(AbstractType type) + { + return ((SetType) unwrap(type)).getElementsType(); } public static class Literal extends Term.Raw @@ -79,17 +89,19 @@ public abstract class Sets values.add(t); } - DelayedValue value = new DelayedValue(((SetType)receiver.type).getElementsType(), values); + DelayedValue value = new DelayedValue(elementsType(receiver.type), values); return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; } private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - if (!(receiver.type instanceof SetType)) + AbstractType type = unwrap(receiver.type); + + if (!(type instanceof SetType)) { // We've parsed empty maps as a set literal to break the ambiguity so // handle that case now - if ((receiver.type instanceof MapType) && elements.isEmpty()) + if ((type instanceof MapType) && elements.isEmpty()) return; throw new InvalidRequestException(String.format("Invalid set literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/FrozenCollectionsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/FrozenCollectionsTest.java index e50528b023..186086a424 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/FrozenCollectionsTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/FrozenCollectionsTest.java @@ -50,63 +50,92 @@ public class FrozenCollectionsTest extends CQLTester } @Test - public void testPartitionKeyUsage() throws Throwable + public void testPartitionKeyUsageSet() throws Throwable { - createTable("CREATE TABLE %s (k frozen> PRIMARY KEY, v int)"); + testPartitionKeyUsage("set", + set(), + set(1, 2, 3), + set(4, 5, 6), + set(7, 8, 9)); + } - execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(), 1); - execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(1, 2, 3), 1); - execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(4, 5, 6), 0); - execute("INSERT INTO %s (k, v) VALUES (?, ?)", set(7, 8, 9), 0); + @Test + public void testPartitionKeyUsageList() throws Throwable + { + testPartitionKeyUsage("list", + list(), + list(1, 2, 3), + list(4, 5, 6), + list(7, 8, 9)); + } + + @Test + public void testPartitionKeyUsageMap() throws Throwable + { + testPartitionKeyUsage("map", + map(), + map(1, 10, 2, 20, 3, 30), + map(4, 40, 5, 50, 6, 60), + map(7, 70, 8, 80, 9, 90)); + } + + private void testPartitionKeyUsage(String type, Object v1, Object v2, Object v3, Object v4) throws Throwable + { + createTable("CREATE TABLE %s (k frozen<" + type + "> PRIMARY KEY, v int)"); + + execute("INSERT INTO %s (k, v) VALUES (?, ?)", v1, 1); + execute("INSERT INTO %s (k, v) VALUES (?, ?)", v2, 1); + execute("INSERT INTO %s (k, v) VALUES (?, ?)", v3, 0); + execute("INSERT INTO %s (k, v) VALUES (?, ?)", v4, 0); // overwrite with an update - execute("UPDATE %s SET v=? WHERE k=?", 0, set()); - execute("UPDATE %s SET v=? WHERE k=?", 0, set(1, 2, 3)); + execute("UPDATE %s SET v=? WHERE k=?", 0, v1); + execute("UPDATE %s SET v=? WHERE k=?", 0, v2); assertRows(execute("SELECT * FROM %s"), - row(set(), 0), - row(set(1, 2, 3), 0), - row(set(4, 5, 6), 0), - row(set(7, 8, 9), 0) + row(v1, 0), + row(v2, 0), + row(v3, 0), + row(v4, 0) ); assertRows(execute("SELECT k FROM %s"), - row(set()), - row(set(1, 2, 3)), - row(set(4, 5, 6)), - row(set(7, 8, 9)) + row(v1), + row(v2), + row(v3), + row(v4) ); assertRows(execute("SELECT * FROM %s LIMIT 2"), - row(set(), 0), - row(set(1, 2, 3), 0) + row(v1, 0), + row(v2, 0) ); - assertRows(execute("SELECT * FROM %s WHERE k=?", set(4, 5, 6)), - row(set(4, 5, 6), 0) + assertRows(execute("SELECT * FROM %s WHERE k=?", v3), + row(v3, 0) ); - assertRows(execute("SELECT * FROM %s WHERE k=?", set()), - row(set(), 0) + assertRows(execute("SELECT * FROM %s WHERE k=?", v1), + row(v1, 0) ); - assertRows(execute("SELECT * FROM %s WHERE k IN ?", list(set(4, 5, 6), set())), - row(set(), 0), - row(set(4, 5, 6), 0) + assertRows(execute("SELECT * FROM %s WHERE k IN ?", list(v3, v1)), + row(v1, 0), + row(v3, 0) ); - assertRows(execute("SELECT * FROM %s WHERE token(k) >= token(?)", set(4, 5, 6)), - row(set(4, 5, 6), 0), - row(set(7, 8, 9), 0) + assertRows(execute("SELECT * FROM %s WHERE token(k) >= token(?)", v3), + row(v3, 0), + row(v4, 0) ); assertInvalid("INSERT INTO %s (k, v) VALUES (null, 0)"); - execute("DELETE FROM %s WHERE k=?", set()); - execute("DELETE FROM %s WHERE k=?", set(4, 5, 6)); + execute("DELETE FROM %s WHERE k=?", v1); + execute("DELETE FROM %s WHERE k=?", v3); assertRows(execute("SELECT * FROM %s"), - row(set(1, 2, 3), 0), - row(set(7, 8, 9), 0) + row(v2, 0), + row(v4, 0) ); } @@ -181,82 +210,223 @@ public class FrozenCollectionsTest extends CQLTester } @Test - public void testClusteringKeyUsage() throws Throwable + public void testClusteringKeyUsageSet() throws Throwable + { + testClusteringKeyUsage("set", + set(), + set(1, 2, 3), + set(4, 5, 6), + set(7, 8, 9)); + } + + @Test + public void testClusteringKeyUsageList() throws Throwable + { + testClusteringKeyUsage("list", + list(), + list(1, 2, 3), + list(4, 5, 6), + list(7, 8, 9)); + } + + @Test + public void testClusteringKeyUsageMap() throws Throwable + { + testClusteringKeyUsage("map", + map(), + map(1, 10, 2, 20, 3, 30), + map(4, 40, 5, 50, 6, 60), + map(7, 70, 8, 80, 9, 90)); + } + + private void testClusteringKeyUsage(String type, Object v1, Object v2, Object v3, Object v4) throws Throwable { for (String option : Arrays.asList("", " WITH COMPACT STORAGE")) { - createTable("CREATE TABLE %s (a int, b frozen>, c int, PRIMARY KEY (a, b))" + option); + createTable(String.format("CREATE TABLE %%s (a int, b frozen<%s>, c int, PRIMARY KEY (a, b)) %s", + type, option)); - execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(), 1); - execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(1, 2, 3), 1); - execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(4, 5, 6), 0); - execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, set(7, 8, 9), 0); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v1, 1); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v2, 1); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v3, 0); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v4, 0); // overwrite with an update - execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, set()); - execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, set(1, 2, 3)); + execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, v1); + execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, v2); assertRows(execute("SELECT * FROM %s"), - row(0, set(), 0), - row(0, set(1, 2, 3), 0), - row(0, set(4, 5, 6), 0), - row(0, set(7, 8, 9), 0) + row(0, v1, 0), + row(0, v2, 0), + row(0, v3, 0), + row(0, v4, 0) ); assertRows(execute("SELECT b FROM %s"), - row(set()), - row(set(1, 2, 3)), - row(set(4, 5, 6)), - row(set(7, 8, 9)) + row(v1), + row(v2), + row(v3), + row(v4) ); assertRows(execute("SELECT * FROM %s LIMIT 2"), - row(0, set(), 0), - row(0, set(1, 2, 3), 0) + row(0, v1, 0), + row(0, v2, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, set(4, 5, 6)), - row(0, set(4, 5, 6), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, v3), + row(0, v3, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, set()), - row(0, set(), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, v1), + row(0, v1, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b IN ?", 0, list(set(4, 5, 6), set())), - row(0, set(), 0), - row(0, set(4, 5, 6), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b IN ?", 0, list(v3, v1)), + row(0, v1, 0), + row(0, v3, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ?", 0, set(4, 5, 6)), - row(0, set(7, 8, 9), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ?", 0, v3), + row(0, v4, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b >= ?", 0, set(4, 5, 6)), - row(0, set(4, 5, 6), 0), - row(0, set(7, 8, 9), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b >= ?", 0, v3), + row(0, v3, 0), + row(0, v4, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b < ?", 0, set(4, 5, 6)), - row(0, set(), 0), - row(0, set(1, 2, 3), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b < ?", 0, v3), + row(0, v1, 0), + row(0, v2, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b <= ?", 0, set(4, 5, 6)), - row(0, set(), 0), - row(0, set(1, 2, 3), 0), - row(0, set(4, 5, 6), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b <= ?", 0, v3), + row(0, v1, 0), + row(0, v2, 0), + row(0, v3, 0) ); - assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ? AND b <= ?", 0, set(1, 2, 3), set(4, 5, 6)), - row(0, set(4, 5, 6), 0) + assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ? AND b <= ?", 0, v2, v3), + row(0, v3, 0) ); - execute("DELETE FROM %s WHERE a=? AND b=?", 0, set()); - execute("DELETE FROM %s WHERE a=? AND b=?", 0, set(4, 5, 6)); + execute("DELETE FROM %s WHERE a=? AND b=?", 0, v1); + execute("DELETE FROM %s WHERE a=? AND b=?", 0, v3); assertRows(execute("SELECT * FROM %s"), - row(0, set(1, 2, 3), 0), - row(0, set(7, 8, 9), 0) + row(0, v2, 0), + row(0, v4, 0) + ); + } + } + + @Test + public void testClusteringKeyUsageWithReverseOrderSet() throws Throwable + { + testClusteringKeyUsageWithReverseOrder("set", + set(), + set(1, 2, 3), + set(4, 5, 6), + set(7, 8, 9)); + } + + @Test + public void testClusteringKeyUsageWithReverseOrderList() throws Throwable + { + testClusteringKeyUsageWithReverseOrder("list", + list(), + list(1, 2, 3), + list(4, 5, 6), + list(7, 8, 9)); + } + + @Test + public void testClusteringKeyUsageWithReverseOrderMap() throws Throwable + { + testClusteringKeyUsageWithReverseOrder("map", + map(), + map(1, 10, 2, 20, 3, 30), + map(4, 40, 5, 50, 6, 60), + map(7, 70, 8, 80, 9, 90)); + } + + private void testClusteringKeyUsageWithReverseOrder(String type, Object v1, Object v2, Object v3, Object v4) throws Throwable + { + for (String option : Arrays.asList("", " AND COMPACT STORAGE")) + { + createTable(String.format("CREATE TABLE %%s (a int, b frozen<%s>, c int, PRIMARY KEY (a, b)) " + + "WITH CLUSTERING ORDER BY (b DESC) %s", type, option)); + + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v1, 1); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v2, 1); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v3, 0); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 0, v4, 0); + + // overwrite with an update + execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, v1); + execute("UPDATE %s SET c=? WHERE a=? AND b=?", 0, 0, v2); + + assertRows(execute("SELECT * FROM %s"), + row(0, v4, 0), + row(0, v3, 0), + row(0, v2, 0), + row(0, v1, 0) + ); + + assertRows(execute("SELECT b FROM %s"), + row(v4), + row(v3), + row(v2), + row(v1) + ); + + assertRows(execute("SELECT * FROM %s LIMIT 2"), + row(0, v4, 0), + row(0, v3, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, v3), + row(0, v3, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, v1), + row(0, v1, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b IN ?", 0, list(v3, v1)), + row(0, v3, 0), + row(0, v1, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ?", 0, v3), + row(0, v4, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b >= ?", 0, v3), + row(0, v4, 0), + row(0, v3, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b < ?", 0, v3), + row(0, v2, 0), + row(0, v1, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b <= ?", 0, v3), + row(0, v3, 0), + row(0, v2, 0), + row(0, v1, 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ? AND b <= ?", 0, v2, v3), + row(0, v3, 0) + ); + + execute("DELETE FROM %s WHERE a=? AND b=?", 0, v1); + execute("DELETE FROM %s WHERE a=? AND b=?", 0, v3); + assertRows(execute("SELECT * FROM %s"), + row(0, v4, 0), + row(0, v2, 0) ); } } @@ -375,6 +545,121 @@ public class FrozenCollectionsTest extends CQLTester } } + @Test + public void testNestedClusteringKeyUsageWithReverseOrder() throws Throwable + { + for (String option : Arrays.asList("", " AND COMPACT STORAGE")) + { + createTable("CREATE TABLE %s (a int, b frozen, list>>, c frozen>, d int, " + + "PRIMARY KEY (a, b, c)) WITH CLUSTERING ORDER BY (b DESC)" + option); + + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(), set(), 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(), list(1, 2, 3)), set(), 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0); + execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0); + + assertRows(execute("SELECT * FROM %s"), + row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(), list(1, 2, 3)), set(), 0), + row(0, map(), set(), 0) + ); + + assertRows(execute("SELECT b FROM %s"), + row(map(set(7, 8, 9), list(1, 2, 3))), + row(map(set(4, 5, 6), list(1, 2, 3))), + row(map(set(1, 2, 3), list(1, 2, 3))), + row(map(set(), list(1, 2, 3))), + row(map()) + ); + + assertRows(execute("SELECT c FROM %s"), + row(set(1, 2, 3)), + row(set(1, 2, 3)), + row(set(1, 2, 3)), + row(set()), + row(set()) + ); + + assertRows(execute("SELECT * FROM %s LIMIT 3"), + row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=0 ORDER BY b DESC LIMIT 4"), + row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(), list(1, 2, 3)), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, map()), + row(0, map(), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, map(set(), list(1, 2, 3))), + row(0, map(set(), list(1, 2, 3)), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=?", 0, map(set(1, 2, 3), list(1, 2, 3))), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(), list(1, 2, 3)), set()), + row(0, map(set(), list(1, 2, 3)), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND (b, c) IN ?", 0, list(tuple(map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3)), + tuple(map(), set()))), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ?", 0, map(set(4, 5, 6), list(1, 2, 3))), + row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b >= ?", 0, map(set(4, 5, 6), list(1, 2, 3))), + row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b < ?", 0, map(set(4, 5, 6), list(1, 2, 3))), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(), list(1, 2, 3)), set(), 0), + row(0, map(), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b <= ?", 0, map(set(4, 5, 6), list(1, 2, 3))), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(), list(1, 2, 3)), set(), 0), + row(0, map(), set(), 0) + ); + + assertRows(execute("SELECT * FROM %s WHERE a=? AND b > ? AND b <= ?", 0, map(set(1, 2, 3), list(1, 2, 3)), map(set(4, 5, 6), list(1, 2, 3))), + row(0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3), 0) + ); + + execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 0, map(), set()); + assertEmpty(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(), set())); + + execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(), list(1, 2, 3)), set()); + assertEmpty(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(), list(1, 2, 3)), set())); + + execute("DELETE FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3)); + assertEmpty(execute("SELECT * FROM %s WHERE a=? AND b=? AND c=?", 0, map(set(4, 5, 6), list(1, 2, 3)), set(1, 2, 3))); + + assertRows(execute("SELECT * FROM %s"), + row(0, map(set(7, 8, 9), list(1, 2, 3)), set(1, 2, 3), 0), + row(0, map(set(1, 2, 3), list(1, 2, 3)), set(1, 2, 3), 0) + ); + } + } + @Test public void testNormalColumnUsage() throws Throwable { @@ -1042,6 +1327,39 @@ public class FrozenCollectionsTest extends CQLTester ); } + /** + * Test parsing of literal lists when the column type is reversed (CASSANDRA-15814) + */ + @Test + public void testLiteralReversedList() throws Throwable + { + createTable("CREATE TABLE %s (k int, c frozen>, PRIMARY KEY (k, c)) WITH CLUSTERING ORDER BY (c DESC)"); + execute("INSERT INTO %s (k, c) VALUES (0, [1, 2])"); + assertRows(execute("SELECT c FROM %s WHERE k=0 AND c=[1, 2]"), row(list(1, 2))); + } + + /** + * Test parsing of literal sets when the column type is reversed (CASSANDRA-15814) + */ + @Test + public void testLiteralReversedSet() throws Throwable + { + createTable("CREATE TABLE %s (k int, c frozen>, PRIMARY KEY (k, c)) WITH CLUSTERING ORDER BY (c DESC)"); + execute("INSERT INTO %s (k, c) VALUES (0, {1, 2})"); + assertRows(execute("SELECT c FROM %s WHERE k=0 AND c={1, 2}"), row(set(1, 2))); + } + + /** + * Test parsing of literal maps when the column type is reversed (CASSANDRA-15814) + */ + @Test + public void testLiteralReversedMap() throws Throwable + { + createTable("CREATE TABLE %s (k int, c frozen>, PRIMARY KEY (k, c)) WITH CLUSTERING ORDER BY (c DESC)"); + execute("INSERT INTO %s (k, c) VALUES (0, {1:2, 3:4})"); + assertRows(execute("SELECT c FROM %s WHERE k=0 AND c={1:2, 3:4}"), row(map(1, 2, 3, 4))); + } + private static String clean(String classname) { return StringUtils.remove(classname, "org.apache.cassandra.db.marshal.");