You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2017/03/23 16:47:44 UTC

[2/3] cassandra git commit: Forbid SELECT restrictions and CREATE INDEX over non-frozen UDT columns

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/trunk
Commit: 82d3cdcd6cfeff043c92ea7a060498942130feb5
Parents: a85eeef
Author: Andr�s de la Pe�a <a....@gmail.com>
Authored: Thu Mar 23 17:40:04 2017 +0100
Committer: Benjamin Lerer <b....@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);
+    }
 }