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.3
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<>();
+        }
+    }
 }

Reply via email to