Add OCTET_LENGTH constraint

patch by Bernardo Botella; reviewed by Stefan Miklosovic for CASSANDRA-20340
This commit is contained in:
Bernardo Botella Corbi 2025-02-17 15:44:03 -08:00
parent fa3542da9c
commit 58b7ebfa4b
8 changed files with 680 additions and 14 deletions

View File

@ -1,4 +1,5 @@
5.1
* Add OCTET_LENGTH constraint (CASSANDRA-20340)
* Reduce memory allocations in miscellaneous places along the hot write path (CASSANDRA-20167)
* Provide keystore_password_file and truststore_password_file options to read credentials from a file (CASSANDRA-13428)
* Unregistering a node should also remove it from tokenMap if it is there and recalculate the placements (CASSANDRA-20346)

View File

@ -92,6 +92,32 @@ Finally, the constraint can be removed:
ALTER TABLE keyspace.table ALTER name DROP CHECK;
----
=== OCTET_LENGTH CONSTRAINT
Defines a condition that checks the size in bytes of text or binary type.
For example, we can create a constraint that checks that name can't be bigger than 256 characters:
----
CREATE TABLE keyspace.table (
name text CHECK OCTET_LENGTH(name) < 2
...,
);
----
Inserting a valid row:
----
INSERT INTO keyspace.table (name) VALUES ("f")
----
Inserting an invalid row:
----
INSERT INTO keyspace.table (name) VALUES ("fooooooo")
ERROR: Column value does not satisfy value constraint for column 'name'. It has a length of 8 and
and it should be should be < 2
----
=== NOT_NULL constraint
Defines a constraint that checks if a column is not null in every modification statement.

View File

