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

smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new d276dec0ad Deduplicate constraint validation logic for supported types
d276dec0ad is described below

commit d276dec0ade704ed01e82185b151077feeb48802
Author: Stefan Miklosovic <smikloso...@apache.org>
AuthorDate: Wed Feb 26 21:14:17 2025 +0100

    Deduplicate constraint validation logic for supported types
    
    patch by Stefan Miklosovic; reviewed by Bernardo Botella for CASSANDRA-20371
---
 .../constraints/AbstractFunctionConstraint.java    |  4 +--
 .../cql3/constraints/ColumnConstraint.java         | 25 +++++++++++++-
 .../cql3/constraints/ColumnConstraints.java        |  6 ++++
 .../cql3/constraints/ConstraintFunction.java       | 20 +++++++----
 .../cql3/constraints/FunctionColumnConstraint.java | 31 +++++++++++------
 .../cassandra/cql3/constraints/JsonConstraint.java | 13 ++++++--
 .../cql3/constraints/LengthConstraint.java         | 39 ++++++----------------
 .../cql3/constraints/NotNullConstraint.java        |  7 ++++
 .../cql3/constraints/ScalarColumnConstraint.java   | 31 ++++++++++++++---
 .../constraints/UnaryFunctionColumnConstraint.java | 13 ++++++--
 ...TableWithColumnCqlConstraintValidationTest.java |  2 +-
 .../cassandra/contraints/JsonConstraintTest.java   |  4 ++-
 12 files changed, 136 insertions(+), 59 deletions(-)

diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/AbstractFunctionConstraint.java
 
b/src/java/org/apache/cassandra/cql3/constraints/AbstractFunctionConstraint.java
index bbfe58fb26..6204b96a21 100644
--- 
a/src/java/org/apache/cassandra/cql3/constraints/AbstractFunctionConstraint.java
+++ 
b/src/java/org/apache/cassandra/cql3/constraints/AbstractFunctionConstraint.java
@@ -18,7 +18,7 @@
 
 package org.apache.cassandra.cql3.constraints;
 
-import java.util.Set;
+import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.CqlBuilder;
@@ -47,7 +47,7 @@ public abstract class AbstractFunctionConstraint<T> extends 
ColumnConstraint<T>
         return term;
     }
 
-    public abstract Set<Operator> getSupportedOperators();
+    public abstract List<Operator> getSupportedOperators();
 
     @Override
     public void appendCqlTo(CqlBuilder builder)
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraint.java 
b/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraint.java
index 9733fccdcc..ddcca653ea 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraint.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraint.java
@@ -31,6 +31,8 @@ import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.schema.ColumnMetadata;
 import org.apache.cassandra.tcm.serialization.MetadataSerializer;
 
+import static java.lang.String.format;
+
 /**
  * Common class for the conditions that a CQL Constraint needs to implement to 
be integrated in the
  * CQL Constraints framework, with T as a constraint serializer.
@@ -52,7 +54,7 @@ public abstract class ColumnConstraint<T>
         // We are serializing its enum position instead of its name.
         // Changing this enum would affect how that int is interpreted when 
deserializing.
         COMPOSED(ColumnConstraints.serializer, new DuplicatesChecker()),
-        FUNCTION(FunctionColumnConstraint.serializer, 
FunctionColumnConstraint.Functions.values()),
+        FUNCTION(FunctionColumnConstraint.serializer, 
FunctionColumnConstraint.getSatisfiabilityCheckers()),
         SCALAR(ScalarColumnConstraint.serializer, new 
ScalarColumnConstraintSatisfiabilityChecker()),
         UNARY_FUNCTION(UnaryFunctionColumnConstraint.serializer, 
UnaryFunctionColumnConstraint.Functions.values());
 
@@ -140,4 +142,25 @@ public abstract class ColumnConstraint<T>
      * @return the Constraint type serializer
      */
     public abstract ConstraintType getConstraintType();
