yifan-c commented on code in PR #3562:
URL: https://github.com/apache/cassandra/pull/3562#discussion_r1854941745
##########
src/antlr/Parser.g:
##########
@@ -779,12 +779,37 @@ tableDefinition[CreateTableStatement.Raw stmt]
;
tableColumns[CreateTableStatement.Raw stmt]
- @init { boolean isStatic = false; }
- : k=ident v=comparatorType (K_STATIC { isStatic = true; })?
(mask=columnMask)? { $stmt.addColumn(k, v, isStatic, mask); }
+ @init {
+ boolean isStatic = false;
+ List<CqlConstraint.Raw> constraints = new ArrayList<>();
+ }
+ : k=ident v=comparatorType (K_STATIC { isStatic = true; })?
(mask=columnMask)? (constraints=columnConstraints[stmt, constraints])? {
$stmt.addColumn(k, v, isStatic, mask, constraints); }
(K_PRIMARY K_KEY { $stmt.setPartitionKeyColumn(k); })?
| K_PRIMARY K_KEY '(' tablePartitionKey[stmt] (',' c=ident {
$stmt.markClusteringColumn(c); } )* ')'
;
+columnConstraints[CreateTableStatement.Raw stmt, List<CqlConstraint.Raw>
constraints]
+ : K_CHECK kconst=cqlConstraintExp[stmt] { constraints.add(kconst); }
(K_AND kconst=cqlConstraintExp[stmt] { constraints.add(kconst); })*
+ ;
+
+cqlConstraintExp[CreateTableStatement.Raw stmt] returns [CqlConstraint.Raw
cqlConstraint]
Review Comment:
The `cql` feels redundant, since everything in the parser.g is for cql.
It is also not a common prefix used by other definitions in the parser.
I would suggest removing the prefix.
##########
src/antlr/Parser.g:
##########
@@ -779,12 +779,37 @@ tableDefinition[CreateTableStatement.Raw stmt]
;
tableColumns[CreateTableStatement.Raw stmt]
- @init { boolean isStatic = false; }
- : k=ident v=comparatorType (K_STATIC { isStatic = true; })?
(mask=columnMask)? { $stmt.addColumn(k, v, isStatic, mask); }
+ @init {
+ boolean isStatic = false;
+ List<CqlConstraint.Raw> constraints = new ArrayList<>();
Review Comment:
The init of `List<CqlConstraint.Raw> constraints` should be pushed down to
`columnConstraints` definition, the same way as `columnMask`. The
field/modifier is optional. It is unnecessary to be initialized for every table
column.
##########
src/java/org/apache/cassandra/schema/ColumnMetadata.java:
##########
@@ -115,6 +116,9 @@ public boolean isPrimaryKeyKind()
@Nullable
private final ColumnMask mask;
+ @Nullable
+ private List<CqlConstraint> cqlConstraints;
Review Comment:
How about define a dedicated class, `ColumnConstraints`, instead of
introducing a list to `ColumnMetadata`? The related new code in this class can
be encapsulated into `ColumnConstraints`
##########
src/java/org/apache/cassandra/cql3/LengthConstraint.java:
##########
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.cql3.terms.Term;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+public class LengthConstraint implements ConstraintFunction
+{
+ @Override
+ public String getName()
+ {
+ return "LENGTH";
+ }
+
+ @Override
+ public void evaluate(List<ColumnIdentifier> args, Operator relationType,
String term, TableMetadata tableMetadata, Map<String, Term.Raw> columnValues)
+ {
+ ColumnMetadata columnMetadata = tableMetadata.getColumn(args.get(0));
+ if (!columnValues.containsKey(columnMetadata.name.toString()))
+ throw new ConstraintViolationException(columnMetadata.name + " is
not an existing column name.");
+
+ String columnValue =
columnValues.get(columnMetadata.name.toString()).getText();
+ int valueLength = stripColumnValue(columnValue).length();
+ int sizeConstraint = Integer.parseInt(term);
+
+ switch (relationType)
+ {
+ case EQ:
+ if (valueLength != sizeConstraint)
+ throw new ConstraintViolationException(columnMetadata.name
+ " value length should be exactly " + sizeConstraint);
+ break;
+ case NEQ:
+ if (valueLength == sizeConstraint)
+ throw new ConstraintViolationException(columnMetadata.name
+ " value length different than " + sizeConstraint);
+ break;
+ case GT:
+ if (valueLength <= sizeConstraint)
+ throw new ConstraintViolationException(columnMetadata.name
+ " value length should be larger than " + sizeConstraint);
+ break;
+ case LT:
+ if (valueLength >= sizeConstraint)
+ throw new ConstraintViolationException(columnMetadata.name
+ " value length should be smaller than " + sizeConstraint);
+ break;
+ case GTE:
+ if (valueLength < sizeConstraint)
+ throw new ConstraintViolationException(columnMetadata.name
+ " value length should be larger or equal than " + sizeConstraint);
+ break;
+ case LTE:
+ if (valueLength > sizeConstraint)
+ throw new ConstraintViolationException(columnMetadata.name
+ " value length should be smaller or equala than " + sizeConstraint);
+ break;
+ default:
+ throw new ConstraintViolationException("Invalid relation type:
" + relationType);
+ }
+ }
+
+ @Override
+ public void validate(List<ColumnIdentifier> args, Operator relationType,
String term, TableMetadata tableMetadata)
+ {
+ if (args.size() != 1)
+ throw new ConstraintInvalidException("LENGTH requires exactly one
argument");
+
+ ColumnMetadata columnMetadata = tableMetadata.getColumn(args.get(0));
+ if (columnMetadata.type.getClass() != UTF8Type.class &&
columnMetadata.type.getClass() != AsciiType.class)
Review Comment:
I would make sense to support blob too, to get the byte size.
Additionally, you can create a hashset for the supported types.
##########
src/java/org/apache/cassandra/cql3/ColumnConstraints.java:
##########
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// group of constraints for the column
+public class ColumnConstraints implements ColumnConstraint
+{
+
+ public static Serializer serializer = new Serializer();
+
+ private final List<ColumnConstraint> constraints;
+
+ public ColumnConstraints(List<ColumnConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public IVersionedSerializer<ColumnConstraint> getSerializer() {
+ return null;
+ }
+
+ @Override
+ public void appendCqlTo(CqlBuilder builder) {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.appendCqlTo(builder);
+ }
+ }
+
+ @Override
+ public void evaluate(Object columnValue) throws
ConstraintViolationException {
+
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.evaluate(columnValue);
+ }
+ }
+
+ public List<ColumnConstraint> getConstraints()
+ {
+ return constraints;
+ }
+
+ public boolean isEmpty()
+ {
+ return constraints.isEmpty();
+ }
+
+
+ @Override
+ public void validate(ColumnMetadata columnMetadata, TableMetadata
tableMetadata) throws ConstraintInvalidException {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.validate(columnMetadata, tableMetadata);
+ }
+ }
+
+ public static class Noop extends ColumnConstraints
+ {
+ public static Noop INSTANCE = new Noop();
+
+ public Noop() {
+ super(new ArrayList<>());
Review Comment:
Nit: avoid creating new array list instance.
```suggestion
super(Collections.emptyList());
```
##########
src/java/org/apache/cassandra/cql3/ColumnConstraints.java:
##########
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// group of constraints for the column
+public class ColumnConstraints implements ColumnConstraint
+{
+
+ public static Serializer serializer = new Serializer();
+
+ private final List<ColumnConstraint> constraints;
+
+ public ColumnConstraints(List<ColumnConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public IVersionedSerializer<ColumnConstraint> getSerializer() {
+ return null;
+ }
Review Comment:
code style: the left curly brace should be placed in a new line.
##########
src/java/org/apache/cassandra/cql3/LengthConstraint.java:
##########
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+public class LengthConstraint implements ConstraintFunction
+{
+ public static String FUNCTION_NAME = "LENGTH";
+ @Override
Review Comment:
nit: add a new line
##########
src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java:
##########
@@ -103,6 +113,7 @@ public void addUpdateForKey(PartitionUpdate.Builder
updateBuilder, Clustering<?>
for (int i = 0, isize = staticOps.size(); i < isize; i++)
staticOps.get(i).execute(updateBuilder.partitionKey(), params);
updateBuilder.add(params.buildRow());
+ //evaluateConstraints(metadata, columnNames, columnValues);
Review Comment:
Update?
##########
src/antlr/Parser.g:
##########
@@ -945,15 +970,20 @@ alterKeyspaceStatement returns
[AlterKeyspaceStatement.Raw stmt]
* ALTER TABLE [IF EXISTS] <table> WITH <property> = <value>;
*/
alterTableStatement returns [AlterTableStatement.Raw stmt]
- @init { boolean ifExists = false; }
+ @init {
+ boolean ifExists = false;
+ List<CqlConstraint.Raw> columnConstraints = new ArrayList<>();
+ }
: K_ALTER K_COLUMNFAMILY (K_IF K_EXISTS { ifExists = true; } )?
cf=columnFamilyName { $stmt = new AlterTableStatement.Raw(cf, ifExists);
}
(
K_ALTER id=cident K_TYPE v=comparatorType { $stmt.alter(id, v); }
| K_ALTER ( K_IF K_EXISTS { $stmt.ifColumnExists(true); } )? id=cident
( mask=columnMask { $stmt.mask(id, mask); }
- | K_DROP K_MASKED { $stmt.mask(id, null); } )
+ | K_DROP K_MASKED { $stmt.mask(id, null); }
+ | K_DROP K_CHECK { $stmt.dropConstraint(id); }
+ | K_CHECK kconst=alterCqlConstraintExp[stmt] {
columnConstraints.add(kconst); } (K_AND kconst=alterCqlConstraintExp[stmt] {
columnConstraints.add(kconst); })* { $stmt.alterConstraints(id,
columnConstraints); })
Review Comment:
Can you follow `mask=columnMask` for alter column constraints?
##########
src/antlr/Parser.g:
##########
@@ -945,15 +970,20 @@ alterKeyspaceStatement returns
[AlterKeyspaceStatement.Raw stmt]
* ALTER TABLE [IF EXISTS] <table> WITH <property> = <value>;
*/
alterTableStatement returns [AlterTableStatement.Raw stmt]
- @init { boolean ifExists = false; }
+ @init {
+ boolean ifExists = false;
+ List<CqlConstraint.Raw> columnConstraints = new ArrayList<>();
Review Comment:
same. unnecessary to init here.
##########
src/antlr/Parser.g:
##########
@@ -976,6 +1006,15 @@ alterTableStatement returns [AlterTableStatement.Raw stmt]
)
;
+alterCqlConstraintExp[AlterTableStatement.Raw stmt] returns [CqlConstraint.Raw
cqlConstraint]
+ : cond=alterConstraintFunctionCondition[stmt] { cqlConstraint = new
CqlConstraint.Raw(cond); }
+ ;
+
+alterConstraintFunctionCondition[AlterTableStatement.Raw stmt] returns
[ConstraintCondition cond]
+ : f=constraintFunctionExpression op=relationType t=value { cond = new
CqlConstraintFunctionCondition.Raw(f, op, t.getText()).prepare(); }
+ | k=ident op=relationType t=value { cond = new
ConstraintScalarCondition.Raw(k, op, t.getText()).prepare(); }
Review Comment:
Why create a separate "condition"? We should be able to reuse
`columnConstraints` here.
##########
src/antlr/Parser.g:
##########
@@ -779,12 +779,27 @@ tableDefinition[CreateTableStatement.Raw stmt]
;
tableColumns[CreateTableStatement.Raw stmt]
- @init { boolean isStatic = false; }
- : k=ident v=comparatorType (K_STATIC { isStatic = true; })?
(mask=columnMask)? { $stmt.addColumn(k, v, isStatic, mask); }
+ @init {
+ boolean isStatic = false;
+ }
Review Comment:
nit: unrelated change
##########
src/java/org/apache/cassandra/schema/ColumnMetadata.java:
##########
@@ -516,6 +583,19 @@ public void appendCqlTo(CqlBuilder builder)
if (isMasked())
mask.appendCqlTo(builder);
+
+ if (cqlConstraints != null && !cqlConstraints.isEmpty())
+ {
+ builder.append(" CHECK ");
+ Iterator<CqlConstraint> constraintIterator =
cqlConstraints.iterator();
+ constraintIterator.next().appendCqlTo(builder);
+
+ while (constraintIterator.hasNext())
+ {
+ builder.append(" AND ");
+ constraintIterator.next().appendCqlTo(builder);
+ }
+ }
Review Comment:
It can be encapsulated in `ColumnConstraints`.
##########
src/java/org/apache/cassandra/cql3/LengthConstraint.java:
##########
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.cql3.terms.Term;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+public class LengthConstraint implements ConstraintFunction
+{
+ @Override
+ public String getName()
+ {
+ return "LENGTH";
+ }
+
+ @Override
+ public void evaluate(List<ColumnIdentifier> args, Operator relationType,
String term, TableMetadata tableMetadata, Map<String, Term.Raw> columnValues)
+ {
+ ColumnMetadata columnMetadata = tableMetadata.getColumn(args.get(0));
+ if (!columnValues.containsKey(columnMetadata.name.toString()))
+ throw new ConstraintViolationException(columnMetadata.name + " is
not an existing column name.");
+
+ String columnValue =
columnValues.get(columnMetadata.name.toString()).getText();
Review Comment:
not a fan of parsing from string/text. Ideally, the value should be
deserialized from the binary value.
##########
src/java/org/apache/cassandra/cql3/ColumnConstraint.java:
##########
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+/**
+ * Common class for the conditions that a CQL Constraint needs to implement to
be integrated in the
+ * CQL Constraints framework.
+ */
+public interface ColumnConstraint
+{
+ IVersionedSerializer<ColumnConstraint> getSerializer();
+
+ void appendCqlTo(CqlBuilder builder);
+
+// /**
+// * Method that evaluates the condition. It can either succeed or throw a
{@link ConstraintViolationException}.
+// *
+// * @param columnValue Column value to be evaluated at write time.
+// */
+ void evaluate(Object columnValue) throws ConstraintViolationException;
+
+// void check(Object value) throws ConstraintViolationException;
Review Comment:
Update?
##########
src/java/org/apache/cassandra/cql3/ConstraintCondition.java:
##########
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import java.util.Map;
+
+import org.apache.cassandra.cql3.terms.Term;
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+/**
+ * Common class for the conditions that a CQL Constraint needs to implement to
be integrated in the
+ * CQL Constraints framework.
+ */
+public interface ConstraintCondition
+{
+ IVersionedAsymmetricSerializer<ConstraintCondition, ConstraintCondition>
getSerializer();
Review Comment:
Since IN and OUT types are the same, it can be replaced with
`IVersionedSerializer`
##########
src/java/org/apache/cassandra/schema/ColumnMetadata.java:
##########
@@ -609,6 +689,14 @@ public void serialize(ColumnMetadata t, DataOutputPlus
out, Version version) thr
out.writeBoolean(t.mask != null);
if (t.mask != null)
ColumnMask.serializer.serialize(t.mask, out, version);
+ if (t.cqlConstraints == null)
+ out.writeInt(0);
Review Comment:
Write a boolean (1 byte), instead of write an integer (4 bytes).
##########
src/java/org/apache/cassandra/cql3/ConstraintFunction.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.cql3.terms.Term;
+import org.apache.cassandra.schema.TableMetadata;
+
+/**
+ * Interface to be implemented by functions that are executed as part of CQL
constraints.
+ */
+public interface ConstraintFunction
+{
+ /**
+ * This method returns the function name to be executed.
+ *
+ * @return
+ */
+ String getName();
+
+ /**
+ * Method that provides the execution of the condition. It can either
succeed or throw a {@link ConstraintViolationException}.
+ *
+ * @param args
+ * @param relationType
+ * @param term
+ * @param tableMetadata
+ * @param columnValues
+ */
+ void evaluate(List<ColumnIdentifier> args, Operator relationType, String
term, TableMetadata tableMetadata, Map<String, Term.Raw> columnValues) throws
ConstraintViolationException;
+
+ /**
+ * 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 ConstraintInvalidException}
+ * @param args
+ * @param relationType
+ * @param term
+ * @param tableMetadata
+ */
+ void validate(List<ColumnIdentifier> args, Operator relationType, String
term, TableMetadata tableMetadata) throws ConstraintInvalidException;
+
+ /**
+ * Removes initial and ending quotes from a column value
+ *
+ * @param columnValue
+ * @return
+ */
+ default String stripColumnValue(String columnValue)
+ {
+ if (columnValue.startsWith("'") && columnValue.endsWith("'"))
+ return columnValue.substring(1, columnValue.length() - 1);
+ return columnValue;
+ }
Review Comment:
Interface is not the best place to declare the utility method. Can you
either move to a utility class, or move to the class that consumes it? It is
only used in `LengthConstraint`
##########
src/java/org/apache/cassandra/cql3/ConstraintInvalidException.java:
##########
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import org.apache.cassandra.exceptions.InvalidRequestException;
+
+/**
+ * Thrown to indicate that the CQL constraint is not valid
+ */
+public class ConstraintInvalidException extends InvalidRequestException
Review Comment:
Nit: how about rename to `InvalidConstraintDefinitionException`? To better
distinguish from `ConstraintViolationException`.
##########
src/antlr/Parser.g:
##########
@@ -945,15 +960,19 @@ alterKeyspaceStatement returns
[AlterKeyspaceStatement.Raw stmt]
* ALTER TABLE [IF EXISTS] <table> WITH <property> = <value>;
*/
alterTableStatement returns [AlterTableStatement.Raw stmt]
- @init { boolean ifExists = false; }
+ @init {
+ boolean ifExists = false;
+ }
Review Comment:
nit: unrelated change
##########
src/java/org/apache/cassandra/cql3/ConstraintCondition.java:
##########
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+import java.util.Map;
+
+import org.apache.cassandra.cql3.terms.Term;
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+/**
+ * Common class for the conditions that a CQL Constraint needs to implement to
be integrated in the
+ * CQL Constraints framework.
+ */
+public interface ConstraintCondition
Review Comment:
relocate this and the related classes to
`org.apache.cassandra.cql3.constraints` to be better organized.
##########
src/java/org/apache/cassandra/cql3/ColumnConstraintScalar.java:
##########
@@ -0,0 +1,164 @@
+/*
+ * 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;
+
+
+import java.io.IOException;
+
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+
+public class ColumnConstraintScalar implements ColumnConstraint
+{
+ public final ColumnIdentifier param;
+ public final Operator relationType;
+ public final String term;
+
+ public final static Serializer serializer = new Serializer();
+
+ public final static class Raw
+ {
+ public final ColumnIdentifier param;
+ public final Operator relationType;
+ public final String term;
+
+ public Raw(ColumnIdentifier param, Operator relationType, String term)
+ {
+ this.param = param;
+ this.relationType = relationType;
+ this.term = term;
+ }
+
+ public ColumnConstraintScalar prepare()
+ {
+ return new ColumnConstraintScalar(param, relationType, term);
+ }
+ }
+
+ public ColumnConstraintScalar(ColumnIdentifier param, Operator
relationType, String term)
+ {
+ this.param = param;
+ this.relationType = relationType;
+ this.term = term;
+ }
+
+ public void evaluate(Object columnValue)
+ {
+ Number columnValueNumber;
+ float sizeConstraint;
+
+ try
+ {
+ columnValueNumber = (Number) columnValue;
+ sizeConstraint = Float.parseFloat(term);
+ }
+ catch (final NumberFormatException exception)
+ {
+ throw new ConstraintViolationException(param + " and " + term + "
need to be numbers.");
+ }
+
+ switch (relationType)
+ {
+ case EQ:
+ if (Float.compare(columnValueNumber.floatValue(),
sizeConstraint) != 0)
+ throw new ConstraintViolationException(param + " value
length should be exactly " + sizeConstraint);
+ break;
+ case NEQ:
+ if (Double.compare(columnValueNumber.floatValue(),
sizeConstraint) == 0)
+ throw new ConstraintViolationException(param + " value
length different than " + sizeConstraint);
+ break;
+ case GT:
+ if (columnValueNumber.floatValue() <= sizeConstraint)
+ throw new ConstraintViolationException(param + " value
length should be larger than " + sizeConstraint);
+ break;
+ case LT:
+ if (columnValueNumber.floatValue() >= sizeConstraint)
+ throw new ConstraintViolationException(param + " value
length should be smaller than " + sizeConstraint);
+ break;
+ case GTE:
+ if (columnValueNumber.floatValue() < sizeConstraint)
+ throw new ConstraintViolationException(param + " value
length should be larger or equal than " + sizeConstraint);
+ break;
+ case LTE:
+ if (columnValueNumber.floatValue() > sizeConstraint)
+ throw new ConstraintViolationException(param + " value
length should be smaller or equal than " + sizeConstraint);
+ break;
+ default:
+ throw new ConstraintViolationException("Invalid relation type:
" + relationType);
+ }
+ }
+
+ @Override
+ public void validate(ColumnMetadata columnMetadata, TableMetadata
tableMetadata) throws ConstraintInvalidException
+ {
+ if (!(columnMetadata.type instanceof
org.apache.cassandra.db.marshal.NumberType))
+ throw new ConstraintInvalidException(param + " is not a number");
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s %s %s", param, relationType, term);
Review Comment:
Avoid `String.format` for its poor performance. Simple string concatenation
is more performant.
##########
src/java/org/apache/cassandra/cql3/ColumnConstraints.java:
##########
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// group of constraints for the column
+public class ColumnConstraints implements ColumnConstraint
+{
+
+ public static Serializer serializer = new Serializer();
+
+ private final List<ColumnConstraint> constraints;
+
+ public ColumnConstraints(List<ColumnConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public IVersionedSerializer<ColumnConstraint> getSerializer() {
+ return null;
+ }
+
+ @Override
+ public void appendCqlTo(CqlBuilder builder) {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.appendCqlTo(builder);
+ }
+ }
+
+ @Override
+ public void evaluate(Object columnValue) throws
ConstraintViolationException {
+
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.evaluate(columnValue);
+ }
+ }
+
+ public List<ColumnConstraint> getConstraints()
+ {
+ return constraints;
+ }
+
+ public boolean isEmpty()
+ {
+ return constraints.isEmpty();
+ }
+
+
+ @Override
+ public void validate(ColumnMetadata columnMetadata, TableMetadata
tableMetadata) throws ConstraintInvalidException {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.validate(columnMetadata, tableMetadata);
+ }
+ }
+
+ public static class Noop extends ColumnConstraints
+ {
+ public static Noop INSTANCE = new Noop();
Review Comment:
```suggestion
public static final Noop INSTANCE = new Noop();
```
##########
src/java/org/apache/cassandra/cql3/ConstraintFunctionExpression.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.TableMetadata;
+
+public class ConstraintFunctionExpression
+{
+ public final ConstraintFunction constraintFunction;
+ public final ColumnIdentifier columnName;
+
+ public static final Serializer serializer = new Serializer();
+
+ public static final Map<String, ConstraintFunction>
supportedConstraintFunctions = Map.of(
+ LengthConstraint.class.getName(), new LengthConstraint()
+ );
+
+ public ConstraintFunctionExpression(ConstraintFunction constraintFunction,
ColumnIdentifier columnName)
+ {
+ this.constraintFunction = constraintFunction;
+ this.columnName = columnName;
+ }
+
+
+ public void evaluate(Operator relationType, String term, Object
columnValues)
+ {
+ constraintFunction.evaluate(columnName, relationType, term,
columnValues);
+ }
+
+ public void validateConstraint(Operator relationType, String term,
TableMetadata tableMetadata)
+ {
+ constraintFunction.validate(columnName, relationType, term,
tableMetadata);
+ }
+
+ public String toCqlString()
+ {
+ return toString();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s(%s)", constraintFunction.getName(),
columnName);
Review Comment:
nit: Avoid `String.format`
##########
src/java/org/apache/cassandra/cql3/ColumnConstraints.java:
##########
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// group of constraints for the column
+public class ColumnConstraints implements ColumnConstraint
+{
+
+ public static Serializer serializer = new Serializer();
+
+ private final List<ColumnConstraint> constraints;
+
+ public ColumnConstraints(List<ColumnConstraint> constraints) {
+ this.constraints = constraints;
Review Comment:
copy the list, instead of assigning the reference directly.
##########
src/java/org/apache/cassandra/cql3/FunctionColumnConstraint.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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;
+
+
+import java.io.IOException;
+
+import org.apache.cassandra.cql3.terms.Constants;
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+
+public class FunctionColumnConstraint implements ColumnConstraint
+{
+ public final ConstraintFunctionExpression function;
+ public final Operator relationType;
+ public final String term;
+
+ public static Serializer serializer = new Serializer();
+
+ public final static class Raw
+ {
+ public final ConstraintFunctionExpression function;
+ public final Operator relationType;
+ public final String term;
+
+ public Raw(ColumnIdentifier functionName, ColumnIdentifier columnName,
Operator relationType, String term)
+ {
+ this.relationType = relationType;
+ this.term = term;
+ if
(LengthConstraint.FUNCTION_NAME.equals(functionName.toCQLString().toUpperCase()))
+ this.function = new ConstraintFunctionExpression(new
LengthConstraint(), columnName);
+ else
+ throw new ConstraintInvalidException("Invalid constraint
function");
+ }
+
+ public FunctionColumnConstraint prepare()
+ {
+ return new FunctionColumnConstraint(function, relationType, term);
+ }
+ }
+
+ @Override
+ public void appendCqlTo(CqlBuilder builder) {
+ builder.append(toString());
+ }
+
+ public FunctionColumnConstraint(ConstraintFunctionExpression function,
Operator relationType, String term)
+ {
+ this.function = function;
+ this.relationType = relationType;
+ this.term = term;
+ }
+
+ @Override
+ public IVersionedSerializer<ColumnConstraint> getSerializer()
+ {
+ return serializer;
+ }
+
+ @Override
+ public void evaluate(Object columnValue)
+ {
+ if (function != null)
+ function.evaluate(relationType, term, columnValue);
+ }
+
+ @Override
+ public void validate(ColumnMetadata columnMetadata, TableMetadata
tableMetadata)
+ {
+ if (columnMetadata != null)
+ validateArgs(columnMetadata);
+ if (function != null)
+ function.validateConstraint(relationType, term, tableMetadata);
+ }
+
+ void validateArgs(ColumnMetadata columnMetadata)
+ {
+ if (function == null)
+ throw new ConstraintInvalidException("Function parameter should be
the column name");
+
+ if (!columnMetadata.name.equals(function.columnName))
+ throw new ConstraintInvalidException("Function parameter should be
the column name");
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s %s %s", function, relationType, term);
Review Comment:
nit: avoid `String.format`
##########
src/java/org/apache/cassandra/cql3/ColumnConstraints.java:
##########
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// group of constraints for the column
+public class ColumnConstraints implements ColumnConstraint
+{
+
+ public static Serializer serializer = new Serializer();
+
+ private final List<ColumnConstraint> constraints;
+
+ public ColumnConstraints(List<ColumnConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public IVersionedSerializer<ColumnConstraint> getSerializer() {
+ return null;
+ }
+
+ @Override
+ public void appendCqlTo(CqlBuilder builder) {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.appendCqlTo(builder);
+ }
+ }
+
+ @Override
+ public void evaluate(Object columnValue) throws
ConstraintViolationException {
+
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.evaluate(columnValue);
+ }
+ }
+
+ public List<ColumnConstraint> getConstraints()
+ {
+ return constraints;
+ }
+
+ public boolean isEmpty()
+ {
+ return constraints.isEmpty();
+ }
+
+
+ @Override
+ public void validate(ColumnMetadata columnMetadata, TableMetadata
tableMetadata) throws ConstraintInvalidException {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.validate(columnMetadata, tableMetadata);
+ }
+ }
+
+ public static class Noop extends ColumnConstraints
+ {
+ public static Noop INSTANCE = new Noop();
+
+ public Noop() {
+ super(new ArrayList<>());
+ }
+ }
+
+ public final static class Raw
+ {
+ private final List<ColumnConstraint> constraints;
+
+ public Raw(List<ColumnConstraint> constraints)
+ {
+ this.constraints = constraints;
+ }
+
+ public Raw()
+ {
+ this.constraints = new ArrayList<>();
Review Comment:
```suggestion
this.constraints = Collections.emptyList();
```
##########
src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java:
##########
@@ -299,9 +300,7 @@ public void validateDiskUsage(QueryOptions options,
ClientState state)
Token token = metadata().partitioner.getToken(key);
for (Replica replica :
ReplicaLayout.forTokenWriteLiveAndDown(keyspace, token).all())
- {
Guardrails.replicaDiskUsage.guard(replica.endpoint(),
state);
- }
Review Comment:
unrelated change. Please revert.
##########
src/java/org/apache/cassandra/cql3/ConstraintFunctionExpression.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.TableMetadata;
+
+public class ConstraintFunctionExpression
+{
+ public final ConstraintFunction constraintFunction;
+ public final ColumnIdentifier columnName;
+
+ public static final Serializer serializer = new Serializer();
+
+ public static final Map<String, ConstraintFunction>
supportedConstraintFunctions = Map.of(
+ LengthConstraint.class.getName(), new LengthConstraint()
+ );
Review Comment:
The check for the supported constraint functions is done at
`org.apache.cassandra.cql3.FunctionColumnConstraint.Raw` already. The map is
redundant.
In fact, I think this class `ConstraintFunctionExpression` is an unnecessary
abstraction. You should achieve the same with `FunctionColumnConstraint` and
`ColumnConstraint`. Please think about it.
##########
src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java:
##########
@@ -485,7 +485,7 @@ public boolean isTopK()
* @param kind the column type
* @return the <code>Restrictions</code> for the specified type of columns
*/
- private Restrictions getRestrictions(ColumnMetadata.Kind kind)
+ public Restrictions getRestrictions(ColumnMetadata.Kind kind)
Review Comment:
the method is only referenced within the class. It is unnecessary to open
the access to `public`
##########
src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java:
##########
@@ -348,4 +371,60 @@ public AuditLogContext getAuditLogContext()
{
return new AuditLogContext(AuditLogEntryType.UPDATE, keyspace(),
table());
}
+
+ public static void evaluateConstraints(TableMetadata tableMetadata,
List<ColumnIdentifier> columnNames, List<Term.Raw> columnValues)
+ {
+ Map<String, Term.Raw> columnMap = getColumnMap(columnNames,
columnValues);
+ for (ColumnMetadata column : tableMetadata.columns())
+ {
+ if (column.hasConstraint())
+ for (ColumnConstraint constraint :
column.getColumnConstraints().getConstraints())
+ evaluateConstraint(column, constraint, columnMap,
column.type.getSerializer(), column);
+ }
+ }
+
+ public static void evaluateConstraint(ColumnMetadata column,
ColumnConstraint constraint, Map<String, Term.Raw> columnMap, TypeSerializer
serializer, ColumnSpecification receiver)
+ {
+
constraint.evaluate(serializer.deserialize(columnMap.get(column.name.toCQLString())
+
.prepare(column.ksName, receiver).bindAndGet(QueryOptions.DEFAULT)));
+ }
+
+ public static void evaluateJsonConstraints(TableMetadata tableMetadata,
List<ColumnIdentifier> columnNames, List<Term.Raw> columnValues)
+ {
+ Map<String, Term.Raw> columnMap = getColumnMap(columnNames,
columnValues);
+ for (ColumnMetadata column : tableMetadata.columns())
+ {
+ if (column.hasConstraint())
+ {
+ // This is needed becouse when parsing from JSON, the literal
type is for numeric
+ // values is float.
Review Comment:
nit: typo
```suggestion
// This is needed because when parsing from JSON, the
literal type for numeric
// values is float.
```
##########
src/java/org/apache/cassandra/cql3/ColumnConstraints.java:
##########
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+
+import org.apache.cassandra.io.IVersionedAsymmetricSerializer;
+import org.apache.cassandra.io.IVersionedSerializer;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+// group of constraints for the column
+public class ColumnConstraints implements ColumnConstraint
+{
+
+ public static Serializer serializer = new Serializer();
+
+ private final List<ColumnConstraint> constraints;
+
+ public ColumnConstraints(List<ColumnConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public IVersionedSerializer<ColumnConstraint> getSerializer() {
+ return null;
+ }
+
+ @Override
+ public void appendCqlTo(CqlBuilder builder) {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.appendCqlTo(builder);
+ }
+ }
+
+ @Override
+ public void evaluate(Object columnValue) throws
ConstraintViolationException {
+
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.evaluate(columnValue);
+ }
+ }
+
+ public List<ColumnConstraint> getConstraints()
+ {
+ return constraints;
+ }
+
+ public boolean isEmpty()
+ {
+ return constraints.isEmpty();
+ }
+
+
+ @Override
+ public void validate(ColumnMetadata columnMetadata, TableMetadata
tableMetadata) throws ConstraintInvalidException {
+ for (ColumnConstraint constraint : constraints)
+ {
+ constraint.validate(columnMetadata, tableMetadata);
+ }
+ }
+
+ public static class Noop extends ColumnConstraints
+ {
+ public static Noop INSTANCE = new Noop();
+
+ public Noop() {
+ super(new ArrayList<>());
+ }
+ }
+
+ public final static class Raw
+ {
+ private final List<ColumnConstraint> constraints;
+
+ public Raw(List<ColumnConstraint> constraints)
+ {
+ this.constraints = constraints;
+ }
+
+ public Raw()
+ {
+ this.constraints = new ArrayList<>();
+ }
+
+ public void addConstraint(ColumnConstraint constraint)
+ {
+ this.constraints.add(constraint);
+ }
Review Comment:
nit: remove the unused method.
##########
src/java/org/apache/cassandra/cql3/FunctionColumnConstraint.java:
##########
@@ -0,0 +1,141 @@
+/*
+ * 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;
+
+
+import java.io.IOException;
+
+import org.apache.cassandra.cql3.terms.Constants;
Review Comment:
Unused import
##########
src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java:
##########
@@ -93,7 +101,9 @@ public void addUpdateForKey(PartitionUpdate.Builder
updateBuilder, Clustering<?>
for (int i = 0, isize = updates.size(); i < isize; i++)
updates.get(i).execute(updateBuilder.partitionKey(), params);
- updateBuilder.add(params.buildRow());
+ Row row = params.buildRow();
+ //evaluateConstraints(metadata, columnNames, columnValues);
+ updateBuilder.add(row);
Review Comment:
Update?
I think you can do this (pseudo code below) to check the constraints of each
column. The benefit is that you do not need to obtain the `Term.Raw` to get the
data bytes. And it handles for both normal insert and json insert when the
check is performed here.
```java
Row row = params.buildRow();
Iterator<Cell<?>> cellIt = row.cells().iterator();
// check constraint for each column
while (cellIt.hasNext())
{
Cell<?> cell = cellIt.next();
ColumnMetadata columnMetadata = cell.column();
if (!columnMetadata.hasConstraint()
|| columnMetadata.isComplex()) // complex column is not
supported, for now
continue;
ByteBuffer cellData = cell.buffer();
evaluateConstraints(columnMetadata, cellData);
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]