Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use set instead of list #73

Open
wants to merge 4 commits into
base: CASSANDRA-20371
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,416 changes: 1,214 additions & 202 deletions .circleci/config.yml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -31,6 +31,8 @@
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 enum ConstraintType
// 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,27 @@ public void evaluate(AbstractType<?> valueType, ByteBuffer columnValue) throws C
* @return the Constraint type serializer
*/
public abstract ConstraintType getConstraintType();


/**
* Tells what types of columns are supported by this constraint.
* Returning empty list 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;

boolean supported = getSupportedTypes().contains(columnMetadata.type.unwrap());

if (!supported)
throw new InvalidConstraintDefinitionException(format("Constraint '%s' can be used only for columns of type %s but it was %s",
name(),
getSupportedTypes(),
columnMetadata.type.getClass()));
}
}
Original file line number Diff line number Diff line change
@@ -140,6 +140,12 @@ public ConstraintType getConstraintType()
return ConstraintType.COMPOSED;
}

@Override
public List<AbstractType<?>> getSupportedTypes()
{
return null;
}

public static class DuplicatesChecker implements SatisfiabilityChecker
{
@Override
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@
package org.apache.cassandra.cql3.constraints;

import java.nio.ByteBuffer;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.cassandra.cql3.ColumnIdentifier;
@@ -38,7 +40,7 @@
*/
public abstract class ConstraintFunction
{
public static final Set<Operator> DEFAULT_FUNCTION_OPERATORS = Set.of(EQ, NEQ, GTE, GT, LTE, LT);
public static final Set<Operator> DEFAULT_FUNCTION_OPERATORS = new LinkedHashSet<>(List.of(EQ, NEQ, GTE, GT, LTE, LT));

protected final ColumnIdentifier columnName;
protected final String name;
@@ -82,12 +84,20 @@ public void evaluate(AbstractType<?> valueType, ByteBuffer columnValue) throws C
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.
*/
public Set<Operator> getSupportedOperators()
{
return Set.of();
return new LinkedHashSet<>();
}

/**
* 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();
}
Original file line number Diff line number Diff line change
@@ -64,16 +64,21 @@ public FunctionColumnConstraint prepare()
}
}

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;

@@ -105,6 +110,12 @@ public Set<Operator> getSupportedOperators()
return function.getSupportedOperators();
}

@Override
public List<AbstractType<?>> getSupportedTypes()
{
return function.getSupportedTypes();
}

@Override
public String name()
{
@@ -145,6 +156,7 @@ protected void internalEvaluate(AbstractType<?> valueType, ByteBuffer columnValu
public void validate(ColumnMetadata columnMetadata)
{
validateArgs(columnMetadata);
validateTypes(columnMetadata);
function.validate(columnMetadata);
}

13 changes: 11 additions & 2 deletions src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java
Original file line number Diff line number Diff line change
@@ -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 @@

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 void internalEvaluate(AbstractType<?> valueType, Operator relationType, S
@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
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
package org.apache.cassandra.cql3.constraints;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Set;

import org.apache.cassandra.cql3.ColumnIdentifier;
@@ -34,7 +35,7 @@
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,21 +58,8 @@ public void internalEvaluate(AbstractType<?> valueType, Operator relationType, S
}

@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
@@ -80,22 +68,18 @@ public Set<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
Original file line number Diff line number Diff line change
@@ -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 void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefi
columnMetadata.name));
}

@Override
public List<AbstractType<?>> getSupportedTypes()
{
return null;
}

@Override
public boolean equals(Object o)
{
Original file line number Diff line number Diff line change
@@ -20,13 +20,25 @@

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedHashSet;
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 +55,13 @@

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 Set<Operator> SUPPORTED_OPERATORS = new LinkedHashSet<>(List.of(EQ, NEQ, GTE, GT, LTE, LT));

public static final Serializer serializer = new Serializer();

@@ -86,6 +104,12 @@ public Set<Operator> getSupportedOperators()
return SUPPORTED_OPERATORS;
}

@Override
public List<AbstractType<?>> getSupportedTypes()
{
return SUPPORTED_TYPES;
}

@Override
protected void internalEvaluate(AbstractType<?> valueType, ByteBuffer columnValue)
{
@@ -107,8 +131,7 @@ protected void internalEvaluate(AbstractType<?> valueType, ByteBuffer columnValu
@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
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

@@ -98,7 +100,13 @@ public MetadataSerializer<UnaryFunctionColumnConstraint> serializer()
@Override
public Set<Operator> getSupportedOperators()
{
return Set.of();
return new LinkedHashSet<>();
}

@Override
public List<AbstractType<?>> getSupportedTypes()
{
return function.getSupportedTypes();
}

@Override
@@ -117,6 +125,7 @@ public void internalEvaluate(AbstractType<?> valueType, ByteBuffer columnValue)
public void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefinitionException
{
validateArgs(columnMetadata);
validateTypes(columnMetadata);
function.validate(columnMetadata);
}

Original file line number Diff line number Diff line change
@@ -1372,7 +1372,7 @@ public void testCreateTableWithColumnWithClusteringColumnInvalidScalarTypeConstr
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"));
}
}
Original file line number Diff line number Diff line change
@@ -57,7 +57,9 @@ public void testJsonConstraint() throws Throwable
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