Add AlterKeyspace statement to 1.1

patch by slebresne; reviewed by xedin for CASSANDRA-4611


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

Branch: refs/heads/trunk
Commit: 2ce7b9b3ada14182067c934c475c6ab899086355
Parents: 59a6a5d
Author: Sylvain Lebresne <[email protected]>
Authored: Thu Oct 4 11:19:21 2012 +0200
Committer: Sylvain Lebresne <[email protected]>
Committed: Thu Oct 4 11:19:21 2012 +0200

----------------------------------------------------------------------
 CHANGES.txt                                        |    1 +
 .../org/apache/cassandra/config/KSMetaData.java    |    8 +-
 .../org/apache/cassandra/cql/QueryProcessor.java   |    3 +-
 src/java/org/apache/cassandra/cql3/CFPropDefs.java |  123 +++------------
 src/java/org/apache/cassandra/cql3/Cql.g           |   35 +++--
 src/java/org/apache/cassandra/cql3/KSPropDefs.java |   89 +++++++++++
 .../apache/cassandra/cql3/PropertyDefinitions.java |  122 ++++++++++++++
 .../cql3/statements/AlterKeyspaceStatement.java    |   95 +++++++++++
 .../cql3/statements/AlterTableStatement.java       |   10 +-
 .../statements/CreateColumnFamilyStatement.java    |   23 ++-
 .../cql3/statements/CreateKeyspaceStatement.java   |   31 ++---
 11 files changed, 387 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index d803c0f..cb725cc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -16,6 +16,7 @@
  * Fix CQL3 'is reversed' logic (CASSANDRA-4716)
  * (CQL3) Don't return ReversedType in result set metadata (CASSANDRA-4717)
  * Pluggable Thrift transport factories for CLI (CASSANDRA-4609)
+ * Backport adding AlterKeyspace statement (CASSANDRA-4611)
 Merged from 1.0:
  * Switch from NBHM to CHM in MessagingService's callback map, which
    prevents OOM in long-running instances (CASSANDRA-4708)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/config/KSMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/KSMetaData.java 
b/src/java/org/apache/cassandra/config/KSMetaData.java
index 7b27875..a5fccc3 100644
--- a/src/java/org/apache/cassandra/config/KSMetaData.java
+++ b/src/java/org/apache/cassandra/config/KSMetaData.java
@@ -60,18 +60,18 @@ public final class KSMetaData
     }
 
     // For new user created keyspaces (through CQL)
-    public static KSMetaData newKeyspace(String name, String strategyName, 
Map<String, String> options) throws ConfigurationException
+    public static KSMetaData newKeyspace(String name, String strategyName, 
Map<String, String> options, boolean durableWrites) throws 
ConfigurationException
     {
         Class<? extends AbstractReplicationStrategy> cls = 
AbstractReplicationStrategy.getClass(strategyName);
         if (cls.equals(LocalStrategy.class))
             throw new ConfigurationException("Unable to use given strategy 
class: LocalStrategy is reserved for internal use.");
 
-        return newKeyspace(name, cls, options, 
Collections.<CFMetaData>emptyList());
+        return newKeyspace(name, cls, options, durableWrites, 
Collections.<CFMetaData>emptyList());
     }
 
-    public static KSMetaData newKeyspace(String name, Class<? extends 
AbstractReplicationStrategy> strategyClass, Map<String, String> options, 
Iterable<CFMetaData> cfDefs)
+    public static KSMetaData newKeyspace(String name, Class<? extends 
AbstractReplicationStrategy> strategyClass, Map<String, String> options, 
boolean durablesWrites, Iterable<CFMetaData> cfDefs)
     {
-        return new KSMetaData(name, strategyClass, options, true, cfDefs);
+        return new KSMetaData(name, strategyClass, options, durablesWrites, 
cfDefs);
     }
 
     public static KSMetaData cloneWith(KSMetaData ksm, Iterable<CFMetaData> 
cfDefs)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql/QueryProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/QueryProcessor.java 
b/src/java/org/apache/cassandra/cql/QueryProcessor.java
index 4a829c6..ab1c15b 100644
--- a/src/java/org/apache/cassandra/cql/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql/QueryProcessor.java
@@ -700,7 +700,8 @@ public class QueryProcessor
                 {
                     KSMetaData ksm = KSMetaData.newKeyspace(create.getName(),
                                                             
create.getStrategyClass(),
-                                                            
create.getStrategyOptions());
+                                                            
create.getStrategyOptions(),
+                                                            true);
                     ThriftValidation.validateKeyspaceNotYetExisting(ksm.name);
                     MigrationManager.announceNewKeyspace(ksm);
                     validateSchemaIsSettled();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/CFPropDefs.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CFPropDefs.java 
b/src/java/org/apache/cassandra/cql3/CFPropDefs.java
index 01e14fb..ab209dd 100644
--- a/src/java/org/apache/cassandra/cql3/CFPropDefs.java
+++ b/src/java/org/apache/cassandra/cql3/CFPropDefs.java
@@ -18,7 +18,16 @@
  */
 package org.apache.cassandra.cql3;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
 import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.db.compaction.AbstractCompactionStrategy;
@@ -29,12 +38,7 @@ import org.apache.cassandra.thrift.InvalidRequestException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class CFPropDefs
+public class CFPropDefs extends PropertyDefinitions
 {
     private static Logger logger = LoggerFactory.getLogger(CFPropDefs.class);
 
@@ -89,7 +93,6 @@ public class CFPropDefs
         allowedKeywords.addAll(obsoleteKeywords);
     }
 
-    public final Map<String, String> properties = new HashMap<String, 
String>();
     private Class<? extends AbstractCompactionStrategy> 
compactionStrategyClass = null;
     public final Map<String, String> compactionStrategyOptions = new 
HashMap<String, String>();
     public final Map<String, String> compressionParameters = new 
HashMap<String, String>()
@@ -115,18 +118,9 @@ public class CFPropDefs
         }
     }
 
