Repository: cassandra
Updated Branches:
  refs/heads/trunk 7a7fb8e36 -> 9515fca76


Validate ALTER for involved views as well as to the base table

patch by carlyeks; reviewed by slebresne for CASSANDRA-10424


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/d76d3a5d
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/d76d3a5d
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/d76d3a5d

Branch: refs/heads/trunk
Commit: d76d3a5d59447a1584f49a2ca5a120d9580a4c1f
Parents: 4f4918f
Author: Carl Yeksigian <c...@apache.org>
Authored: Wed Sep 30 13:38:38 2015 -0400
Committer: Sylvain Lebresne <sylv...@datastax.com>
Committed: Thu Oct 8 12:06:48 2015 +0200

----------------------------------------------------------------------
 .../org/apache/cassandra/config/CFMetaData.java |  2 +-
 .../cql3/statements/AlterTableStatement.java    | 99 +++++++++++---------
 .../org/apache/cassandra/cql3/ViewTest.java     | 76 +++++++++++++++
 .../cql3/validation/operations/AlterTest.java   | 14 +++
 4 files changed, 148 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/d76d3a5d/src/java/org/apache/cassandra/config/CFMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java 
b/src/java/org/apache/cassandra/config/CFMetaData.java
index 00ca704..0387060 100644
--- a/src/java/org/apache/cassandra/config/CFMetaData.java
+++ b/src/java/org/apache/cassandra/config/CFMetaData.java
@@ -786,7 +786,7 @@ public final class CFMetaData
             throw new ConfigurationException("types do not match.");
 
         if (!cfm.comparator.isCompatibleWith(comparator))
-            throw new ConfigurationException(String.format("Column family 
comparators do not match or are not compatible (found %s; expected %s).", 
cfm.comparator.getClass().getSimpleName(), 
comparator.getClass().getSimpleName()));
+            throw new ConfigurationException(String.format("Column family 
comparators do not match or are not compatible (found %s; expected %s).", 
cfm.comparator.toString(), comparator.toString()));
     }
 
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d76d3a5d/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index c410f10..879f618 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -32,6 +32,7 @@ import org.apache.cassandra.db.Keyspace;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.CollectionType;
 import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.db.marshal.ReversedType;
 import org.apache.cassandra.db.view.View;
 import org.apache.cassandra.exceptions.*;
 import org.apache.cassandra.schema.IndexMetadata;
