This is an automated email from the ASF dual-hosted git repository.

adelapena pushed a commit to branch cassandra-5.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-5.0 by this push:
     new b265b4658e Forbid SAI indexes with analysis options on primary key 
columns
b265b4658e is described below

commit b265b4658e007b6943d543a11c609b7ba5fd979f
Author: Andrés de la Peña <[email protected]>
AuthorDate: Wed Jul 19 17:40:50 2023 +0100

    Forbid SAI indexes with analysis options on primary key columns
    
    patch by Andrés de la Peña; reviewed by Caleb Rackliffe for CASSANDRA-18782
---
 CHANGES.txt                                        |  1 +
 .../cassandra/index/sai/StorageAttachedIndex.java  | 11 +++++++-
 .../index/sai/analyzer/AbstractAnalyzer.java       | 12 ++++++---
 .../index/sai/analyzer/NonTokenizingOptions.java   |  5 ++++
 .../index/sai/cql/StorageAttachedIndexDDLTest.java | 31 ++++++++++++++++++++++
 5 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 262a2edcb6..68515312fe 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 5.0-alpha2
+ * Forbid SAI indexes with analysis options on primary key columns 
(CASSANDRA-18782)
 Merged from 4.1:
 Merged from 4.0:
 Merged from 3.11:
diff --git a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java 
b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
index e8c4b53f4e..805a6b65e1 100644
--- a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
+++ b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.CqlBuilder;
 import org.apache.cassandra.cql3.Operator;
 import org.apache.cassandra.cql3.statements.schema.IndexTarget;
 import org.apache.cassandra.db.CassandraWriteContext;
@@ -96,6 +97,9 @@ import org.apache.cassandra.utils.concurrent.OpOrder;
 public class StorageAttachedIndex implements Index
 {
     public static final String NAME = "sai";
+
+    @VisibleForTesting
+    public static final String ANALYSIS_ON_KEY_COLUMNS_MESSAGE = "Analysis 
options are not supported on primary key columns, but found ";
     
     private static final Logger logger = 
LoggerFactory.getLogger(StorageAttachedIndex.class);
 
@@ -253,7 +257,12 @@ public class StorageAttachedIndex implements Index
             throw new InvalidRequestException("Unsupported type: " + 
type.asCQL3Type());
         }
 
-        AbstractAnalyzer.fromOptions(type, options);
+        Map<String, String> analysisOptions = 
AbstractAnalyzer.getAnalyzerOptions(options);
+        if (target.left.isPrimaryKeyColumn() && !analysisOptions.isEmpty())
+        {
+            throw new InvalidRequestException(ANALYSIS_ON_KEY_COLUMNS_MESSAGE 
+ new CqlBuilder().append(analysisOptions));
+        }
+        AbstractAnalyzer.fromOptions(type, analysisOptions);
 
         return Collections.emptyMap();
     }
diff --git 
a/src/java/org/apache/cassandra/index/sai/analyzer/AbstractAnalyzer.java 
b/src/java/org/apache/cassandra/index/sai/analyzer/AbstractAnalyzer.java
index 1f5e339657..da0b2a4d19 100644
--- a/src/java/org/apache/cassandra/index/sai/analyzer/AbstractAnalyzer.java
+++ b/src/java/org/apache/cassandra/index/sai/analyzer/AbstractAnalyzer.java
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.stream.Collectors;
 
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.StringType;
@@ -103,9 +104,14 @@ public abstract class AbstractAnalyzer implements 
Iterator<ByteBuffer>
 
     private static boolean hasNonTokenizingOptions(Map<String, String> options)
     {
-        return options.get(NonTokenizingOptions.ASCII) != null ||
-               options.containsKey(NonTokenizingOptions.CASE_SENSITIVE) ||
-               options.containsKey(NonTokenizingOptions.NORMALIZE);
+        return 
options.keySet().stream().anyMatch(NonTokenizingOptions::hasOption);
+    }
+
+    public static Map<String, String> getAnalyzerOptions(Map<String, String> 
options)
+    {
+        return options.entrySet().stream()
+                      .filter(e -> NonTokenizingOptions.hasOption(e.getKey()))
+                      .collect(Collectors.toMap(Map.Entry::getKey, 
Map.Entry::getValue));
     }
 
 }
