Updated Branches: refs/heads/trunk c64d975cd -> 1e126dada
Add ALTER KEYSPACE statement to CQL3 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/1e126dad Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1e126dad Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1e126dad Branch: refs/heads/trunk Commit: 1e126dadac0498a8fac9841da6d216d2510a2a1c Parents: c64d975 Author: Sylvain Lebresne <[email protected]> Authored: Wed Sep 12 18:03:19 2012 +0200 Committer: Sylvain Lebresne <[email protected]> Committed: Wed Sep 12 18:03:19 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 | 194 ++++----------- src/java/org/apache/cassandra/cql3/Cql.g | 32 ++- src/java/org/apache/cassandra/cql3/KSPropDefs.java | 86 +++++++ .../apache/cassandra/cql3/PropertyDefinitions.java | 148 +++++++++++ .../cql3/statements/AlterKeyspaceStatement.java | 86 +++++++ .../cql3/statements/AlterTableStatement.java | 6 +- .../statements/CreateColumnFamilyStatement.java | 2 +- .../cql3/statements/CreateKeyspaceStatement.java | 38 +-- 11 files changed, 412 insertions(+), 192 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 630ae18..371af7c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -61,6 +61,7 @@ * Replace Throttle with Guava's RateLimiter for HintedHandOff (CASSANDRA-4541) * fix counter add/get using CQL2 and CQL3 in stress tool (CASSANDRA-4633) * Add sstable count per level to cfstats (CASSANDRA-4537) + * (cql3) Add ALTER KEYSPACE statement (CASSANDRA-4611) 1.1.6 http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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 9feb0d3..050e32f 100644 --- a/src/java/org/apache/cassandra/config/KSMetaData.java +++ b/src/java/org/apache/cassandra/config/KSMetaData.java @@ -57,18 +57,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/1e126dad/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 074c50a..f3800cb 100644 --- a/src/java/org/apache/cassandra/cql/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql/QueryProcessor.java @@ -647,7 +647,8 @@ public class QueryProcessor { KSMetaData ksm = KSMetaData.newKeyspace(create.getName(), create.getStrategyClass(), - create.getStrategyOptions()); + create.getStrategyOptions(), + true); ThriftValidation.validateKeyspaceNotYetExisting(ksm.name); MigrationManager.announceNewKeyspace(ksm); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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 0d1722c..fb5f365 100644 --- a/src/java/org/apache/cassandra/cql3/CFPropDefs.java +++ b/src/java/org/apache/cassandra/cql3/CFPropDefs.java @@ -17,20 +17,23 @@ */ 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.exceptions.ConfigurationException; +import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.db.compaction.AbstractCompactionStrategy; import org.apache.cassandra.io.compress.CompressionParameters; -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 final Logger logger = LoggerFactory.getLogger(CFPropDefs.class); @@ -41,17 +44,15 @@ public class CFPropDefs public static final String KW_MINCOMPACTIONTHRESHOLD = "min_threshold"; public static final String KW_MAXCOMPACTIONTHRESHOLD = "max_threshold"; public static final String KW_REPLICATEONWRITE = "replicate_on_write"; - public static final String KW_COMPACTION_STRATEGY_CLASS = "class"; public static final String KW_CACHING = "caching"; public static final String KW_BF_FP_CHANCE = "bloom_filter_fp_chance"; + public static final String KW_COMPACTION = "compaction"; + public static final String KW_COMPRESSION = "compression"; + + public static final String COMPACTION_STRATEGY_CLASS_KEY = "class"; - // Maps CQL short names to the respective Cassandra comparator/validator class names public static final Set<String> keywords = new HashSet<String>(); public static final Set<String> obsoleteKeywords = new HashSet<String>(); - public static final Set<String> allowedKeywords = new HashSet<String>(); - - public static final String COMPACTION_PARAMETERS = "compaction"; - public static final String COMPRESSION_PARAMETERS = "compression"; static { @@ -62,6 +63,8 @@ public class CFPropDefs keywords.add(KW_REPLICATEONWRITE); keywords.add(KW_CACHING); keywords.add(KW_BF_FP_CHANCE); + keywords.add(KW_COMPACTION); + keywords.add(KW_COMPRESSION); obsoleteKeywords.add("compaction_strategy_class"); obsoleteKeywords.add("compaction_strategy_options"); @@ -69,170 +72,75 @@ public class CFPropDefs obsoleteKeywords.add("max_compaction_threshold"); obsoleteKeywords.add("compaction_parameters"); obsoleteKeywords.add("compression_parameters"); - - allowedKeywords.addAll(keywords); - 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>() - {{ - if (CFMetaData.DEFAULT_COMPRESSOR != null) - put(CompressionParameters.SSTABLE_COMPRESSION, CFMetaData.DEFAULT_COMPRESSOR); - }}; - - public void validate() throws ConfigurationException + + public void validate() throws ConfigurationException, SyntaxException { - // 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 (!compactionStrategyOptions.isEmpty()) + Map<String, String> compactionOptions = getCompactionOptions(); + if (!compactionOptions.isEmpty()) { - if (compactionStrategyOptions.containsKey(KW_COMPACTION_STRATEGY_CLASS)) - { - compactionStrategyClass = CFMetaData.createCompactionStrategy(compactionStrategyOptions.get(KW_COMPACTION_STRATEGY_CLASS)); - compactionStrategyOptions.remove(KW_COMPACTION_STRATEGY_CLASS); - } - else - { - throw new ConfigurationException("Missing sub-option '" + KW_COMPACTION_STRATEGY_CLASS + "' for the '" + COMPACTION_PARAMETERS + "' option."); - } + String strategy = compactionOptions.get(COMPACTION_STRATEGY_CLASS_KEY); + if (strategy == null) + throw new ConfigurationException("Missing sub-option '" + COMPACTION_STRATEGY_CLASS_KEY + "' for the '" + KW_COMPACTION + "' option."); + + compactionStrategyClass = CFMetaData.createCompactionStrategy(strategy); + compactionOptions.remove(COMPACTION_STRATEGY_CLASS_KEY); } } - /** Map a keyword to the corresponding value */ - public void addProperty(String name, String value) + public Map<String, String> getCompactionOptions() throws SyntaxException { - properties.put(name, value); + Map<String, String> compactionOptions = getMap(KW_COMPACTION); + if (compactionOptions == null) + return Collections.<String, String>emptyMap(); + return compactionOptions; } - public void addProperty(String name, Map<String, String> value) + public Map<String, String> getCompressionOptions() throws SyntaxException { - if (name.equalsIgnoreCase(COMPACTION_PARAMETERS)) + Map<String, String> compressionOptions = getMap(KW_COMPRESSION); + if (compressionOptions == null) { - for (Map.Entry<String, String> entry : value.entrySet()) - compactionStrategyOptions.put(entry.getKey().toLowerCase(), entry.getValue()); + return new HashMap<String, String>() + {{ + if (CFMetaData.DEFAULT_COMPRESSOR != null) + put(CompressionParameters.SSTABLE_COMPRESSION, CFMetaData.DEFAULT_COMPRESSOR); + }}; } - else if (name.equalsIgnoreCase(COMPRESSION_PARAMETERS)) - { - for (Map.Entry<String, String> entry : value.entrySet()) - compressionParameters.put(entry.getKey().toLowerCase(), entry.getValue()); - } - } - - public void addAll(Map<String, String> propertyMap) - { - for (Map.Entry<String, String> entry : propertyMap.entrySet()) - addProperty(entry.getKey(), entry.getValue()); + return compressionOptions; } - public Boolean hasProperty(String name) - { - return properties.containsKey(name); - } - - public void applyToCFMetadata(CFMetaData cfm) throws ConfigurationException + public void applyToCFMetadata(CFMetaData cfm) throws ConfigurationException, SyntaxException { 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())); cfm.gcGraceSeconds(getInt(KW_GCGRACESECONDS, cfm.getGcGraceSeconds())); cfm.replicateOnWrite(getBoolean(KW_REPLICATEONWRITE, cfm.getReplicateOnWrite())); - cfm.minCompactionThreshold(toInt(KW_MINCOMPACTIONTHRESHOLD, compactionStrategyOptions.get(KW_MINCOMPACTIONTHRESHOLD), cfm.getMinCompactionThreshold())); - cfm.maxCompactionThreshold(toInt(KW_MAXCOMPACTIONTHRESHOLD, compactionStrategyOptions.get(KW_MAXCOMPACTIONTHRESHOLD), cfm.getMaxCompactionThreshold())); + cfm.minCompactionThreshold(toInt(KW_MINCOMPACTIONTHRESHOLD, getCompactionOptions().get(KW_MINCOMPACTIONTHRESHOLD), cfm.getMinCompactionThreshold())); + cfm.maxCompactionThreshold(toInt(KW_MAXCOMPACTIONTHRESHOLD, getCompactionOptions().get(KW_MAXCOMPACTIONTHRESHOLD), cfm.getMaxCompactionThreshold())); cfm.caching(CFMetaData.Caching.fromString(getString(KW_CACHING, cfm.getCaching().toString()))); 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.valueOf(value); - } - catch (NumberFormatException e) - { - throw new ConfigurationException(String.format("%s not valid for \"%s\"", value, key)); - } + cfm.compactionStrategyClass(compactionStrategyClass); + cfm.compactionStrategyOptions(new HashMap<String, String>(getCompactionOptions())); } - 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.valueOf(value); - } - catch (NumberFormatException e) - { - throw new ConfigurationException(String.format("%s not valid for \"%s\"", value, key)); - } - } - return result; + if (!getCompressionOptions().isEmpty()) + cfm.compressionParameters(CompressionParameters.create(getCompressionOptions())); } @Override public String toString() { - return String.format("CFPropDefs(%s, compaction: %s, compression: %s)", - properties.toString(), - compactionStrategyOptions.toString(), - compressionParameters.toString()); + return String.format("CFPropDefs(%s)", properties.toString()); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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 acb6cb1..79c5b9f 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -169,6 +169,7 @@ cqlStatement returns [ParsedStatement stmt] | st15=grantStatement { $stmt = st15; } | st16=revokeStatement { $stmt = st16; } | st17=listGrantsStatement { $stmt = st17; } + | st18=alterKeyspaceStatement { $stmt = st18; } ; /* @@ -373,8 +374,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=mapProperties { $expr = new CreateKeyspaceStatement(ks, props); } + K_WITH properties[attrs] { $expr = new CreateKeyspaceStatement(ks, attrs); } ; /** @@ -424,6 +426,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>; @@ -438,8 +450,7 @@ alterTableStatement returns [AlterTableStatement expr] ( 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 (property[props] - ( K_AND property[props] )* { type = AlterTableStatement.Type.OPTS; }) + | K_WITH properties[props] { type = AlterTableStatement.Type.OPTS; } ) { $expr = new AlterTableStatement(cf, type, id, v, props); @@ -623,9 +634,13 @@ operation returns [Operation op] | '+' ml=map_literal { $op = MapOperation.Put(ml); } ; -property[CFPropDefs props] - : k=cident '=' (simple=propertyValue { $props.addProperty(k.toString(), simple); } - | map=map_literal { $props.addProperty(k.toString(), convertMap(map)); }) +properties[PropertyDefinitions props] + : property[props] (K_AND property[props])* + ; + +property[PropertyDefinitions props] + : k=cident '=' (simple=propertyValue { try { $props.addProperty(k.toString(), simple); } catch (SyntaxException e) { addRecognitionError(e.getMessage()); } } + | map=map_literal { try { $props.addProperty(k.toString(), convertMap(map)); } catch (SyntaxException e) { addRecognitionError(e.getMessage()); } }) ; propertyValue returns [String str] @@ -633,11 +648,6 @@ propertyValue returns [String str] | u=unreserved_keyword { $str = u; } ; -mapProperties returns [Map<String, Map<String, String>> props] - @init { $props = new HashMap<String, Map<String, String>>(); } - : k=cident '=' v=map_literal { $props.put(k.toString(), convertMap(v)); } (K_AND kn=cident '=' vn=map_literal { $props.put(kn.toString(), convertMap(vn)); } )* - ; - // Either a string or a list of terms tokenDefinition returns [Pair<String, List<Term>> tkdef] : K_TOKEN { List<Term> l = new ArrayList<Term>(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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..70df622 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/KSPropDefs.java @@ -0,0 +1,86 @@ +/* + * 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.exceptions.*; + +public class KSPropDefs extends PropertyDefinitions +{ + public static final String KW_DURABLE_WRITES = "durable_writes"; + public static final String KW_REPLICATION = "replication"; + + public static final String REPLICATION_STRATEGY_CLASS_KEY = "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); + + obsoleteKeywords.add("strategy_class"); + } + + private String strategyClass; + + public void validate() throws ConfigurationException, SyntaxException + { + validate(keywords, obsoleteKeywords); + + Map<String, String> replicationOptions = getReplicationOptions(); + if (!replicationOptions.isEmpty()) + { + strategyClass = replicationOptions.get(REPLICATION_STRATEGY_CLASS_KEY); + replicationOptions.remove(REPLICATION_STRATEGY_CLASS_KEY); + } + } + + public Map<String, String> getReplicationOptions() throws SyntaxException + { + Map<String, String> replicationOptions = getMap(KW_REPLICATION); + if (replicationOptions == null) + return Collections.<String, String>emptyMap(); + return replicationOptions; + } + + public String getReplicationStrategyClass() + { + return strategyClass; + } + + public KSMetaData asKSMetadata(String ksName) throws RequestValidationException + { + return KSMetaData.newKeyspace(ksName, getReplicationStrategyClass(), getReplicationOptions(), getBoolean(KW_DURABLE_WRITES, true)); + } + + public KSMetaData asKSMetadataUpdate(KSMetaData old) throws RequestValidationException + { + 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/1e126dad/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..3db84fa --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/PropertyDefinitions.java @@ -0,0 +1,148 @@ +/* + * 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.exceptions.SyntaxException; + +public class PropertyDefinitions +{ + protected static final Logger logger = LoggerFactory.getLogger(PropertyDefinitions.class); + + protected final Map<String, Object> properties = new HashMap<String, Object>(); + + public void addProperty(String name, String value) throws SyntaxException + { + if (properties.put(name, value) != null) + throw new SyntaxException(String.format("Multiple definition for property '%s'", name)); + } + + public void addProperty(String name, Map<String, String> value) throws SyntaxException + { + // Lowercase the map keys to be nice to users + Map<String, String> lowerCased = new HashMap<String, String>(value.size()); + for (Map.Entry<String, String> entry : value.entrySet()) + lowerCased.put(entry.getKey().toLowerCase(), entry.getValue()); + + if (properties.put(name, lowerCased) != null) + throw new SyntaxException(String.format("Multiple definition for property '%s'", name)); + } + + public void validate(Set<String> keywords, Set<String> obsolete) throws SyntaxException + { + for (String name : properties.keySet()) + { + if (keywords.contains(name)) + continue; + + if (obsolete.contains(name)) + logger.warn("Ignoring obsolete property {}", name); + else + throw new SyntaxException(String.format("Unknown property '%s'", name)); + } + } + + protected String getSimple(String name) throws SyntaxException + { + Object val = properties.get(name); + if (val == null) + return null; + if (!(val instanceof String)) + throw new SyntaxException(String.format("Invalid value for property '%s'", name)); + return (String)val; + } + + protected Map<String, String> getMap(String name) throws SyntaxException + { + Object val = properties.get(name); + if (val == null) + return null; + if (!(val instanceof Map)) + throw new SyntaxException(String.format("Invalid value for property '%s'", name)); + return (Map<String, String>)val; + } + + public Boolean hasProperty(String name) + { + return properties.containsKey(name); + } + + public String getString(String key, String defaultValue) throws SyntaxException + { + 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 SyntaxException + { + 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 SyntaxException + { + String value = getSimple(key); + if (value == null) + { + return defaultValue; + } + else + { + try + { + return Double.valueOf(value); + } + catch (NumberFormatException e) + { + throw new SyntaxException(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 SyntaxException + { + String value = getSimple(key); + return toInt(key, value, defaultValue); + } + + public static Integer toInt(String key, String value, Integer defaultValue) throws SyntaxException + { + if (value == null) + { + return defaultValue; + } + else + { + try + { + return Integer.valueOf(value); + } + catch (NumberFormatException e) + { + throw new SyntaxException(String.format("Invalid integer value %s for '%s'", value, key)); + } + } + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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..13c9e44 --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/statements/AlterKeyspaceStatement.java @@ -0,0 +1,86 @@ +/* + * 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.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.exceptions.*; +import org.apache.cassandra.locator.AbstractReplicationStrategy; +import org.apache.cassandra.service.ClientState; +import org.apache.cassandra.service.MigrationManager; +import org.apache.cassandra.service.StorageService; + +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 UnauthorizedException, InvalidRequestException + { + state.hasKeyspaceAccess(name, Permission.ALTER); + } + + @Override + public void validate(ClientState state) throws RequestValidationException + { + super.validate(state); + + KSMetaData ksm = Schema.instance.getKSMetaData(name); + if (ksm == null) + throw new InvalidRequestException("Unknown keyspace " + name); + if (ksm.name.equalsIgnoreCase(Table.SYSTEM_KS)) + throw new InvalidRequestException("Cannot alter system keyspace"); + + attrs.validate(); + + if (attrs.getReplicationStrategyClass() == null && !attrs.getReplicationOptions().isEmpty()) + { + throw new ConfigurationException("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()); + } + } + + public void announceMigration() throws RequestValidationException + { + 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/1e126dad/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 fe89f32..a33ff12 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java @@ -28,9 +28,7 @@ import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.*; import org.apache.cassandra.db.marshal.*; -import org.apache.cassandra.exceptions.ConfigurationException; -import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.exceptions.UnauthorizedException; +import org.apache.cassandra.exceptions.*; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.service.MigrationManager; @@ -62,7 +60,7 @@ public class AlterTableStatement extends SchemaAlteringStatement state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER); } - public void announceMigration() throws InvalidRequestException, ConfigurationException + public void announceMigration() throws RequestValidationException { CFMetaData meta = validateColumnFamily(keyspace(), columnFamily()); CFMetaData cfm = meta.clone(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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 238ce67..1cf3137 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java @@ -119,7 +119,7 @@ public class CreateColumnFamilyStatement extends SchemaAlteringStatement return newCFMD; } - public void applyPropertiesTo(CFMetaData cfmd) throws InvalidRequestException, ConfigurationException + public void applyPropertiesTo(CFMetaData cfmd) throws RequestValidationException { cfmd.defaultValidator(defaultValidator) .columnMetadata(getColumns()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/1e126dad/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 4252dfc..5933292 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java @@ -25,6 +25,7 @@ import org.apache.cassandra.auth.Permission; 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.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestValidationException; import org.apache.cassandra.exceptions.UnauthorizedException; @@ -37,13 +38,8 @@ import org.apache.cassandra.thrift.ThriftValidation; /** A <code>CREATE KEYSPACE</code> statement parsed from a CQL query. */ public class CreateKeyspaceStatement extends SchemaAlteringStatement { - private static String REPLICATION_PARAMETERS_PREFIX = "replication"; - private static String REPLICATION_STRATEGY_CLASS_KEY = "class"; - private final String name; - private final Map<String, Map<String, String>> attrs; - private String strategyClass; - private final Map<String, String> strategyOptions = new HashMap<String, String>(); + private final KSPropDefs attrs; /** * Creates a new <code>CreateKeyspaceStatement</code> instance for a given @@ -52,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, Map<String, String>> attrs) + public CreateKeyspaceStatement(String name, KSPropDefs attrs) { super(); this.name = name; @@ -83,35 +79,21 @@ 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)); - if (!attrs.containsKey(REPLICATION_PARAMETERS_PREFIX)) - throw new InvalidRequestException("missing required argument '" + REPLICATION_PARAMETERS_PREFIX + "'"); - - Map<String, String> replication_parameters = attrs.get(REPLICATION_PARAMETERS_PREFIX); - - strategyClass = replication_parameters.get(REPLICATION_STRATEGY_CLASS_KEY); - - if (strategyClass == null) - throw new InvalidRequestException("missing required field '" + REPLICATION_STRATEGY_CLASS_KEY + "' for '" + REPLICATION_PARAMETERS_PREFIX + "' option"); - - for (Map.Entry<String, String> entry : replication_parameters.entrySet()) - { - if (entry.getKey().equals(REPLICATION_STRATEGY_CLASS_KEY)) - continue; + attrs.validate(); - strategyOptions.put(entry.getKey(), entry.getValue()); - } + 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()); } - public void announceMigration() throws InvalidRequestException, ConfigurationException + public void announceMigration() throws RequestValidationException { - KSMetaData ksm = KSMetaData.newKeyspace(name, strategyClass, strategyOptions); - MigrationManager.announceNewKeyspace(ksm); + MigrationManager.announceNewKeyspace(attrs.asKSMetadata(name)); } }
