Repository: cassandra Updated Branches: refs/heads/cassandra-3.0 97e8953ec -> 5e5ea772e refs/heads/cassandra-3.3 b48d71cc6 -> 501f75bf9 refs/heads/trunk b719cff71 -> 75f78729d
Add custom 2i validation method which uses base CFMetaData Patch by Andrés de la Peña; reviewed by Sam Tunnicliffe for CASSANDRA-10924 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5e5ea772 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5e5ea772 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5e5ea772 Branch: refs/heads/cassandra-3.0 Commit: 5e5ea772ed346b5197a6a7be3046953686a8975a Parents: 97e8953 Author: Andrés de la Peña <[email protected]> Authored: Tue Jan 12 15:11:37 2016 +0000 Committer: Sam Tunnicliffe <[email protected]> Committed: Fri Jan 15 11:13:19 2016 +0000 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../org/apache/cassandra/config/CFMetaData.java | 2 +- src/java/org/apache/cassandra/index/Index.java | 21 +++++--- .../apache/cassandra/schema/IndexMetadata.java | 20 +++++-- .../apache/cassandra/index/CustomIndexTest.java | 57 ++++++++++++++++++++ 5 files changed, 88 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5ea772/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 8c3527f..ff607a5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.3 + * Support passing base table metadata to custom 2i validation (CASSANDRA-10924) * Ensure stale index entries are purged during reads (CASSANDRA-11013) * Fix AssertionError when removing from list using UPDATE (CASSANDRA-10954) * Fix UnsupportedOperationException when reading old sstable with range http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5ea772/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 62b2369..cb6d3b8 100644 --- a/src/java/org/apache/cassandra/config/CFMetaData.java +++ b/src/java/org/apache/cassandra/config/CFMetaData.java @@ -877,7 +877,7 @@ public final class CFMetaData throw new ConfigurationException("Duplicate index name " + index.name); indexNames.add(index.name); - index.validate(); + index.validate(this); } return this; http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5ea772/src/java/org/apache/cassandra/index/Index.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/index/Index.java b/src/java/org/apache/cassandra/index/Index.java index 084d0e3..71dcfc9 100644 --- a/src/java/org/apache/cassandra/index/Index.java +++ b/src/java/org/apache/cassandra/index/Index.java @@ -89,16 +89,21 @@ import org.apache.cassandra.utils.concurrent.OpOrder; * The post processing function is obtained from the Index's postProcessorFor method; the built-in indexes which ship * with Cassandra return a no-op function here. * - * An optional static method may be provided to validate custom index options: + * An optional static method may be provided to validate custom index options (two variants are supported): * - * <pre> {@code - * public static Map<String, String> validateOptions(Map<String, String> options); - * } </pre> + * <pre>{@code public static Map<String, String> validateOptions(Map<String, String> options);</pre> * - * The input is the map of index options supplied in the WITH clause of a CREATE INDEX statement. The method should - * return a map containing any of the supplied options which are not valid for the implementation. If the returned - * map is not empty, validation is considered failed and an error is raised. Alternatively, the implementation may - * choose to throw an org.apache.cassandra.exceptions.ConfigurationException if invalid options are encountered. + * The input is the map of index options supplied in the WITH clause of a CREATE INDEX statement. + * + * <pre>{@code public static Map<String, String> validateOptions(Map<String, String> options, CFMetaData cfm);}</pre> + * + * In this version, the base table's metadata is also supplied as an argument. + * If both overloaded methods are provided, only the one including the base table's metadata will be invoked. + * + * The validation method should return a map containing any of the supplied options which are not valid for the + * implementation. If the returned map is not empty, validation is considered failed and an error is raised. + * Alternatively, the implementation may choose to throw an org.apache.cassandra.exceptions.ConfigurationException + * if invalid options are encountered. * */ public interface Index http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5ea772/src/java/org/apache/cassandra/schema/IndexMetadata.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/IndexMetadata.java b/src/java/org/apache/cassandra/schema/IndexMetadata.java index ee9179a..7c60a64 100644 --- a/src/java/org/apache/cassandra/schema/IndexMetadata.java +++ b/src/java/org/apache/cassandra/schema/IndexMetadata.java @@ -138,7 +138,7 @@ public final class IndexMetadata return (cfName + "_" + root + "_idx").replaceAll("\\W", ""); } - public void validate() + public void validate(CFMetaData cfm) { if (!isNameValid(name)) throw new ConfigurationException("Illegal index name " + name); @@ -155,11 +155,14 @@ public final class IndexMetadata Class<Index> indexerClass = FBUtilities.classForName(className, "custom indexer"); if(!Index.class.isAssignableFrom(indexerClass)) throw new ConfigurationException(String.format("Specified Indexer class (%s) does not implement the Indexer interface", className)); - validateCustomIndexOptions(indexerClass, options); + validateCustomIndexOptions(cfm, indexerClass, options); } } - private void validateCustomIndexOptions(Class<? extends Index> indexerClass, Map<String, String> options) throws ConfigurationException + private void validateCustomIndexOptions(CFMetaData cfm, + Class<? extends Index> indexerClass, + Map<String, String> options) + throws ConfigurationException { try { @@ -169,7 +172,16 @@ public final class IndexMetadata if (filteredOptions.isEmpty()) return; - Map<?,?> unknownOptions = (Map) indexerClass.getMethod("validateOptions", Map.class).invoke(null, filteredOptions); + Map<?,?> unknownOptions; + try + { + unknownOptions = (Map) indexerClass.getMethod("validateOptions", Map.class, CFMetaData.class).invoke(null, filteredOptions, cfm); + } + catch (NoSuchMethodException e) + { + unknownOptions = (Map) indexerClass.getMethod("validateOptions", Map.class).invoke(null, filteredOptions); + } + if (!unknownOptions.isEmpty()) throw new ConfigurationException(String.format("Properties specified %s are not understood by %s", unknownOptions.keySet(), indexerClass.getSimpleName())); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e5ea772/test/unit/org/apache/cassandra/index/CustomIndexTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/index/CustomIndexTest.java b/test/unit/org/apache/cassandra/index/CustomIndexTest.java index b305868..3bfb6a5 100644 --- a/test/unit/org/apache/cassandra/index/CustomIndexTest.java +++ b/test/unit/org/apache/cassandra/index/CustomIndexTest.java @@ -32,6 +32,7 @@ import org.apache.cassandra.utils.FBUtilities; import static org.apache.cassandra.Util.throwAssert; import static org.apache.cassandra.cql3.statements.IndexTarget.CUSTOM_INDEX_OPTION_NAME; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -504,6 +505,28 @@ public class CustomIndexTest extends CQLTester assertEquals(index.rowsDeleted.get(i).clustering(), index.rowsInserted.get(i).clustering()); } + @Test + public void validateOptions() throws Throwable + { + createTable("CREATE TABLE %s(k int, c int, v1 int, v2 int, PRIMARY KEY(k,c))"); + createIndex(String.format("CREATE CUSTOM INDEX ON %%s(c, v2) USING '%s' WITH OPTIONS = {'foo':'bar'}", + IndexWithValidateOptions.class.getName())); + assertNotNull(IndexWithValidateOptions.options); + assertEquals("bar", IndexWithValidateOptions.options.get("foo")); + } + + @Test + public void validateOptionsWithCFMetaData() throws Throwable + { + createTable("CREATE TABLE %s(k int, c int, v1 int, v2 int, PRIMARY KEY(k,c))"); + createIndex(String.format("CREATE CUSTOM INDEX ON %%s(c, v2) USING '%s' WITH OPTIONS = {'foo':'bar'}", + IndexWithOverloadedValidateOptions.class.getName())); + CFMetaData cfm = getCurrentColumnFamilyStore().metadata; + assertEquals(cfm, IndexWithOverloadedValidateOptions.cfm); + assertNotNull(IndexWithOverloadedValidateOptions.options); + assertEquals("bar", IndexWithOverloadedValidateOptions.options.get("foo")); + } + private void testCreateIndex(String indexName, String... targetColumnNames) throws Throwable { createIndex(String.format("CREATE CUSTOM INDEX %s ON %%s(%s) USING '%s'", @@ -672,4 +695,38 @@ public class CustomIndexTest extends CQLTester throw new InvalidRequestException("None shall pass"); } } + + public static final class IndexWithValidateOptions extends StubIndex + { + public static Map<String, String> options; + + public IndexWithValidateOptions(ColumnFamilyStore baseCfs, IndexMetadata metadata) + { + super(baseCfs, metadata); + } + + public static Map<String, String> validateOptions(Map<String, String> options) + { + IndexWithValidateOptions.options = options; + return new HashMap<>(); + } + } + + public static final class IndexWithOverloadedValidateOptions extends StubIndex + { + public static CFMetaData cfm; + public static Map<String, String> options; + + public IndexWithOverloadedValidateOptions(ColumnFamilyStore baseCfs, IndexMetadata metadata) + { + super(baseCfs, metadata); + } + + public static Map<String, String> validateOptions(Map<String, String> options, CFMetaData cfm) + { + IndexWithOverloadedValidateOptions.options = options; + IndexWithOverloadedValidateOptions.cfm = cfm; + return new HashMap<>(); + } + } }