+
+
+    /**
+     * Tells what types of columns are supported by this constraint.
+     * Returning empty list or null means that all types are supported.
+     *
+     * @return supported types for given constraint
+     */
+    public abstract List<AbstractType<?>> getSupportedTypes();
+
+    protected void validateTypes(ColumnMetadata columnMetadata)
+    {
+        if (getSupportedTypes() == null || getSupportedTypes().isEmpty())
+            return;
+
+        if (!getSupportedTypes().contains(columnMetadata.type.unwrap()))
+            throw new InvalidConstraintDefinitionException(format("Constraint 
'%s' can be used only for columns of type %s but it was %s",
+                                                                  name(),
+                                                                  
getSupportedTypes(),
+                                                                  
columnMetadata.type.getClass()));
+    }
 }
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraints.java 
b/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraints.java
index 02571976e1..0acace098f 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraints.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/ColumnConstraints.java
@@ -140,6 +140,12 @@ public class ColumnConstraints extends 
ColumnConstraint<ColumnConstraints>
         return ConstraintType.COMPOSED;
     }
 
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return null;
+    }
+
     public static class DuplicatesChecker implements SatisfiabilityChecker
     {
         @Override
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java 
b/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java
index bdd36d1181..a95a4f782d 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java
@@ -19,7 +19,7 @@
 package org.apache.cassandra.cql3.constraints;
 
 import java.nio.ByteBuffer;
-import java.util.Set;
+import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.Operator;
@@ -38,7 +38,7 @@ import static org.apache.cassandra.cql3.Operator.NEQ;
  */
 public abstract class ConstraintFunction
 {
-    public static final Set<Operator> DEFAULT_FUNCTION_OPERATORS = Set.of(EQ, 
NEQ, GTE, GT, LTE, LT);
+    public static final List<Operator> DEFAULT_FUNCTION_OPERATORS = 
List.of(EQ, NEQ, GTE, GT, LTE, LT);
 
     protected final ColumnIdentifier columnName;
     protected final String name;
@@ -82,12 +82,20 @@ public abstract class ConstraintFunction
     public abstract void validate(ColumnMetadata columnMetadata) throws 
InvalidConstraintDefinitionException;
 
     /**
-     * Return operators this function supports. By default, it returns an 
empty set, modelling unary function.
+     * Return operators this function supports. By default, it returns an 
empty list, modelling unary function.
      *
-     * @return set of operators this function is allowed to have.
+     * @return list of operators this function is allowed to have.
      */
-    public Set<Operator> getSupportedOperators()
+    public List<Operator> getSupportedOperators()
     {
-        return Set.of();
+        return List.of();
     }
+
+    /**
+     * Tells what types of columns are supported by this constraint.
+     * Returning null or empty list means that all types are supported.
+     *
+     * @return supported types for given constraint
+     */
+    public abstract List<AbstractType<?>> getSupportedTypes();
 }
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java 
b/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java
index 02f49986b5..2383f4ee9c 100644
--- 
a/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java
+++ 
b/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java
@@ -21,7 +21,6 @@ package org.apache.cassandra.cql3.constraints;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Function;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
@@ -64,16 +63,21 @@ public class FunctionColumnConstraint extends 
AbstractFunctionConstraint<Functio
         }
     }
 
-    public enum Functions implements SatisfiabilityChecker
+    public static SatisfiabilityChecker[] getSatisfiabilityCheckers()
     {
-        LENGTH(LengthConstraint::new)
+        SatisfiabilityChecker[] satisfiabilityCheckers = new 
SatisfiabilityChecker[Functions.values().length];
+        for (int i = 0; i < Functions.values().length; i++)
         {
-            @Override
-            public void checkSatisfiability(List<ColumnConstraint<?>> 
constraints, ColumnMetadata columnMetadata)
-            {
-                FUNCTION_SATISFIABILITY_CHECKER.check(name(), constraints, 
columnMetadata);
-            }
-        };
+            String name = Functions.values()[i].name();
+            satisfiabilityCheckers[i] = (constraints, columnMetadata) -> 
FUNCTION_SATISFIABILITY_CHECKER.check(name, constraints, columnMetadata);
+        }
+
+        return satisfiabilityCheckers;
+    }
+
+    public enum Functions
+    {
+        LENGTH(LengthConstraint::new);
 
         private final Function<ColumnIdentifier, ConstraintFunction> 
functionCreator;
 
@@ -100,11 +104,17 @@ public class FunctionColumnConstraint extends 
AbstractFunctionConstraint<Functio
     }
 
     @Override