@ -79,7 +79,9 @@ public abstract class ConstraintFunction
* Method that validates that a condition is valid. This method is called when the CQL constraint is created to determine
* if the CQL statement is valid or needs to be rejected as invalid throwing a {@link InvalidConstraintDefinitionException}
*/
public abstract void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefinitionException;
public void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefinitionException
{
}
/**
* Return operators this function supports. By default, it returns an empty list, modelling unary function.

View File

@ -77,7 +77,8 @@ public class FunctionColumnConstraint extends AbstractFunctionConstraint<Functio
public enum Functions
{
LENGTH(LengthConstraint::new);
LENGTH(LengthConstraint::new),
OCTET_LENGTH(OctetLengthConstraint::new);
private final Function<ColumnIdentifier, ConstraintFunction> functionCreator;

View File

@ -26,7 +26,6 @@ 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;
@ -63,11 +62,6 @@ public class JsonConstraint extends ConstraintFunction
}
}
@Override
public void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefinitionException
{
}
@Override
public List<AbstractType<?>> getSupportedTypes()
{

View File

@ -28,7 +28,6 @@ import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
public class LengthConstraint extends ConstraintFunction
@ -56,11 +55,6 @@ public class LengthConstraint extends ConstraintFunction
+ relationType + ' ' + term);
}
@Override
public void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefinitionException
{
}
@Override
public List<Operator> getSupportedOperators()
{

View File

@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.BytesType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.utils.ByteBufferUtil;
public class OctetLengthConstraint extends ConstraintFunction
{
private static final List<AbstractType<?>> SUPPORTED_TYPES = List.of(BytesType.instance, UTF8Type.instance, AsciiType.instance);
public OctetLengthConstraint(ColumnIdentifier columnName)
{
super(columnName, "OCTET_LENGTH");
}
@Override
public void internalEvaluate(AbstractType<?> valueType, Operator relationType, String term, ByteBuffer columnValue)
{
int valueLength = columnValue.remaining();
int sizeConstraint = Integer.parseInt(term);
ByteBuffer leftOperand = ByteBufferUtil.bytes(valueLength);
ByteBuffer rightOperand = ByteBufferUtil.bytes(sizeConstraint);
if (!relationType.isSatisfiedBy(Int32Type.instance, leftOperand, rightOperand))
throw new ConstraintViolationException("Column value does not satisfy value constraint for column '" + columnName + "'. "
+ "It has a length of " + valueLength + " and it should be should be "
+ relationType + ' ' + term);
}
@Override
public List<Operator> getSupportedOperators()
{
return DEFAULT_FUNCTION_OPERATORS;
}
@Override
public List<AbstractType<?>> getSupportedTypes()
{
return SUPPORTED_TYPES;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof OctetLengthConstraint))
return false;
OctetLengthConstraint other = (OctetLengthConstraint) o;
return columnName.equals(other.columnName);
}
}

View File

@ -0,0 +1,566 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.contraints;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.utils.Generators;
import static accord.utils.Property.qt;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.quicktheories.generators.SourceDSL.integers;
@RunWith(Parameterized.class)
public class CreateTableWithColumnOctetLengthConstraintValidationTest extends CqlConstraintValidationTester
{
@Parameterized.Parameter
public String order;
@Parameterized.Parameters()
public static Collection<Object[]> generateData()
{
return Arrays.asList(new Object[][]{
{ "ASC" },
{ "DESC" }
});
}
@Test
public void testCreateTableWithColumnWithClusteringColumnSerializedSizeEqualToConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 text CHECK OCTET_LENGTH(ck1) = 4, ck2 int, v int, PRIMARY KEY ((pk), ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fooo', 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fño', 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'foo', 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fooñ', 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'foooo', 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringColumnSerializedSizeDifferentThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 text CHECK OCTET_LENGTH(ck1) != 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñ', 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñoo', 2, 3)");
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fño', 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringColumnSerializedSizeBiggerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 text CHECK OCTET_LENGTH(ck1) > 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñoo', 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñ', 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fño', 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringColumnSerializedSizeBiggerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 text CHECK OCTET_LENGTH(ck1) >= 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñoo', 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fño', 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñ', 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringColumnSerializedSizeSmallerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 text CHECK OCTET_LENGTH(ck1) < 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñ', 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fño', 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñoo', 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringColumnSerializedSizeSmallerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 text CHECK OCTET_LENGTH(ck1) <= 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñ', 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fño', 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 'fñoo', 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringBlobColumnSerializedSizeEqualToConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 blob CHECK OCTET_LENGTH(ck1) = 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fño'), 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñ'), 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñoo'), 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringBlobColumnSerializedSizeDifferentThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 blob CHECK OCTET_LENGTH(ck1) != 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñ'), 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñoo'), 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fño'), 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringBlobColumnSerializedSizeBiggerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 blob CHECK OCTET_LENGTH(ck1) > 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñoo'), 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñ'), 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fño'), 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringBlobColumnSerializedSizeBiggerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 blob CHECK OCTET_LENGTH(ck1) >= 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñoo'), 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fño'), 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñ'), 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringBlobColumnSerializedSizeSmallerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 blob CHECK OCTET_LENGTH(ck1) < 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñ'), 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fño'), 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñoo'), 2, 3)");
}
@Test
public void testCreateTableWithColumnWithClusteringBlobColumnSerializedSizeSmallerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 blob CHECK OCTET_LENGTH(ck1) <= 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñ'), 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fño'), 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'ck1'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, textAsBlob('fñoo'), 2, 3)");
}
@Test
public void testCreateTableWithColumnWithPkColumnSerializedSizeEqualToConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) = 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 1, 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 1, 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 1, 2, 3)");
}
@Test
public void testCreateTableWithColumnWithPkColumnSerializedSizeDifferentThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) != 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 1, 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 1, 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 1, 2, 3)");
}
@Test
public void testCreateTableWithColumnWithPkColumnSerializedSizeBiggerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) > 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 1, 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 1, 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 1, 2, 3)");
}
@Test
public void testCreateTableWithColumnWithPkColumnSerializedSizeBiggerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) >= 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 1, 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 1, 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 1, 2, 3)");
}
@Test
public void testCreateTableWithColumnWithPkColumnSerializedSizeSmallerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) < 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 1, 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 1, 2, 3)");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 1, 2, 3)");
}
@Test
public void testCreateTableWithColumnWithPkColumnSerializedSizeSmallerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) <= 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 1, 2, 3)");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 1, 2, 3)");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 1, 2, 3)");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeEqualToConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) = 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fño')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñ')");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñoo')");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeDifferentThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) != 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñ')");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñoo')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fño')");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeBiggerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) > 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñoo')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñ')");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fño')");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeBiggerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) >= 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñoo')");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fño')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñ')");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeSmallerThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) < 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñ')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fño')");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñoo')");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeSmallerOrEqualThanConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) <= 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñ')");
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fño')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, 'fñoo')");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeCheckNullTextConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) <= 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v' as it is null.";
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, null)");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeCheckNullVarcharConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v varchar CHECK OCTET_LENGTH(v) <= 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v' as it is null.";
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, null)");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeCheckNullAsciiConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v ascii CHECK OCTET_LENGTH(v) <= 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v' as it is null.";
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, null)");
}
@Test
public void testCreateTableWithColumnWithRegularColumnSerializedSizeCheckNullBlobConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk int, ck1 int, ck2 int, v blob CHECK OCTET_LENGTH(v) <= 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'v' as it is null.";
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES (1, 2, 3, null)");
}
@Test
public void testCreateTableWithColumnMixedColumnsSerializedSizeConstraint() throws Throwable
{
createTable("CREATE TABLE %s (pk text CHECK OCTET_LENGTH(pk) = 4, ck1 int, ck2 int, v text CHECK OCTET_LENGTH(v) = 4, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
// Valid
execute("INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 2, 3, 'fño')");
final String expectedErrorMessage = "Column value does not satisfy value constraint for column 'pk'. It has a length of";
final String expectedErrorMessage2 = "Column value does not satisfy value constraint for column 'v'. It has a length of";
// Invalid
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 2, 3, 'fñ')");
assertInvalidThrowMessage(expectedErrorMessage2, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 2, 3, 'fñ')");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñ', 2, 3, 'fño')");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 2, 3, 'fño')");
assertInvalidThrowMessage(expectedErrorMessage2, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fño', 2, 3, 'fñoo')");
assertInvalidThrowMessage(expectedErrorMessage, InvalidRequestException.class, "INSERT INTO %s (pk, ck1, ck2, v) VALUES ('fñoo', 2, 3, 'fñoo')");
}
@Test
public void testCreateTableWithWrongColumnConstraint() throws Throwable
{
try
{
createTable("CREATE TABLE %s (pk text, ck1 int CHECK OCTET_LENGTH(pk) = 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
fail();
}
catch (InvalidRequestException e)
{
assertTrue(e.getCause() instanceof InvalidRequestException);
assertTrue(e.getMessage().contains("Error setting schema for test"));
}
}
@Test
public void testCreateTableWithWrongColumnMultipleConstraint() throws Throwable
{
try
{
createTable("CREATE TABLE %s (pk text, ck1 int CHECK OCTET_LENGTH(pk) = 4 AND ck1 < 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
fail();
}
catch (InvalidRequestException e)
{
assertTrue(e.getCause() instanceof InvalidRequestException);
assertTrue(e.getMessage().contains("Error setting schema for test"));
}
}
@Test
public void testCreateTableWithColumnWithClusteringColumnInvalidTypeConstraint() throws Throwable
{
try
{
createTable("CREATE TABLE %s (pk int, ck1 int CHECK OCTET_LENGTH(ck1) = 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
fail();
}
catch (InvalidRequestException e)
{
assertTrue(e.getCause() instanceof InvalidRequestException);
assertTrue(e.getMessage().contains("Error setting schema for test"));
}
}
@Test
public void testCreateTableWithColumnWithClusteringColumnInvalidScalarTypeConstraint() throws Throwable
{
try
{
createTable("CREATE TABLE %s (pk text CHECK pk = 4, ck1 int, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
fail();
}
catch (InvalidRequestException e)
{
assertTrue(e.getCause() instanceof InvalidRequestException);
assertTrue(e.getCause().getMessage().contains("Constraint 'pk =' can be used only for columns of type"));
assertTrue(e.getMessage().contains("Error setting schema for test"));
}
}
@Test
public void testCreateTableInvalidFunction() throws Throwable
{
try
{
createTable("CREATE TABLE %s (pk text CHECK not_a_function(pk) = 4, ck1 int, ck2 int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");");
fail();
}
catch (InvalidRequestException e)
{
assertTrue(e.getCause() instanceof InvalidRequestException);
assertTrue(e.getMessage().contains("Error setting schema for test"));
}
}
@Test
public void testCreateTableWithPKConstraintsAndCDCEnabled() throws Throwable
{
// It works
createTable("CREATE TABLE %s (pk text CHECK length(pk) = 4, ck1 int, ck2 int, PRIMARY KEY ((pk), ck1, ck2)) WITH cdc = true;");
}
@Test
public void testCreateTableWithClusteringConstraintsAndCDCEnabled() throws Throwable
{
// It works
createTable("CREATE TABLE %s (pk text, ck1 int CHECK ck1 < 100, ck2 int, PRIMARY KEY ((pk), ck1, ck2)) WITH cdc = true;");
}
@Test
public void testCreateTableWithRegularConstraintsAndCDCEnabled() throws Throwable
{
// It works
createTable("CREATE TABLE %s (pk text, ck1 int CHECK ck1 < 100, ck2 int, PRIMARY KEY (pk)) WITH cdc = true;");
}
// Copy table with like
@Test
public void testCreateTableWithColumnWithClusteringColumnLessThanScalarConstraintIntegerOnLikeTable() throws Throwable
{
createTable(KEYSPACE, "CREATE TABLE %s (pk int, ck1 int CHECK ck1 < 4, ck2 int, v int, PRIMARY KEY ((pk),ck1, ck2)) WITH CLUSTERING ORDER BY (ck1 " + order + ");", "liketabletame");
execute("create table " + KEYSPACE + ".tb_copy like %s");
// Valid
qt().forAll(Generators.toGen(integers().between(0, 3)))
.check(d -> execute("INSERT INTO " + KEYSPACE + ".tb_copy (pk, ck1, ck2, v) VALUES (1, " + d + ", 3, 4)"));
// Invalid
qt().forAll(Generators.toGen(integers().between(4, 100)))
.check(d -> {
try
{
assertInvalidThrow(InvalidRequestException.class, "INSERT INTO " + KEYSPACE + ".tb_copy(pk, ck1, ck2, v) VALUES (1, " + d + ", 3, 4)");
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
});
}
}