@@ -186,55 +187,25 @@ public class AlterTableStatement extends 
SchemaAlteringStatement
                 if (def == null)
                     throw new InvalidRequestException(String.format("Column %s 
was not found in table %s", columnName, columnFamily()));
 
-                AbstractType<?> validatorType = validator.getType();
-                switch (def.kind)
-                {
-                    case PARTITION_KEY:
-                        if (validatorType instanceof CounterColumnType)
-                            throw new 
InvalidRequestException(String.format("counter type is not supported for 
PRIMARY KEY part %s", columnName));
-
-                        AbstractType<?> currentType = 
cfm.getKeyValidatorAsClusteringComparator().subtype(def.position());
-                        if (!validatorType.isValueCompatibleWith(currentType))
-                            throw new 
ConfigurationException(String.format("Cannot change %s from type %s to type %s: 
types are incompatible.",
-                                                                           
columnName,
-                                                                           
currentType.asCQL3Type(),
-                                                                           
validator));
-                        break;
-                    case CLUSTERING:
-                        AbstractType<?> oldType = 
cfm.comparator.subtype(def.position());
-                        // Note that CFMetaData.validateCompatibility already 
validate the change we're about to do. However, the error message it
-                        // sends is a bit cryptic for a CQL3 user, so 
validating here for a sake of returning a better error message
-                        // Do note that we need isCompatibleWith here, not 
just isValueCompatibleWith.
-                        if (!validatorType.isCompatibleWith(oldType))
-                            throw new 
ConfigurationException(String.format("Cannot change %s from type %s to type %s: 
types are not order-compatible.",
-                                                                           
columnName,
-                                                                           
oldType.asCQL3Type(),
-                                                                           
validator));
-
-                        break;
-                    case REGULAR:
-                    case STATIC:
-                        // Thrift allows to change a column validator so 
CFMetaData.validateCompatibility will let it slide
-                        // if we change to an incompatible type (contrarily to 
the comparator case). But we don't want to
-                        // allow it for CQL3 (see #5882) so validating it 
explicitly here. We only care about value compatibility
-                        // though since we won't compare values (except when 
there is an index, but that is validated by
-                        // ColumnDefinition already).
-                        if (!validatorType.isValueCompatibleWith(def.type))
-                            throw new 
ConfigurationException(String.format("Cannot change %s from type %s to type %s: 
types are incompatible.",
-                                                                           
columnName,
-                                                                           
def.type.asCQL3Type(),
-                                                                           
validator));
-                        break;
-                }
+                AbstractType<?> validatorType = def.isReversedType() && 
!validator.getType().isReversed()
+                                                ? 
ReversedType.getInstance(validator.getType())
+                                                : validator.getType();
+                validateAlter(cfm, def, validatorType);
                 // In any case, we update the column definition
                 
cfm.addOrReplaceColumnDefinition(def.withNewType(validatorType));
 
-                // We have to alter the schema of the view table as well; it 
doesn't affect the definition however
+                // We also have to validate the view types here. If we have a 
view which includes a column as part of
+                // the clustering key, we need to make sure that it is indeed 
compatible.
                 for (ViewDefinition view : views)
                 {
                     if (!view.includes(columnName)) continue;
                     ViewDefinition viewCopy = view.copy();
-                    
viewCopy.metadata.addOrReplaceColumnDefinition(def.withNewType(validatorType));
+                    ColumnDefinition viewDef = 
view.metadata.getColumnDefinition(columnName);
+                    AbstractType viewType = viewDef.isReversedType() && 
!validator.getType().isReversed()
+                                            ? 
ReversedType.getInstance(validator.getType())
+                                            : validator.getType();
+                    validateAlter(view.metadata, viewDef, viewType);
+                    
viewCopy.metadata.addOrReplaceColumnDefinition(viewDef.withNewType(viewType));
 
                     if (viewUpdates == null)
                         viewUpdates = new ArrayList<>();
@@ -361,6 +332,50 @@ public class AlterTableStatement extends 
SchemaAlteringStatement
         return true;
     }
 
+    private static void validateAlter(CFMetaData cfm, ColumnDefinition def, 
AbstractType<?> validatorType)
+    {
+        switch (def.kind)
+        {
+            case PARTITION_KEY:
+                if (validatorType instanceof CounterColumnType)
+                    throw new InvalidRequestException(String.format("counter 
type is not supported for PRIMARY KEY part %s", def.name));
+
+                AbstractType<?> currentType = 
cfm.getKeyValidatorAsClusteringComparator().subtype(def.position());
+                if (!validatorType.isValueCompatibleWith(currentType))
+                    throw new ConfigurationException(String.format("Cannot 
change %s from type %s to type %s: types are incompatible.",
+                                                                   def.name,
+                                                                   
currentType.asCQL3Type(),
+                                                                   
validatorType.asCQL3Type()));
+                break;
+            case CLUSTERING:
+                AbstractType<?> oldType = 
cfm.comparator.subtype(def.position());
+                // Note that CFMetaData.validateCompatibility already validate 
the change we're about to do. However, the error message it
+                // sends is a bit cryptic for a CQL3 user, so validating here 
for a sake of returning a better error message
+                // Do note that we need isCompatibleWith here, not just 
isValueCompatibleWith.
+                if (!validatorType.isCompatibleWith(oldType))
+                {
+                    throw new ConfigurationException(String.format("Cannot 
change %s from type %s to type %s: types are not order-compatible.",
+                                                                   def.name,
+                                                                   
oldType.asCQL3Type(),
+                                                                   
validatorType.asCQL3Type()));
+                }
+                break;
+            case REGULAR:
+            case STATIC:
+                // Thrift allows to change a column validator so 
CFMetaData.validateCompatibility will let it slide
+                // if we change to an incompatible type (contrarily to the 
comparator case). But we don't want to
+                // allow it for CQL3 (see #5882) so validating it explicitly 
here. We only care about value compatibility
+                // though since we won't compare values (except when there is 
an index, but that is validated by
+                // ColumnDefinition already).
+                if (!validatorType.isValueCompatibleWith(def.type))
+                    throw new ConfigurationException(String.format("Cannot 
change %s from type %s to type %s: types are incompatible.",
+                                                                   def.name,
+                                                                   
def.type.asCQL3Type(),
+                                                                   
validatorType.asCQL3Type()));
+                break;
+        }
+    }
+
     public String toString()
     {
         return String.format("AlterTableStatement(name=%s, type=%s, column=%s, 
validator=%s)",

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d76d3a5d/test/unit/org/apache/cassandra/cql3/ViewTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/ViewTest.java 
b/test/unit/org/apache/cassandra/cql3/ViewTest.java
index 5d65115..55e7e1f 100644
--- a/test/unit/org/apache/cassandra/cql3/ViewTest.java
+++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java
@@ -1456,4 +1456,80 @@ public class ViewTest extends CQLTester
         ResultSet mvRows = executeNet(protocolVersion, "SELECT a, b FROM mv1");
         assertRowsNet(protocolVersion, mvRows, row(1, 1));
     }
+
+    @Test
+    public void testAlterTable() throws Throwable
+    {
+        createTable("CREATE TABLE %s (" +
+                    "a int," +
+                    "b text," +
+                    "PRIMARY KEY (a, b))");
+
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        createView("mv1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s 
WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (b, a)");
+
+        alterTable("ALTER TABLE %s ALTER b TYPE blob");
+    }
+
+    @Test
+    public void testAlterReversedTypeBaseTable() throws Throwable
+    {
+        createTable("CREATE TABLE %s (" +
+                    "a int," +
+                    "b text," +
+                    "PRIMARY KEY (a, b))" +
+                    "WITH CLUSTERING ORDER BY (b DESC)");
+
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        createView("mv1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s 
WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (a, b) WITH CLUSTERING ORDER 
BY (b ASC)");
+
+        alterTable("ALTER TABLE %s ALTER b TYPE blob");
+    }
+
+    @Test
+    public void testAlterReversedTypeViewTable() throws Throwable
+    {
+        createTable("CREATE TABLE %s (" +
+                    "a int," +
+                    "b text," +
+                    "PRIMARY KEY (a, b))");
+
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        createView("mv1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s 
WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (a, b) WITH CLUSTERING ORDER 
BY (b DESC)");
+
+        alterTable("ALTER TABLE %s ALTER b TYPE blob");
+    }
+
+    @Test
+    public void testAlterClusteringViewTable() throws Throwable
+    {
+        createTable("CREATE TABLE %s (" +
+                    "a int," +
+                    "b text," +
+                    "PRIMARY KEY (a))");
+
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        createView("mv1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s 
WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (a, b) WITH CLUSTERING ORDER 
BY (b DESC)");
+
+        alterTable("ALTER TABLE %s ALTER b TYPE blob");
+    }
+
+    @Test
+    public void testAlterViewTableValue() throws Throwable
+    {
+        createTable("CREATE TABLE %s (" +
+                    "a int," +
+                    "b int," +
+                    "PRIMARY KEY (a))");
+
+        executeNet(protocolVersion, "USE " + keyspace());
+
+        createView("mv1", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM %%s 
WHERE a IS NOT NULL AND b IS NOT NULL PRIMARY KEY (a, b) WITH CLUSTERING ORDER 
BY (b DESC)");
+
+        assertInvalid("ALTER TABLE %s ALTER b TYPE blob");
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/d76d3a5d/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index b7f814b..a56ccc9 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@ -273,6 +273,20 @@ public class AlterTest extends CQLTester
                                            "ALTER TABLE %s WITH compression = 
{ 'class' : 'SnappyCompressor', 'chunk_length_kb' : 32 , 'chunk_length_in_kb' : 
32 };");
     }
 
+    @Test
+    public void testAlterType() throws Throwable
+    {
+        createTable("CREATE TABLE %s (id text PRIMARY KEY, content text);");
+        alterTable("ALTER TABLE %s ALTER content TYPE blob");
+
+        createTable("CREATE TABLE %s (pk int, ck text, value blob, PRIMARY KEY 
(pk, ck)) WITH CLUSTERING ORDER BY (ck DESC)");
+        alterTable("ALTER TABLE %s ALTER ck TYPE blob");
+
+        createTable("CREATE TABLE %s (pk int, ck int, value blob, PRIMARY KEY 
(pk, ck))");
+        assertThrowsConfigurationException("Cannot change value from type blob 
to type text: types are incompatible.",
+                                           "ALTER TABLE %s ALTER value TYPE 
TEXT;");
+    }
+
     private void assertThrowsConfigurationException(String errorMsg, String 
alterStmt) throws Throwable
     {
         try

Reply via email to