-    public Set<Operator> getSupportedOperators()
+    public List<Operator> getSupportedOperators()
     {
         return function.getSupportedOperators();
     }
 
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return function.getSupportedTypes();
+    }
+
     @Override
     public String name()
     {
@@ -145,6 +155,7 @@ public class FunctionColumnConstraint extends 
AbstractFunctionConstraint<Functio
     public void validate(ColumnMetadata columnMetadata)
     {
         validateArgs(columnMetadata);
+        validateTypes(columnMetadata);
         function.validate(columnMetadata);
     }
 
diff --git a/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java 
b/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java
index 0abd1d511e..62c9617437 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java
@@ -19,10 +19,13 @@
 package org.apache.cassandra.cql3.constraints;
 
 import java.nio.ByteBuffer;
+import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.Operator;
 import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.UTF8Type;
 import org.apache.cassandra.schema.ColumnMetadata;
 import org.apache.cassandra.serializers.MarshalException;
 import org.apache.cassandra.utils.JsonUtils;
@@ -31,6 +34,8 @@ import static java.lang.String.format;
 
 public class JsonConstraint extends ConstraintFunction
 {
+    private static final List<AbstractType<?>> SUPPORTED_TYPES = 
List.of(UTF8Type.instance, AsciiType.instance);
+
     public static final String FUNCTION_NAME = "JSON";
 
     public JsonConstraint(ColumnIdentifier columnName)
@@ -61,8 +66,12 @@ public class JsonConstraint extends ConstraintFunction
     @Override
     public void validate(ColumnMetadata columnMetadata) throws 
InvalidConstraintDefinitionException
     {
-        if (!columnMetadata.type.unwrap().isString())
-            throw new InvalidConstraintDefinitionException(name + " can be 
used only for columns of 'text', 'varchar' or 'ascii' types.");
+    }
+
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return SUPPORTED_TYPES;
     }
 
     @Override
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java 
b/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java
index 708028a6ea..5b5d8c97dd 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java
@@ -19,7 +19,7 @@
 package org.apache.cassandra.cql3.constraints;
 
 import java.nio.ByteBuffer;
-import java.util.Set;
+import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.Operator;
@@ -34,7 +34,7 @@ import org.apache.cassandra.utils.ByteBufferUtil;
 public class LengthConstraint extends ConstraintFunction
 {
     private static final String NAME = "LENGTH";
-    private static final AbstractType<?>[] SUPPORTED_TYPES = new 
AbstractType[]{ BytesType.instance, UTF8Type.instance, AsciiType.instance };
+    private static final List<AbstractType<?>> SUPPORTED_TYPES = 
List.of(BytesType.instance, UTF8Type.instance, AsciiType.instance);
 
     public LengthConstraint(ColumnIdentifier columnName)
     {
@@ -57,45 +57,28 @@ public class LengthConstraint extends ConstraintFunction
     }
 
     @Override
-    public void validate(ColumnMetadata columnMetadata)
+    public void validate(ColumnMetadata columnMetadata) throws 
InvalidConstraintDefinitionException
     {
-        boolean supported = false;
-        AbstractType<?> unwrapped = columnMetadata.type.unwrap();
-        for (AbstractType<?> supportedType : SUPPORTED_TYPES)
-        {
-            if (supportedType == unwrapped)
-            {
-                supported = true;
-                break;
-            }
-        }
-
-        if (!supported)
-            throw invalidConstraintDefinitionException(columnMetadata.type);
     }
 
     @Override
-    public Set<Operator> getSupportedOperators()
+    public List<Operator> getSupportedOperators()
     {
         return DEFAULT_FUNCTION_OPERATORS;
     }
 
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return SUPPORTED_TYPES;
+    }
+
     private int getValueLength(ByteBuffer value, AbstractType<?> valueType)
     {
         if (valueType.getClass() == BytesType.class)
-        {
             return value.remaining();
-        }
-
-        if (valueType.getClass() == AsciiType.class || valueType.getClass() == 
UTF8Type.class)
+        else
             return ((String) valueType.compose(value)).length();
-
-        throw invalidConstraintDefinitionException(valueType);
-    }
-
-    private InvalidConstraintDefinitionException 
invalidConstraintDefinitionException(AbstractType<?> valueType)
-    {
-        throw new InvalidConstraintDefinitionException("Column type " + 
valueType.getClass() + " is not supported.");
     }
 
     @Override
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/NotNullConstraint.java 
b/src/java/org/apache/cassandra/cql3/constraints/NotNullConstraint.java
index 0fb164562b..465db80933 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/NotNullConstraint.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/NotNullConstraint.java
@@ -19,6 +19,7 @@
 package org.apache.cassandra.cql3.constraints;
 
 import java.nio.ByteBuffer;
+import java.util.List;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.Operator;
@@ -57,6 +58,12 @@ public class NotNullConstraint extends ConstraintFunction
                                                                   
columnMetadata.name));
     }
 
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return null;
+    }
+
     @Override
     public boolean equals(Object o)
     {
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/ScalarColumnConstraint.java 
b/src/java/org/apache/cassandra/cql3/constraints/ScalarColumnConstraint.java
index cd35589454..70b33a7569 100644
--- a/src/java/org/apache/cassandra/cql3/constraints/ScalarColumnConstraint.java
+++ b/src/java/org/apache/cassandra/cql3/constraints/ScalarColumnConstraint.java
@@ -21,12 +21,22 @@ package org.apache.cassandra.cql3.constraints;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
-import java.util.Set;
+
+import com.google.common.annotations.VisibleForTesting;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.cql3.Operator;
 import org.apache.cassandra.db.TypeSizes;
 import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.ShortType;
 import org.apache.cassandra.io.util.DataInputPlus;
 import org.apache.cassandra.io.util.DataOutputPlus;
 import org.apache.cassandra.schema.ColumnMetadata;
@@ -43,7 +53,13 @@ import static 
org.apache.cassandra.cql3.constraints.AbstractFunctionSatisfiabili
 
 public class ScalarColumnConstraint extends 
AbstractFunctionConstraint<ScalarColumnConstraint>
 {
-    public static final Set<Operator> SUPPORTED_OPERATORS = Set.of(EQ, NEQ, 
GTE, GT, LTE, LT);
+    private static final List<AbstractType<?>> SUPPORTED_TYPES =
+    List.of(ByteType.instance, CounterColumnType.instance, 
DecimalType.instance, DoubleType.instance,
+            FloatType.instance, Int32Type.instance, IntegerType.instance, 
LongType.instance,
+            ShortType.instance);
+
+    @VisibleForTesting
+    public static final List<Operator> SUPPORTED_OPERATORS = List.of(EQ, NEQ, 
GTE, GT, LTE, LT);
 
     public static final Serializer serializer = new Serializer();
 
@@ -81,11 +97,17 @@ public class ScalarColumnConstraint extends 
AbstractFunctionConstraint<ScalarCol
     }
 
     @Override
-    public Set<Operator> getSupportedOperators()
+    public List<Operator> getSupportedOperators()
     {
         return SUPPORTED_OPERATORS;
     }
 
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return SUPPORTED_TYPES;
+    }
+
     @Override
     protected void internalEvaluate(AbstractType<?> valueType, ByteBuffer 
columnValue)
     {
@@ -107,8 +129,7 @@ public class ScalarColumnConstraint extends 
AbstractFunctionConstraint<ScalarCol
     @Override
     public void validate(ColumnMetadata columnMetadata) throws 
InvalidConstraintDefinitionException
     {
-        if (!columnMetadata.type.isNumber())
-            throw new InvalidConstraintDefinitionException("Column '" + 
columnName + "' is not a number type.");
+        validateTypes(columnMetadata);
     }
 
     @Override
diff --git 
a/src/java/org/apache/cassandra/cql3/constraints/UnaryFunctionColumnConstraint.java
 
b/src/java/org/apache/cassandra/cql3/constraints/UnaryFunctionColumnConstraint.java
index bb41f57c23..d39f7f865d 100644
--- 
a/src/java/org/apache/cassandra/cql3/constraints/UnaryFunctionColumnConstraint.java
+++ 
b/src/java/org/apache/cassandra/cql3/constraints/UnaryFunctionColumnConstraint.java
@@ -20,7 +20,7 @@ package org.apache.cassandra.cql3.constraints;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.util.Set;
+import java.util.List;
 import java.util.function.Function;
 
 import org.apache.cassandra.cql3.ColumnIdentifier;
@@ -96,9 +96,15 @@ public class UnaryFunctionColumnConstraint extends 
AbstractFunctionConstraint<Un
     }
 
     @Override
