diff --git a/CHANGES.txt b/CHANGES.txt index c6708f5b28..3f17d5c880 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ * Validate existence of DCs when repairing (CASSANDRA-17407) * dropping of a materialized view creates a snapshot with dropped- prefix (CASSANDRA-17415) Merged from 3.0: + * Disallow CONTAINS for UPDATE and DELETE (CASSANDRA-15266) * filter out NULL_VERSION entries from peers table in ConfiguredLimit (CASSANDRA-16518) * Suppress inapplicable CVEs (CASSANDRA-17368) * Fix flaky test - test_cqlsh_completion.TestCqlshCompletion (CASSANDRA-17338) diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java index 7636eccb50..b738f8c246 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -152,9 +152,15 @@ public final class StatementRestrictions * allow two IN for the same entity but that doesn't seem very useful) * - The value_alias cannot be restricted in any way (we don't support wide rows with indexed value * in CQL so far) + * - CONTAINS and CONTAINS_KEY cannot be used with UPDATE or DELETE */ for (Relation relation : whereClause.relations) { + if ((relation.isContains() || relation.isContainsKey()) && (type.isUpdate() || type.isDelete())) + { + throw invalidRequest("Cannot use %s with %s", type, relation.operator()); + } + if (relation.operator() == Operator.IS_NOT) { if (!forView) diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java index 66554dad84..b46cc75f66 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java @@ -162,6 +162,27 @@ public class DeleteTest extends CQLTester row("abc", 4, "xyz", "some other value")); } + @Test + public void testDeletionWithContainsAndContainsKey() throws Throwable + { + createTable("CREATE TABLE %s (a int, b frozen>, c int, primary key (a, b))"); + + Object[] row = row(1, map(1, 1, 2, 2), 3); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", row); + + assertRows(execute("SELECT * FROM %s"), row); + + assertInvalidMessage("Cannot use DELETE with CONTAINS", + "DELETE FROM %s WHERE a=1 AND b CONTAINS 1"); + + assertRows(execute("SELECT * FROM %s"), row); + + assertInvalidMessage("Cannot use DELETE with CONTAINS KEY", + "DELETE FROM %s WHERE a=1 AND b CONTAINS KEY 1"); + + assertRows(execute("SELECT * FROM %s"), row); + } + /** * Test deletion by 'composite prefix' (range tombstones) * migrated from cql_tests.py:TestCQL.range_tombstones_test() @@ -489,7 +510,7 @@ public class DeleteTest extends CQLTester assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", "DELETE FROM %s WHERE partitionKey > ? ", 0); - assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey", + assertInvalidMessage("Cannot use DELETE with CONTAINS", "DELETE FROM %s WHERE partitionKey CONTAINS ?", 0); // Non primary key in the where clause @@ -581,7 +602,7 @@ public class DeleteTest extends CQLTester assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", "DELETE FROM %s WHERE partitionKey > ? AND clustering = ?", 0, 1); - assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey", + assertInvalidMessage("Cannot use DELETE with CONTAINS", "DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering = ?", 0, 1); // Non primary key in the where clause @@ -711,7 +732,7 @@ public class DeleteTest extends CQLTester assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", "DELETE FROM %s WHERE partitionKey > ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1); - assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey", + assertInvalidMessage("Cannot use DELETE with CONTAINS", "DELETE FROM %s WHERE partitionKey CONTAINS ? AND clustering_1 = ? AND clustering_2 = ?", 0, 1, 1); // Non primary key in the where clause @@ -1124,17 +1145,17 @@ public class DeleteTest extends CQLTester assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value", "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 3, 3, 3); - assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values", + assertInvalidMessage("Cannot use DELETE with CONTAINS", "DELETE FROM %s WHERE partitionKey = ? AND clustering_1 = ? AND values CONTAINS ?", 3, 3, 3); assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value", "DELETE FROM %s WHERE partitionKey = ? AND value = ?", 3, 3); - assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values", + assertInvalidMessage("Cannot use DELETE with CONTAINS", "DELETE FROM %s WHERE partitionKey = ? AND values CONTAINS ?", 3, 3); assertInvalidMessage("Some partition key parts are missing: partitionkey", "DELETE FROM %s WHERE clustering_1 = ?", 3); assertInvalidMessage("Some partition key parts are missing: partitionkey", "DELETE FROM %s WHERE value = ?", 3); - assertInvalidMessage("Some partition key parts are missing: partitionkey", + assertInvalidMessage("Cannot use DELETE with CONTAINS", "DELETE FROM %s WHERE values CONTAINS ?", 3); } diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java index c9c8051914..340155f3f6 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java @@ -182,7 +182,7 @@ public class UpdateTest extends CQLTester assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", "UPDATE %s SET value = ? WHERE partitionKey > ? AND clustering_1 = ?", 7, 0, 1); - assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey", + assertInvalidMessage("Cannot use UPDATE with CONTAINS", "UPDATE %s SET value = ? WHERE partitionKey CONTAINS ? AND clustering_1 = ?", 7, 0, 1); assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value", @@ -193,6 +193,27 @@ public class UpdateTest extends CQLTester } } + @Test + public void testUpdateWithContainsAndContainsKey() throws Throwable + { + createTable("CREATE TABLE %s (a int, b frozen>, c int, primary key (a, b))"); + + Object[] row = row(1, map(1, 1, 2, 2), 3); + execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", row); + + assertRows(execute("SELECT * FROM %s"), row); + + assertInvalidMessage("Cannot use UPDATE with CONTAINS", + "UPDATE %s SET c=3 WHERE a=1 AND b CONTAINS 1"); + + assertRows(execute("SELECT * FROM %s"), row); + + assertInvalidMessage("Cannot use UPDATE with CONTAINS KEY", + "UPDATE %s SET c=3 WHERE a=1 AND b CONTAINS KEY 1"); + + assertRows(execute("SELECT * FROM %s"), row); + } + @Test public void testUpdateWithSecondaryIndices() throws Throwable { @@ -222,17 +243,17 @@ public class UpdateTest extends CQLTester assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value", "UPDATE %s SET values= {6} WHERE partitionKey = ? AND clustering_1 = ? AND value = ?", 3, 3, 3); - assertInvalidMessage("Non PRIMARY KEY columns found in where clause: values", + assertInvalidMessage("Cannot use UPDATE with CONTAINS", "UPDATE %s SET value= ? WHERE partitionKey = ? AND clustering_1 = ? AND values CONTAINS ?", 6, 3, 3, 3); assertInvalidMessage("Some clustering keys are missing: clustering_1", "UPDATE %s SET values= {6} WHERE partitionKey = ? AND value = ?", 3, 3); - assertInvalidMessage("Some clustering keys are missing: clustering_1", + assertInvalidMessage("Cannot use UPDATE with CONTAINS", "UPDATE %s SET value= ? WHERE partitionKey = ? AND values CONTAINS ?", 6, 3, 3); assertInvalidMessage("Some partition key parts are missing: partitionkey", "UPDATE %s SET values= {6} WHERE clustering_1 = ?", 3); assertInvalidMessage("Some partition key parts are missing: partitionkey", "UPDATE %s SET values= {6} WHERE value = ?", 3); - assertInvalidMessage("Some partition key parts are missing: partitionkey", + assertInvalidMessage("Cannot use UPDATE with CONTAINS", "UPDATE %s SET value= ? WHERE values CONTAINS ?", 6, 3); } @@ -369,7 +390,7 @@ public class UpdateTest extends CQLTester assertInvalidMessage("Only EQ and IN relation are supported on the partition key (unless you use the token() function)", "UPDATE %s SET value = ? WHERE partitionKey > ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1); - assertInvalidMessage("Cannot use CONTAINS on non-collection column partitionkey", + assertInvalidMessage("Cannot use UPDATE with CONTAINS", "UPDATE %s SET value = ? WHERE partitionKey CONTAINS ? AND clustering_1 = ? AND clustering_2 = ?", 7, 0, 1, 1); assertInvalidMessage("Non PRIMARY KEY columns found in where clause: value",