-    /* If not comparator/validator is not specified, default to text 
(BytesType is the wrong default for CQL
-     * since it uses hex terms).  If the value specified is not found in the 
comparators map, assume the user
-     * knows what they are doing (a custom comparator/validator for example), 
and pass it on as-is.
-     */
-
-    public void validate() throws ConfigurationException
+    public void validate() throws ConfigurationException, 
InvalidRequestException
     {
-        // Catch the case where someone passed a kwarg that is not recognized.
-        for (String bogus : Sets.difference(properties.keySet(), 
allowedKeywords))
-            throw new ConfigurationException(bogus + " is not a valid keyword 
argument for CREATE TABLE");
-        for (String obsolete : Sets.intersection(properties.keySet(), 
obsoleteKeywords))
-            logger.warn("Ignoring obsolete property {}", obsolete);
+        validate(keywords, obsoleteKeywords);
 
         if (properties.containsKey(KW_COMPACTION_STRATEGY_CLASS))
         {
@@ -135,8 +129,8 @@ public class CFPropDefs
         }
     }
 
-    /** Map a keyword to the corresponding value */
-    public void addProperty(String name, String value)
+    @Override
+    public void addProperty(String name, String value) throws 
InvalidRequestException
     {
         String[] composite = name.split(":");
         if (composite.length > 1)
@@ -152,24 +146,13 @@ public class CFPropDefs
                 return;
             }
         }
-        properties.put(name, value);
-    }
-
-    public void addAll(Map<String, String> propertyMap)
-    {
-        for (Map.Entry<String, String> entry : propertyMap.entrySet())
-            addProperty(entry.getKey(), entry.getValue());
-    }
-
-    public Boolean hasProperty(String name)
-    {
-        return properties.containsKey(name);
+        super.addProperty(name, value);
     }
 
