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