-    public Set<Operator> getSupportedOperators()
+    public List<Operator> getSupportedOperators()
     {
-        return Set.of();
+        return List.of();
+    }
+
+    @Override
+    public List<AbstractType<?>> getSupportedTypes()
+    {
+        return function.getSupportedTypes();
     }
 
     @Override
@@ -117,6 +123,7 @@ public class UnaryFunctionColumnConstraint extends 
AbstractFunctionConstraint<Un
     public void validate(ColumnMetadata columnMetadata) throws 
InvalidConstraintDefinitionException
     {
         validateArgs(columnMetadata);
+        validateTypes(columnMetadata);
         function.validate(columnMetadata);
     }
 
diff --git 
a/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnCqlConstraintValidationTest.java
 
b/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnCqlConstraintValidationTest.java
index b92f7b1c41..17c093c9f4 100644
--- 
a/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnCqlConstraintValidationTest.java
+++ 
b/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnCqlConstraintValidationTest.java
@@ -1372,7 +1372,7 @@ public class 
CreateTableWithColumnCqlConstraintValidationTest extends CqlConstra
         catch (InvalidRequestException e)
         {
             assertTrue(e.getCause() instanceof InvalidRequestException);
-            assertTrue(e.getCause().getMessage().equals("Column 'pk' is not a 
number type."));
+            assertTrue(e.getCause().getMessage().contains("can be used only 
for columns of type"));
             assertTrue(e.getMessage().contains("Error setting schema for 
test"));
         }
     }
diff --git a/test/unit/org/apache/cassandra/contraints/JsonConstraintTest.java 
b/test/unit/org/apache/cassandra/contraints/JsonConstraintTest.java
index ba92812230..5a436892af 100644
--- a/test/unit/org/apache/cassandra/contraints/JsonConstraintTest.java
+++ b/test/unit/org/apache/cassandra/contraints/JsonConstraintTest.java
@@ -57,7 +57,9 @@ public class JsonConstraintTest
     public void testInvalidTypes()
     {
         assertThatThrownBy(() -> 
json.validate(getColumnOfType(IntegerType.instance)))
-        .hasMessageContaining("JSON can be used only for columns of 'text', 
'varchar' or 'ascii' types.");
+        .hasMessage("Constraint 'JSON' can be used only for columns of type " +
+                    "[org.apache.cassandra.db.marshal.UTF8Type, 
org.apache.cassandra.db.marshal.AsciiType] " +
+                    "but it was class 
org.apache.cassandra.db.marshal.IntegerType");
     }
 
     private void run(String jsonToCheck) throws Throwable


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to