-    public void applyToCFMetadata(CFMetaData cfm) throws ConfigurationException
+    public void applyToCFMetadata(CFMetaData cfm) throws 
ConfigurationException, InvalidRequestException
     {
         if (hasProperty(KW_COMMENT))
-            cfm.comment(get(KW_COMMENT));
+            cfm.comment(getString(KW_COMMENT, ""));
 
         cfm.readRepairChance(getDouble(KW_READREPAIRCHANCE, 
cfm.getReadRepairChance()));
         cfm.dcLocalReadRepairChance(getDouble(KW_DCLOCALREADREPAIRCHANCE, 
cfm.getDcLocalReadRepair()));
@@ -181,80 +164,12 @@ public class CFPropDefs
         cfm.bloomFilterFpChance(getDouble(KW_BF_FP_CHANCE, 
cfm.getBloomFilterFpChance()));
 
         if (compactionStrategyClass != null)
+        {
             cfm.compactionStrategyClass(compactionStrategyClass);
-
-        if (!compactionStrategyOptions.isEmpty())
             cfm.compactionStrategyOptions(new HashMap<String, 
String>(compactionStrategyOptions));
-
-        if (!compressionParameters.isEmpty())
-            
cfm.compressionParameters(CompressionParameters.create(compressionParameters));
-    }
-
-    public String get(String name)
-    {
-        return properties.get(name);
-    }
-
-    public String getString(String key, String defaultValue)
-    {
-        String value = properties.get(key);
-        return value != null ? value : defaultValue;
-    }
-
-    // Return a property value, typed as a Boolean
-    public Boolean getBoolean(String key, Boolean defaultValue)
-    {
-        String value = properties.get(key);
-        return (value == null) ? defaultValue : 
value.toLowerCase().matches("(1|true|yes)");
-    }
-
-    // Return a property value, typed as a Double
-    public Double getDouble(String key, Double defaultValue) throws 
ConfigurationException
-    {
-        Double result;
-        String value = properties.get(key);
-
-        if (value == null)
-            result = defaultValue;
-        else
-        {
-            try
-            {
-                result = Double.parseDouble(value);
-            }
-            catch (NumberFormatException e)
-            {
-                throw new ConfigurationException(String.format("%s not valid 
for \"%s\"", value, key));
-            }
         }
-        return result;
-    }
-
-    // Return a property value, typed as an Integer
-    public Integer getInt(String key, Integer defaultValue) throws 
ConfigurationException
-    {
-        String value = properties.get(key);
-        return toInt(key, value, defaultValue);
-    }
-
-    public static Integer toInt(String key, String value, Integer 
defaultValue) throws ConfigurationException
-    {
-        Integer result;
 
-        if (value == null)
-            result = defaultValue;
-        else
-        {
-            try
-            {
-                result = Integer.parseInt(value);
-            }
-            catch (NumberFormatException e)
-            {
-                throw new ConfigurationException(String.format("%s not valid 
for \"%s\"", value, key));
-            }
-        }
-        return result;
+        
cfm.compressionParameters(CompressionParameters.create(compressionParameters));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g 
b/src/java/org/apache/cassandra/cql3/Cql.g
index f0481f6..387e694 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -144,6 +144,7 @@ cqlStatement returns [ParsedStatement stmt]
     | st15=grantStatement              { $stmt = st15; }
     | st16=revokeStatement             { $stmt = st16; }
     | st17=listGrantsStatement         { $stmt = st17; }
+    | st18=alterKeyspaceStatement      { $stmt = st18; }
     ;
 
 /*
@@ -341,8 +342,9 @@ batchStatementObjective returns [ModificationStatement 
statement]
  * CREATE KEYSPACE <KEYSPACE> WITH attr1 = value1 AND attr2 = value2;
  */
 createKeyspaceStatement returns [CreateKeyspaceStatement expr]
+    @init { KSPropDefs attrs = new KSPropDefs(); }
     : K_CREATE K_KEYSPACE ks=keyspaceName
-      K_WITH props=properties { $expr = new CreateKeyspaceStatement(ks, 
props); }
+      K_WITH properties[attrs] { $expr = new CreateKeyspaceStatement(ks, 
attrs); }
     ;
 
 /**
@@ -368,7 +370,7 @@ cfamColumns[CreateColumnFamilyStatement.RawStatement expr]
     ;
 
 cfamProperty[CreateColumnFamilyStatement.RawStatement expr]
-    : k=property '=' v=propertyValue { $expr.addProperty(k, v); }
+    : k=propertyKey '=' v=propertyValue { try { $expr.addProperty(k, v); } 
catch (InvalidRequestException e) { addRecognitionError(e.getMessage()); } }
     | K_COMPACT K_STORAGE { $expr.setCompactStorage(); }
     | K_CLUSTERING K_ORDER K_BY '(' cfamOrdering[expr] (',' 
cfamOrdering[expr])* ')'
     ;
@@ -387,6 +389,16 @@ createIndexStatement returns [CreateIndexStatement expr]
     ;
 
 /**
+ * ALTER KEYSPACE <KS> WITH <property> = <value>;
+ */
+alterKeyspaceStatement returns [AlterKeyspaceStatement expr]
+    @init { KSPropDefs attrs = new KSPropDefs(); }
+    : K_ALTER K_KEYSPACE ks=keyspaceName
+        K_WITH properties[attrs] { $expr = new AlterKeyspaceStatement(ks, 
attrs); }
+    ;
+
+
+/**
  * ALTER COLUMN FAMILY <CF> ALTER <column> TYPE <newtype>;
  * ALTER COLUMN FAMILY <CF> ADD <column> <newtype>;
  * ALTER COLUMN FAMILY <CF> DROP <column>;
@@ -395,13 +407,13 @@ createIndexStatement returns [CreateIndexStatement expr]
 alterTableStatement returns [AlterTableStatement expr]
     @init {
         AlterTableStatement.Type type = null;
-        props = new HashMap<String, String>();
+        CFPropDefs props = new CFPropDefs();
     }
     : K_ALTER K_COLUMNFAMILY cf=columnFamilyName
           ( K_ALTER id=cident K_TYPE v=comparatorType { type = 
AlterTableStatement.Type.ALTER; }
           | K_ADD   id=cident v=comparatorType        { type = 
AlterTableStatement.Type.ADD; }
           | K_DROP  id=cident                         { type = 
AlterTableStatement.Type.DROP; }
-          | K_WITH  props=properties                  { type = 
AlterTableStatement.Type.OPTS; }
+          | K_WITH  properties[props]                 { type = 
AlterTableStatement.Type.OPTS; }
           )
     {
         $expr = new AlterTableStatement(cf, type, id, v, props);
@@ -545,7 +557,15 @@ termPairWithOperation[Map<ColumnIdentifier, Operation> 
columns]
         )
     ;
 
-property returns [String str]
+properties[PropertyDefinitions props]
+    : property[props] (K_AND property[props])*
+    ;
+
+property[PropertyDefinitions props]
+    : k=propertyKey '=' simple=propertyValue { try { $props.addProperty(k, 
simple); } catch (InvalidRequestException e) { 
addRecognitionError(e.getMessage()); } }
+    ;
+
+propertyKey returns [String str]
     @init{ StringBuilder sb = new StringBuilder(); }
     : c1=cident { sb.append(c1); } ( ':' cn=cident { 
sb.append(':').append(cn); } )* { $str = sb.toString(); }
     ;
@@ -555,11 +575,6 @@ propertyValue returns [String str]
     | u=unreserved_keyword                         { $str = u; }
     ;
 
-properties returns [Map<String, String> props]
-    @init{ $props = new HashMap<String, String>(); }
-    : k1=property '=' v1=propertyValue { $props.put(k1, v1); } (K_AND 
kn=property '=' vn=propertyValue { $props.put(kn, vn); } )*
-    ;
-
 relation returns [Relation rel]
     : name=cident type=('=' | '<' | '<=' | '>=' | '>') t=term { $rel = new 
Relation($name.id, $type.text, $t.term); }
     | K_TOKEN '(' name=cident ')' type=('=' |'<' | '<=' | '>=' | '>') 
t=extendedTerm { $rel = new Relation($name.id, $type.text, $t.term, true); }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/KSPropDefs.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/KSPropDefs.java 
b/src/java/org/apache/cassandra/cql3/KSPropDefs.java
new file mode 100644
index 0000000..d480df4
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/KSPropDefs.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3;
+
+import java.util.*;
+
+import org.apache.cassandra.config.KSMetaData;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+public class KSPropDefs extends PropertyDefinitions
+{
+    public static final String KW_DURABLE_WRITES = "durable_writes";
+
+    public static final String KW_REPLICATION_STRATEGY = "strategy_class";
+
+    public static final Set<String> keywords = new HashSet<String>();
+    public static final Set<String> obsoleteKeywords = new HashSet<String>();
+
+    static
+    {
+        keywords.add(KW_DURABLE_WRITES);
+        keywords.add(KW_REPLICATION_STRATEGY);
+    }
+
+    private String strategyClass;
+    private final Map<String, String> strategyOptions = new HashMap<String, 
String>();
+
+    public void validate() throws ConfigurationException, 
InvalidRequestException
+    {
+        validate(keywords, obsoleteKeywords);
+
+        if (!properties.containsKey("strategy_class"))
+            throw new InvalidRequestException("missing required argument 
\"strategy_class\"");
+        strategyClass = properties.get("strategy_class");
+    }
+
+    @Override
+    public void addProperty(String name, String value) throws 
InvalidRequestException
+    {
+        // optional
+        if (name.contains(":") && name.startsWith("strategy_options"))
+            strategyOptions.put(name.split(":")[1], value);
+        else
+            super.addProperty(name, value);
+    }
+
+    public String getReplicationStrategyClass()
+    {
+        return strategyClass;
+    }
+
+    public Map<String, String> getReplicationOptions()
+    {
+        return strategyOptions;
+    }
+
+    public KSMetaData asKSMetadata(String ksName) throws 
InvalidRequestException, ConfigurationException
+    {
+        return KSMetaData.newKeyspace(ksName, getReplicationStrategyClass(), 
getReplicationOptions(), getBoolean(KW_DURABLE_WRITES, true));
+    }
+
+    public KSMetaData asKSMetadataUpdate(KSMetaData old) throws 
InvalidRequestException, ConfigurationException
+    {
+        String sClass = strategyClass;
+        Map<String, String> sOptions = getReplicationOptions();
+        if (sClass == null)
+        {
+            sClass = old.strategyClass.getName();
+            sOptions = old.strategyOptions;
+        }
+        return KSMetaData.newKeyspace(old.name, sClass, sOptions, 
getBoolean(KW_DURABLE_WRITES, old.durableWrites));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/PropertyDefinitions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/PropertyDefinitions.java 
b/src/java/org/apache/cassandra/cql3/PropertyDefinitions.java
new file mode 100644
index 0000000..75b8ba5
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/PropertyDefinitions.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3;
+
+import java.util.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+public class PropertyDefinitions
+{
+    protected static final Logger logger = 
LoggerFactory.getLogger(PropertyDefinitions.class);
+
+    protected final Map<String, String> properties = new HashMap<String, 
String>();
+
+    public void addProperty(String name, String value) throws 
InvalidRequestException
+    {
+        if (properties.put(name, value) != null)
+            throw new InvalidRequestException(String.format("Multiple 
definition for property '%s'", name));
+    }
+
+    public void validate(Set<String> keywords, Set<String> obsolete) throws 
InvalidRequestException
+    {
+        for (String name : properties.keySet())
+        {
+            if (keywords.contains(name))
+                continue;
+
+            if (obsolete.contains(name))
+                logger.warn("Ignoring obsolete property {}", name);
+            else
+                throw new InvalidRequestException(String.format("Unknown 
property '%s'", name));
+        }
+    }
+
+    protected String getSimple(String name) throws InvalidRequestException
+    {
+        return properties.get(name);
+    }
+
+    public Boolean hasProperty(String name)
+    {
+        return properties.containsKey(name);
+    }
+
+    public String getString(String key, String defaultValue) throws 
InvalidRequestException
+    {
+        String value = getSimple(key);
+        return value != null ? value : defaultValue;
+    }
+
+    // Return a property value, typed as a Boolean
+    public Boolean getBoolean(String key, Boolean defaultValue) throws 
InvalidRequestException
+    {
+        String value = getSimple(key);
+        return (value == null) ? defaultValue : 
value.toLowerCase().matches("(1|true|yes)");
+    }
+
+    // Return a property value, typed as a Double
+    public Double getDouble(String key, Double defaultValue) throws 
InvalidRequestException
+    {
+        String value = getSimple(key);
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        else
+        {
+            try
+            {
+                return Double.valueOf(value);
+            }
+            catch (NumberFormatException e)
+            {
+                throw new InvalidRequestException(String.format("Invalid 
double value %s for '%s'", value, key));
+            }
+        }
+    }
+
+    // Return a property value, typed as an Integer
+    public Integer getInt(String key, Integer defaultValue) throws 
InvalidRequestException
+    {
+        String value = getSimple(key);
+        return toInt(key, value, defaultValue);
+    }
+
+    public static Integer toInt(String key, String value, Integer 
defaultValue) throws InvalidRequestException
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        else
+        {
+            try
+            {
+                return Integer.valueOf(value);
+            }
+            catch (NumberFormatException e)
+            {
+                throw new InvalidRequestException(String.format("Invalid 
integer value %s for '%s'", value, key));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
new file mode 100644
index 0000000..644fd24
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cassandra.cql3.statements;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.KSMetaData;
+import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.KSPropDefs;
+import org.apache.cassandra.db.Table;
+import org.apache.cassandra.locator.AbstractReplicationStrategy;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.MigrationManager;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.thrift.InvalidRequestException;
+import org.apache.cassandra.thrift.SchemaDisagreementException;
+
+public class AlterKeyspaceStatement extends SchemaAlteringStatement
+{
+    private final String name;
+    private final KSPropDefs attrs;
+
+    public AlterKeyspaceStatement(String name, KSPropDefs attrs)
+    {
+        super();
+        this.name = name;
+        this.attrs = attrs;
+    }
+
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasKeyspaceAccess(name, Permission.ALTER);
+    }
+
+    @Override
+    public void validate(ClientState state) throws InvalidRequestException, 
SchemaDisagreementException
+    {
+        super.validate(state);
+
+        KSMetaData ksm = Schema.instance.getKSMetaData(name);
+        if (ksm == null)
+            throw new InvalidRequestException("Unknown keyspace " + name);
+        if (ksm.name.equalsIgnoreCase(Table.SYSTEM_TABLE))
+            throw new InvalidRequestException("Cannot alter system keyspace");
+
+        try
+        {
+            attrs.validate();
+
+            if (attrs.getReplicationStrategyClass() == null && 
!attrs.getReplicationOptions().isEmpty())
+            {
+                throw new InvalidRequestException("Missing replication 
strategy class");
+            }
+            else if (attrs.getReplicationStrategyClass() != null)
+            {
+                // trial run to let ARS validate class + per-class options
+                AbstractReplicationStrategy.createReplicationStrategy(name,
+                                                                      
AbstractReplicationStrategy.getClass(attrs.getReplicationStrategyClass()),
+                                                                      
StorageService.instance.getTokenMetadata(),
+                                                                      
DatabaseDescriptor.getEndpointSnitch(),
+                                                                      
attrs.getReplicationOptions());
+            }
+        }
+        catch (ConfigurationException e)
+        {
+            throw new InvalidRequestException(e.getMessage());
+        }
+    }
+
+    public void announceMigration() throws InvalidRequestException, 
ConfigurationException
+    {
+        KSMetaData ksm = Schema.instance.getKSMetaData(name);
+        // In the (very) unlikely case the keyspace was dropped since 
validate()
+        if (ksm == null)
+            throw new InvalidRequestException("Unknown keyspace " + name);
+
+        MigrationManager.announceKeyspaceUpdate(attrs.asKSMetadataUpdate(ksm));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/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 7867019..965b27e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -23,9 +23,7 @@ import java.util.*;
 import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.config.*;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.db.marshal.CompositeType;
-import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.db.marshal.*;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
 import org.apache.cassandra.thrift.InvalidRequestException;
@@ -42,15 +40,15 @@ public class AlterTableStatement extends 
SchemaAlteringStatement
     public final Type oType;
     public final String validator;
     public final ColumnIdentifier columnName;
-    private final CFPropDefs cfProps = new CFPropDefs();
+    private final CFPropDefs cfProps;
 
-    public AlterTableStatement(CFName name, Type type, ColumnIdentifier 
columnName, String validator, Map<String, String> propertyMap)
+    public AlterTableStatement(CFName name, Type type, ColumnIdentifier 
columnName, String validator, CFPropDefs cfProps)
     {
         super(name);
         this.oType = type;
         this.columnName = columnName;
         this.validator = validator; // used only for ADD/ALTER commands
-        this.cfProps.addAll(propertyMap);
+        this.cfProps = cfProps;
     }
 
     public void checkAccess(ClientState state) throws InvalidRequestException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
 
b/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
index 763db28..286f265 100644
--- 
a/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
+++ 
b/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
@@ -108,14 +108,7 @@ public class CreateColumnFamilyStatement extends 
SchemaAlteringStatement
                                      comparator,
                                      null);
 
-            newCFMD.defaultValidator(defaultValidator)
-                   .columnMetadata(getColumns())
-                   .keyValidator(keyValidator)
-                   .keyAlias(keyAlias)
-                   .columnAliases(columnAliases)
-                   .valueAlias(valueAlias);
-
-            properties.applyToCFMetadata(newCFMD);
+            applyPropertiesTo(newCFMD);
         }
         catch (ConfigurationException e)
         {
@@ -124,6 +117,18 @@ public class CreateColumnFamilyStatement extends 
SchemaAlteringStatement
         return newCFMD;
     }
 
+    public void applyPropertiesTo(CFMetaData cfmd) throws 
InvalidRequestException, ConfigurationException
+    {
+        cfmd.defaultValidator(defaultValidator)
+            .columnMetadata(getColumns())
+            .keyValidator(keyValidator)
+            .keyAlias(keyAlias)
+            .columnAliases(columnAliases)
+            .valueAlias(valueAlias);
+
+        properties.applyToCFMetadata(cfmd);
+    }
+
     public static class RawStatement extends CFStatement
     {
         private final Map<ColumnIdentifier, String> definitions = new 
HashMap<ColumnIdentifier, String>();
@@ -277,7 +282,7 @@ public class CreateColumnFamilyStatement extends 
SchemaAlteringStatement
             columnAliases.add(alias);
         }
 
-        public void addProperty(String name, String value)
+        public void addProperty(String name, String value) throws 
InvalidRequestException
         {
             properties.addProperty(name, value);
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2ce7b9b3/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
index 863a869..9b36531 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
@@ -26,6 +26,7 @@ import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.KSMetaData;
 import org.apache.cassandra.config.Schema;
+import org.apache.cassandra.cql3.KSPropDefs;
 import org.apache.cassandra.locator.AbstractReplicationStrategy;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
@@ -38,9 +39,7 @@ import org.apache.cassandra.thrift.ThriftValidation;
 public class CreateKeyspaceStatement extends SchemaAlteringStatement
 {
     private final String name;
-    private final Map<String, String> attrs;
-    private String strategyClass;
-    private Map<String, String> strategyOptions = new HashMap<String, 
String>();
+    private final KSPropDefs attrs;
 
     /**
      * Creates a new <code>CreateKeyspaceStatement</code> instance for a given
@@ -49,7 +48,7 @@ public class CreateKeyspaceStatement extends 
SchemaAlteringStatement
      * @param name the name of the keyspace to create
      * @param attrs map of the raw keyword arguments that followed the 
<code>WITH</code> keyword.
      */
-    public CreateKeyspaceStatement(String name, Map<String, String> attrs)
+    public CreateKeyspaceStatement(String name, KSPropDefs attrs)
     {
         super();
         this.name = name;
@@ -80,24 +79,19 @@ public class CreateKeyspaceStatement extends 
SchemaAlteringStatement
         if (name.length() > Schema.NAME_LENGTH)
             throw new InvalidRequestException(String.format("Keyspace names 
shouldn't be more than %s characters long (got \"%s\")", Schema.NAME_LENGTH, 
name));
 
-        // required
-        if (!attrs.containsKey("strategy_class"))
-            throw new InvalidRequestException("missing required argument 
\"strategy_class\"");
-        strategyClass = attrs.get("strategy_class");
-
-        // optional
-        for (String key : attrs.keySet())
-            if ((key.contains(":")) && (key.startsWith("strategy_options")))
-                strategyOptions.put(key.split(":")[1], attrs.get(key));
-
-        // trial run to let ARS validate class + per-class options
         try
         {
+            attrs.validate();
+
+            if (attrs.getReplicationStrategyClass() == null)
+                throw new ConfigurationException("Missing mandatory 
replication strategy class");
+
+            // trial run to let ARS validate class + per-class options
             AbstractReplicationStrategy.createReplicationStrategy(name,
-                                                                  
AbstractReplicationStrategy.getClass(strategyClass),
+                                                                  
AbstractReplicationStrategy.getClass(attrs.getReplicationStrategyClass()),
                                                                   
StorageService.instance.getTokenMetadata(),
                                                                   
DatabaseDescriptor.getEndpointSnitch(),
-                                                                  
strategyOptions);
+                                                                  
attrs.getReplicationOptions());
         }
         catch (ConfigurationException e)
         {
@@ -107,8 +101,7 @@ public class CreateKeyspaceStatement extends 
SchemaAlteringStatement
 
     public void announceMigration() throws InvalidRequestException, 
ConfigurationException
     {
-        KSMetaData ksm = KSMetaData.newKeyspace(name, strategyClass, 
strategyOptions);
         ThriftValidation.validateKeyspaceNotYetExisting(name);
-        MigrationManager.announceNewKeyspace(ksm);
+        MigrationManager.announceNewKeyspace(attrs.asKSMetadata(name));
     }
 }

Reply via email to