This is an automated email from the ASF dual-hosted git repository. bernardobotella 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 58b7ebfa4b Add OCTET_LENGTH constraint 58b7ebfa4b is described below commit 58b7ebfa4b18092f52ba6ef21efe73084e0ee1f1 Author: Bernardo Botella Corbi <conta...@bernardobotella.com> AuthorDate: Mon Feb 17 15:44:03 2025 -0800 Add OCTET_LENGTH constraint patch by Bernardo Botella; reviewed by Stefan Miklosovic for CASSANDRA-20340 --- CHANGES.txt | 1 + .../pages/developing/cql/constraints.adoc | 26 + .../cql3/constraints/ConstraintFunction.java | 4 +- .../cql3/constraints/FunctionColumnConstraint.java | 3 +- .../cassandra/cql3/constraints/JsonConstraint.java | 6 - .../cql3/constraints/LengthConstraint.java | 6 - ...hConstraint.java => OctetLengthConstraint.java} | 29 +- ...hColumnOctetLengthConstraintValidationTest.java | 566 +++++++++++++++++++++ 8 files changed, 605 insertions(+), 36 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b91aca2cb7..58d702a7b8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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) diff --git a/doc/modules/cassandra/pages/developing/cql/constraints.adoc b/doc/modules/cassandra/pages/developing/cql/constraints.adoc index 0bd3120edc..f768d4fd8f 100644 --- a/doc/modules/cassandra/pages/developing/cql/constraints.adoc +++ b/doc/modules/cassandra/pages/developing/cql/constraints.adoc @@ -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. diff --git a/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java b/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java index a95a4f782d..1dda89093f 100644 --- a/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java +++ b/src/java/org/apache/cassandra/cql3/constraints/ConstraintFunction.java @@ -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. diff --git a/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java b/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java index 2383f4ee9c..dac62ddfc6 100644 --- a/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java +++ b/src/java/org/apache/cassandra/cql3/constraints/FunctionColumnConstraint.java @@ -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; diff --git a/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java b/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java index 62c9617437..99aeb6734e 100644 --- a/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java +++ b/src/java/org/apache/cassandra/cql3/constraints/JsonConstraint.java @@ -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() { diff --git a/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java b/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java index 5b5d8c97dd..49954c28fb 100644 --- a/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java +++ b/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java @@ -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() { diff --git a/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java b/src/java/org/apache/cassandra/cql3/constraints/OctetLengthConstraint.java similarity index 75% copy from src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java copy to src/java/org/apache/cassandra/cql3/constraints/OctetLengthConstraint.java index 5b5d8c97dd..8147d37d62 100644 --- a/src/java/org/apache/cassandra/cql3/constraints/LengthConstraint.java +++ b/src/java/org/apache/cassandra/cql3/constraints/OctetLengthConstraint.java @@ -28,23 +28,21 @@ 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 +public class OctetLengthConstraint extends ConstraintFunction { - private static final String NAME = "LENGTH"; private static final List<AbstractType<?>> SUPPORTED_TYPES = List.of(BytesType.instance, UTF8Type.instance, AsciiType.instance); - public LengthConstraint(ColumnIdentifier columnName) + public OctetLengthConstraint(ColumnIdentifier columnName) { - super(columnName, NAME); + super(columnName, "OCTET_LENGTH"); } @Override public void internalEvaluate(AbstractType<?> valueType, Operator relationType, String term, ByteBuffer columnValue) { - int valueLength = getValueLength(columnValue, valueType); + int valueLength = columnValue.remaining(); int sizeConstraint = Integer.parseInt(term); ByteBuffer leftOperand = ByteBufferUtil.bytes(valueLength); @@ -52,15 +50,10 @@ public class LengthConstraint extends ConstraintFunction 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 " + + "It has a length of " + valueLength + " and it should be should be " + relationType + ' ' + term); } - @Override - public void validate(ColumnMetadata columnMetadata) throws InvalidConstraintDefinitionException - { - } - @Override public List<Operator> getSupportedOperators() { @@ -73,24 +66,16 @@ public class LengthConstraint extends ConstraintFunction return SUPPORTED_TYPES; } - private int getValueLength(ByteBuffer value, AbstractType<?> valueType) - { - if (valueType.getClass() == BytesType.class) - return value.remaining(); - else - return ((String) valueType.compose(value)).length(); - } - @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof LengthConstraint)) + if (!(o instanceof OctetLengthConstraint)) return false; - LengthConstraint other = (LengthConstraint) o; + OctetLengthConstraint other = (OctetLengthConstraint) o; return columnName.equals(other.columnName); } diff --git a/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnOctetLengthConstraintValidationTest.java b/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnOctetLengthConstraintValidationTest.java new file mode 100644 index 0000000000..5c79c957a5 --- /dev/null +++ b/test/unit/org/apache/cassandra/contraints/CreateTableWithColumnOctetLengthConstraintValidationTest.java @@ -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); + } + }); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org