diff --git 
a/src/java/org/apache/cassandra/index/sai/analyzer/NonTokenizingOptions.java 
b/src/java/org/apache/cassandra/index/sai/analyzer/NonTokenizingOptions.java
index ab6485acf5..66c7740a53 100644
--- a/src/java/org/apache/cassandra/index/sai/analyzer/NonTokenizingOptions.java
+++ b/src/java/org/apache/cassandra/index/sai/analyzer/NonTokenizingOptions.java
@@ -65,6 +65,11 @@ public class NonTokenizingOptions
         this.normalized = normalized;
     }
 
+    static boolean hasOption(String option)
+    {
+        return option.equals(NORMALIZE) || option.equals(CASE_SENSITIVE) || 
option.equals(ASCII);
+    }
+
     public static class OptionsBuilder
     {
         private boolean caseSensitive = true;
diff --git 
a/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java 
b/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java
index b3d57d95a0..6dedd5ed2d 100644
--- 
a/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java
+++ 
b/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java
@@ -24,11 +24,13 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.LongStream;
 
+import com.google.common.collect.ImmutableMap;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -40,6 +42,7 @@ import 
com.datastax.driver.core.exceptions.ReadFailureException;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.cql3.CQL3Type;
 import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.CqlBuilder;
 import org.apache.cassandra.cql3.restrictions.IndexRestrictions;
 import org.apache.cassandra.cql3.statements.schema.CreateIndexStatement;
 import org.apache.cassandra.db.ColumnFamilyStore;
@@ -58,6 +61,7 @@ import org.apache.cassandra.index.sai.IndexContext;
 import org.apache.cassandra.index.sai.SAITester;
 import org.apache.cassandra.index.sai.StorageAttachedIndex;
 import org.apache.cassandra.index.sai.StorageAttachedIndexBuilder;
+import org.apache.cassandra.index.sai.analyzer.NonTokenizingOptions;
 import org.apache.cassandra.index.sai.disk.format.IndexComponent;
 import org.apache.cassandra.index.sai.disk.format.Version;
 import org.apache.cassandra.index.sai.disk.v1.segment.SegmentBuilder;
@@ -417,6 +421,33 @@ public class StorageAttachedIndexDDLTest extends SAITester
         assertEquals(1, execute("SELECT id FROM %s WHERE val = 
'eppinger'").size());
     }
 
+    @Test
+    public void shouldRejectAnalysisOnPrimaryKeyColumns()
+    {
+        createTable("CREATE TABLE %s (k1 text, k2 text, c1 text, c2 text, 
PRIMARY KEY((k1, k2), c1, c2))");
+
+        for (String column : Arrays.asList("k1", "k2", "c1", "c2"))
+        {
+            for (String enabled : Arrays.asList("true", "false"))
+            {
+                assertRejectsAnalysisOnPrimaryKeyColumns(column, 
ImmutableMap.of(NonTokenizingOptions.NORMALIZE, enabled));
+                assertRejectsAnalysisOnPrimaryKeyColumns(column, 
ImmutableMap.of(NonTokenizingOptions.CASE_SENSITIVE, enabled));
+                assertRejectsAnalysisOnPrimaryKeyColumns(column, 
ImmutableMap.of(NonTokenizingOptions.ASCII, enabled));
+                assertRejectsAnalysisOnPrimaryKeyColumns(column, 
ImmutableMap.of(NonTokenizingOptions.NORMALIZE, enabled,
+                                                                               
  NonTokenizingOptions.CASE_SENSITIVE, enabled,
+                                                                               
  NonTokenizingOptions.ASCII, enabled));
+            }
+        }
+    }
+
+    private void assertRejectsAnalysisOnPrimaryKeyColumns(String column, 
Map<String, String> optionsMap)
+    {
+        String options = new CqlBuilder().append(optionsMap).toString();
+        Assertions.assertThatThrownBy(() -> createIndex("CREATE INDEX ON %s(" 
+ column + ") USING 'sai' WITH OPTIONS = " + options))
+                  .hasRootCauseInstanceOf(InvalidRequestException.class)
+                  
.hasRootCauseMessage(StorageAttachedIndex.ANALYSIS_ON_KEY_COLUMNS_MESSAGE + 
options);
+    }
+
     @Test
     public void shouldCreateIndexOnReversedType()
     {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to