Repository: cassandra Updated Branches: refs/heads/cassandra-3.11 a85eeefe8 -> 82d3cdcd6 refs/heads/trunk 9330409ac -> 18c6ed25e
Forbid SELECT restrictions and CREATE INDEX over non-frozen UDT columns patch by Andrés de la Peña; reviewed by Benjamin Lerer for CASSANDRA-13247 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/82d3cdcd Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/82d3cdcd Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/82d3cdcd Branch: refs/heads/cassandra-3.11 Commit: 82d3cdcd6cfeff043c92ea7a060498942130feb5 Parents: a85eeef Author: Andrés de la Peña <a.penya.gar...@gmail.com> Authored: Thu Mar 23 17:40:04 2017 +0100 Committer: Benjamin Lerer <b.le...@gmail.com> Committed: Thu Mar 23 17:40:04 2017 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../cassandra/cql3/SingleColumnRelation.java | 6 ++ .../cql3/statements/CreateIndexStatement.java | 2 + .../validation/entities/SecondaryIndexTest.java | 103 +++++++++++++++++++ .../SelectSingleColumnRelationTest.java | 24 +++++ 5 files changed, 136 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/82d3cdcd/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 728e3e7..6644796 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.11.0 + * 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) http://git-wip-us.apache.org/repos/asf/cassandra/blob/82d3cdcd/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java index ae07f56..e0ee519 100644 --- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java +++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java @@ -273,6 +273,12 @@ public final class SingleColumnRelation extends Relation checkTrue(isEQ(), "Only EQ relations are supported on map entries"); } + // Non-frozen UDTs don't support any operator + checkFalse(receiver.type.isUDT() && receiver.type.isMultiCell(), + "Non-frozen UDT column '%s' (%s) cannot be restricted by any relation", + receiver.name, + receiver.type.asCQL3Type()); + if (receiver.type.isCollection()) { // We don't support relations against entire collections (unless they're frozen), like "numbers = {1, 2, 3}" http://git-wip-us.apache.org/repos/asf/cassandra/blob/82d3cdcd/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java index ed4658f..204edf4 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java @@ -134,6 +134,8 @@ public class CreateIndexStatement extends SchemaAlteringStatement validateIsSimpleIndexIfTargetColumnNotCollection(cd, target); validateTargetColumnIsMapIfIndexInvolvesKeys(isMap, target); } + + checkFalse(cd.type.isUDT() && cd.type.isMultiCell(), "Secondary indexes are not supported on non-frozen UDTs"); } if (!Strings.isNullOrEmpty(indexName)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/82d3cdcd/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java index 88c6f17..013e41d 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java @@ -1409,6 +1409,109 @@ public class SecondaryIndexTest extends CQLTester "CREATE INDEX ON %s (t)"); } + @Test + public void testIndexOnFrozenUDT() throws Throwable + { + String type = createType("CREATE TYPE %s (a int)"); + String tableName = createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<" + type + ">)"); + + Object udt1 = userType("a", 1); + Object udt2 = userType("a", 2); + + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, udt1); + execute("CREATE INDEX idx ON %s (v)"); + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 1, udt2); + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 1, udt1); + assertTrue(waitForIndex(keyspace(), tableName, "idx")); + + assertRows(execute("SELECT * FROM %s WHERE v = ?", udt1), row(1, udt1), row(0, udt1)); + assertEmpty(execute("SELECT * FROM %s WHERE v = ?", udt2)); + + execute("DELETE FROM %s WHERE k = 0"); + assertRows(execute("SELECT * FROM %s WHERE v = ?", udt1), row(1, udt1)); + + dropIndex("DROP INDEX %s.idx"); + assertInvalidMessage("Index 'idx' could not be found", "DROP INDEX " + KEYSPACE + ".idx"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE v = ?", udt1); + } + + @Test + public void testIndexOnFrozenCollectionOfUDT() throws Throwable + { + String type = createType("CREATE TYPE %s (a int)"); + String tableName = createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<set<frozen<" + type + ">>>)"); + + Object udt1 = userType("a", 1); + Object udt2 = userType("a", 2); + + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 1, set(udt1, udt2)); + assertInvalidMessage("Frozen collections only support full()", "CREATE INDEX idx ON %s (keys(v))"); + assertInvalidMessage("Frozen collections only support full()", "CREATE INDEX idx ON %s (values(v))"); + execute("CREATE INDEX idx ON %s (full(v))"); + + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 2, set(udt2)); + assertTrue(waitForIndex(keyspace(), tableName, "idx")); + + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE v CONTAINS ?", udt1); + + assertRows(execute("SELECT * FROM %s WHERE v = ?", set(udt1, udt2)), row(1, set(udt1, udt2))); + assertRows(execute("SELECT * FROM %s WHERE v = ?", set(udt2)), row(2, set(udt2))); + + execute("DELETE FROM %s WHERE k = 2"); + assertEmpty(execute("SELECT * FROM %s WHERE v = ?", set(udt2))); + + dropIndex("DROP INDEX %s.idx"); + assertInvalidMessage("Index 'idx' could not be found", "DROP INDEX " + KEYSPACE + ".idx"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE v CONTAINS ?", udt1); + } + + @Test + public void testIndexOnNonFrozenCollectionOfFrozenUDT() throws Throwable + { + String type = createType("CREATE TYPE %s (a int)"); + String tableName = createTable("CREATE TABLE %s (k int PRIMARY KEY, v set<frozen<" + type + ">>)"); + + Object udt1 = userType("a", 1); + Object udt2 = userType("a", 2); + + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 1, set(udt1)); + assertInvalidMessage("Cannot create index on keys of column v with non-map type", + "CREATE INDEX idx ON %s (keys(v))"); + assertInvalidMessage("full() indexes can only be created on frozen collections", + "CREATE INDEX idx ON %s (full(v))"); + execute("CREATE INDEX idx ON %s (values(v))"); + + execute("INSERT INTO %s (k, v) VALUES (?, ?)", 2, set(udt2)); + execute("UPDATE %s SET v = v + ? WHERE k = ?", set(udt2), 1); + assertTrue(waitForIndex(keyspace(), tableName, "idx")); + + assertRows(execute("SELECT * FROM %s WHERE v CONTAINS ?", udt1), row(1, set(udt1, udt2))); + assertRows(execute("SELECT * FROM %s WHERE v CONTAINS ?", udt2), row(1, set(udt1, udt2)), row(2, set(udt2))); + + execute("DELETE FROM %s WHERE k = 1"); + assertEmpty(execute("SELECT * FROM %s WHERE v CONTAINS ?", udt1)); + assertRows(execute("SELECT * FROM %s WHERE v CONTAINS ?", udt2), row(2, set(udt2))); + + dropIndex("DROP INDEX %s.idx"); + assertInvalidMessage("Index 'idx' could not be found", "DROP INDEX " + KEYSPACE + ".idx"); + assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, + "SELECT * FROM %s WHERE v CONTAINS ?", udt1); + } + + @Test + public void testIndexOnNonFrozenUDT() throws Throwable + { + String type = createType("CREATE TYPE %s (a int)"); + createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + type + ")"); + assertInvalidMessage("Secondary indexes are not supported on non-frozen UDTs", "CREATE INDEX ON %s (v)"); + assertInvalidMessage("Non-collection columns support only simple indexes", "CREATE INDEX ON %s (keys(v))"); + assertInvalidMessage("Non-collection columns support only simple indexes", "CREATE INDEX ON %s (values(v))"); + assertInvalidMessage("full() indexes can only be created on frozen collections", "CREATE INDEX ON %s (full(v))"); + } + private ResultMessage.Prepared prepareStatement(String cql, boolean forThrift) { return QueryProcessor.prepare(String.format(cql, KEYSPACE, currentTable()), http://git-wip-us.apache.org/repos/asf/cassandra/blob/82d3cdcd/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java index 2ad0427..7e5afda 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java @@ -638,4 +638,28 @@ public class SelectSingleColumnRelationTest extends CQLTester assertInvalidMessage("Undefined column name d", "SELECT c AS d FROM %s WHERE d CONTAINS KEY 0"); assertInvalidMessage("Undefined column name d", "SELECT d FROM %s WHERE a = 0"); } + + @Test + public void testInvalidNonFrozenUDTRelation() throws Throwable + { + String type = createType("CREATE TYPE %s (a int)"); + createTable("CREATE TABLE %s (a int PRIMARY KEY, b " + type + ")"); + Object udt = userType("a", 1); + + // All operators + String msg = "Non-frozen UDT column 'b' (" + type + ") cannot be restricted by any relation"; + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b = ?", udt); + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b > ?", udt); + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b < ?", udt); + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b >= ?", udt); + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b <= ?", udt); + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b IN (?)", udt); + assertInvalidMessage(msg, "SELECT * FROM %s WHERE b LIKE ?", udt); + assertInvalidMessage("Unsupported \"!=\" relation: b != {a: 0}", + "SELECT * FROM %s WHERE b != {a: 0}", udt); + assertInvalidMessage("Unsupported restriction: b IS NOT NULL", + "SELECT * FROM %s WHERE b IS NOT NULL", udt); + assertInvalidMessage("Cannot use CONTAINS on non-collection column b", + "SELECT * FROM %s WHERE b CONTAINS ?", udt); + } }