mirror of https://github.com/apache/cassandra
Allow custom constraints to be loaded via SPI
patch by Stefan Miklosovic; reviewed by Bernardo Botella, Saranya Krishnakumar for CASSANDRA-20824
This commit is contained in:
parent
9ed9c7cacd
commit
9d5cef7f8c
|
|
@ -1,4 +1,5 @@
|
||||||
5.1
|
5.1
|
||||||
|
* Allow custom constraints to be loaded via SPI (CASSANDRA-20824)
|
||||||
* Optimize DataPlacement lookup by ReplicationParams (CASSANDRA-20804)
|
* Optimize DataPlacement lookup by ReplicationParams (CASSANDRA-20804)
|
||||||
* Fix ShortPaxosSimulationTest and AccordSimulationRunner do not execute from the cli (CASSANDRA-20805)
|
* Fix ShortPaxosSimulationTest and AccordSimulationRunner do not execute from the cli (CASSANDRA-20805)
|
||||||
* Allow overriding arbitrary settings via environment variables (CASSANDRA-20749)
|
* Allow overriding arbitrary settings via environment variables (CASSANDRA-20749)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,21 @@ ALTER TABLE [IF EXISTS] <table> ALTER [IF EXISTS] <column> DROP CHECK;
|
||||||
There is no way how to drop individual check when multiple checks are specified on a column. After dropping checks, you
|
There is no way how to drop individual check when multiple checks are specified on a column. After dropping checks, you
|
||||||
are required to re-define all necessary checks again.
|
are required to re-define all necessary checks again.
|
||||||
|
|
||||||
|
== Constraints are pluggable
|
||||||
|
|
||||||
|
On top of in-built constraints enumerated below, since https://issues.apache.org/jira/browse/CASSANDRA-20824[CASSANDRA-20824],
|
||||||
|
it is possible to provide your own implementation of a constraint and use it in Cassandra. This is possible
|
||||||
|
thanks to Java SPI, where you need to implement `ConstraintProvider` interface. There is an example of a custom constraint implementation in `examples/constraints` in the source distribution you can follow to implement your own constraints.
|
||||||
|
|
||||||
|
The advantage of this approach is that you can use an official release while you can just "patch" Cassandra by
|
||||||
|
providing your additional constraints specific to your business / needs. Feel free to reach us if you want
|
||||||
|
to make your constraint in-built!
|
||||||
|
|
||||||
|
If you start to use custom constraints, your nodes will fail to start if there isn't appropriate JAR these
|
||||||
|
constraints are implemented in. If constraints specified in JAR via SPI were not present, we skipped them and
|
||||||
|
a user didn't notice, then it might be possible to insert invalid data (per constraint) which we consider
|
||||||
|
is not desired behavior.
|
||||||
|
|
||||||
== AVAILABLE CONSTRAINTS
|
== AVAILABLE CONSTRAINTS
|
||||||
|
|
||||||
=== SCALAR CONSTRAINT
|
=== SCALAR CONSTRAINT
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
== Cassandra Constraint Example
|
||||||
|
|
||||||
|
|
||||||
|
The `CustomConstraintsProvider` class will create custom constraints. For the purposes of this example,
|
||||||
|
there will be two constraints: `MY_CUSTOM_CONSTRAINT` and `MY_CUSTOM_UNARY_CONSTRAINT`.
|
||||||
|
|
||||||
|
If you want to code your own constraints without patching Cassandra yourself, you need to do the following:
|
||||||
|
|
||||||
|
1. Code against interface `org.apache.cassandra.cql3.constraints.ConstraintProvider`.
|
||||||
|
2. Put the class implementing this interface to `META-INF/services/org.apache.cassandra.cql3.constraints.ConstraintProvider`
|
||||||
|
3. Build a JAR both with the implementation and `META-INF` resources, as show in this example, and put this JAR onto
|
||||||
|
Cassandra's classpath.
|
||||||
|
4. When Cassandra starts, it will auto-detect new constraints by loading `CustomConstraintsProvider` in your JAR.
|
||||||
|
Custom constraints are resolved first, in-built ones when constraint can not be resolved by given provider. This
|
||||||
|
means that if you have a constraint in Cassandra which is buggy, you can create your own provider, and return constraint
|
||||||
|
which "fixes" the in-built one, without any need to patch Cassandra itself. Of course, you can just implement any
|
||||||
|
other constraint you want so you can start to use your custom constraints without depending on Cassandra release etc.
|
||||||
|
|
||||||
|
There is only one `ConstraintProvider` loaded via `ServiceLoader`.
|
||||||
|
|
||||||
|
=== Installation
|
||||||
|
|
||||||
|
----
|
||||||
|
$ cd <cassandra_src_dir>/examples/constraints
|
||||||
|
$ ant install
|
||||||
|
----
|
||||||
|
|
||||||
|
It will build the constraints and will copy it to `lib` as well as to `build/lib/jars`.
|
||||||
|
|
||||||
|
You remove it from everywhere by
|
||||||
|
|
||||||
|
----
|
||||||
|
$ cd <cassandra_src_dir>/examples/constraints
|
||||||
|
$ ant clean
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Usage
|
||||||
|
|
||||||
|
----
|
||||||
|
cqlsh> CREATE KEYSPACE ks WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};
|
||||||
|
cqlsh> CREATE TABLE ks.tb (id int primary key, col text CHECK MY_CUSTOM_CONSTRAINT() > 10);
|
||||||
|
cqlsh> ALTER TABLE ks.tb ALTER col CHECK MY_CUSTOM_CONSTRAINT() > 20;
|
||||||
|
----
|
||||||
|
|
||||||
|
`MY_CUSTOM_CONSTRAINT` is functionally same as `LENGTH` constraint and `MY_CUSTOM_UNARY_CONSTRAINT` is functionally
|
||||||
|
same as `JSON` constraint.
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project default="jar" name="constraint-example">
|
||||||
|
<property name="cassandra.dir" value="../.." />
|
||||||
|
<property name="cassandra.dir.lib" value="${cassandra.dir}/lib" />
|
||||||
|
<property name="cassandra.classes" value="${cassandra.dir}/build/classes/main" />
|
||||||
|
<property name="build.src" value="${basedir}/src" />
|
||||||
|
<property name="build.dir" value="${basedir}/build" />
|
||||||
|
<property name="conf.dir" value="${basedir}/conf" />
|
||||||
|
<property name="build.classes" value="${build.dir}/classes" />
|
||||||
|
<property name="final.name" value="constraint-example" />
|
||||||
|
|
||||||
|
<path id="build.classpath">
|
||||||
|
<fileset dir="${cassandra.dir.lib}">
|
||||||
|
<include name="**/*.jar" />
|
||||||
|
</fileset>
|
||||||
|
<fileset dir="${cassandra.dir}/build/lib/jars">
|
||||||
|
<include name="**/*.jar" />
|
||||||
|
</fileset>
|
||||||
|
<pathelement location="${cassandra.classes}" />
|
||||||
|
</path>
|
||||||
|
<target name="init">
|
||||||
|
<mkdir dir="${build.classes}" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="init">
|
||||||
|
<javac destdir="${build.classes}" debug="true" includeantruntime="false">
|
||||||
|
<src path="${build.src}" />
|
||||||
|
<classpath refid="build.classpath" />
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="jar" depends="build">
|
||||||
|
<jar jarfile="${build.dir}/${final.name}.jar">
|
||||||
|
<fileset dir="${build.classes}" />
|
||||||
|
<fileset dir="${build.src}/resources"/>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="install" depends="jar">
|
||||||
|
<copy verbose="true" file="${build.dir}/${final.name}.jar" todir="${cassandra.dir}/lib" overwrite="true"/>
|
||||||
|
<copy verbose="true" file="${build.dir}/${final.name}.jar" todir="${cassandra.dir}/build/lib/jars" overwrite="true"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="${build.dir}" />
|
||||||
|
<delete file="${cassandra.dir}/lib/${final.name}.jar"/>
|
||||||
|
<delete file="${cassandra.dir}/build/lib/jars/${final.name}.jar"/>
|
||||||
|
</target>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cassandra.cql3.constraints;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.constraints.ColumnConstraint;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintFunction;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintProvider;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintResolver;
|
||||||
|
import org.apache.cassandra.cql3.constraints.InvalidConstraintDefinitionException;
|
||||||
|
import org.apache.cassandra.cql3.constraints.JsonConstraint;
|
||||||
|
import org.apache.cassandra.cql3.constraints.LengthConstraint;
|
||||||
|
import org.apache.cassandra.cql3.constraints.SatisfiabilityChecker;
|
||||||
|
import org.apache.cassandra.cql3.constraints.SatisfiabilityChecker.UnaryFunctionSatisfiabilityChecker;
|
||||||
|
import org.apache.cassandra.cql3.constraints.UnaryConstraintFunction;
|
||||||
|
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||||
|
import org.apache.cassandra.schema.ColumnMetadata;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.constraints.AbstractFunctionSatisfiabilityChecker.FUNCTION_SATISFIABILITY_CHECKER;
|
||||||
|
import static org.apache.cassandra.cql3.constraints.ConstraintResolver.getConstraintFunction;
|
||||||
|
import static org.apache.cassandra.cql3.constraints.ConstraintResolver.getUnaryConstraintFunction;
|
||||||
|
|
||||||
|
public class CustomConstraintsProvider implements ConstraintProvider
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Optional<UnaryConstraintFunction> getUnaryConstraint(String functionName, List<String> arguments)
|
||||||
|
{
|
||||||
|
if (!functionName.equalsIgnoreCase(MyCustomUnaryConstraint.NAME))
|
||||||
|
return Optional.empty();
|
||||||
|
|
||||||
|
return Optional.of(new MyCustomUnaryConstraint(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ConstraintFunction> getConstraintFunction(String functionName, List<String> arguments)
|
||||||
|
{
|
||||||
|
if (!functionName.equalsIgnoreCase(MyCustomConstraint.NAME))
|
||||||
|
return Optional.empty();
|
||||||
|
|
||||||
|
return Optional.of(new MyCustomConstraint(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends SatisfiabilityChecker> getUnaryConstraintSatisfiabilityCheckers()
|
||||||
|
{
|
||||||
|
return List.of(new MyCustomUnaryConstraint(List.of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends SatisfiabilityChecker> getConstraintFunctionSatisfiabilityCheckers()
|
||||||
|
{
|
||||||
|
return List.of((constraints, columnMetadata) ->
|
||||||
|
FUNCTION_SATISFIABILITY_CHECKER.check(MyCustomConstraint.NAME,
|
||||||
|
constraints,
|
||||||
|
columnMetadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as length constraint, just under different name to prove the point
|
||||||
|
*/
|
||||||
|
public static class MyCustomConstraint extends LengthConstraint
|
||||||
|
{
|
||||||
|
public static final String NAME = "MY_CUSTOM_CONSTRAINT";
|
||||||
|
|
||||||
|
public MyCustomConstraint(List<String> arguments)
|
||||||
|
{
|
||||||
|
super(NAME, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as JSON constraint, just under different name to prove the point
|
||||||
|
*/
|
||||||
|
public static class MyCustomUnaryConstraint extends JsonConstraint
|
||||||
|
{
|
||||||
|
public static final String NAME = "MY_CUSTOM_UNARY_CONSTRAINT";
|
||||||
|
|
||||||
|
public MyCustomUnaryConstraint(List<String> arguments)
|
||||||
|
{
|
||||||
|
super(NAME, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
# 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.
|
||||||
|
org.apache.cassandra.cql3.constraints.CustomConstraintsProvider
|
||||||
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.cassandra.cql3.CqlBuilder;
|
import org.apache.cassandra.cql3.CqlBuilder;
|
||||||
import org.apache.cassandra.cql3.Operator;
|
import org.apache.cassandra.cql3.Operator;
|
||||||
import org.apache.cassandra.utils.LocalizeString;
|
|
||||||
|
|
||||||
public abstract class AbstractFunctionConstraint<T> extends ColumnConstraint<T>
|
public abstract class AbstractFunctionConstraint<T> extends ColumnConstraint<T>
|
||||||
{
|
{
|
||||||
|
|
@ -52,16 +51,4 @@ public abstract class AbstractFunctionConstraint<T> extends ColumnConstraint<T>
|
||||||
{
|
{
|
||||||
builder.append(toString());
|
builder.append(toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends Enum<T>> T getEnum(Class<T> enumClass, String functionName)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Enum.valueOf(enumClass, LocalizeString.toUpperCaseLocalized(functionName));
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException e)
|
|
||||||
{
|
|
||||||
throw new InvalidConstraintDefinitionException("Unrecognized constraint function: " + functionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ public abstract class ColumnConstraint<T>
|
||||||
// We are serializing its enum position instead of its name.
|
// We are serializing its enum position instead of its name.
|
||||||
// Changing this enum would affect how that int is interpreted when deserializing.
|
// Changing this enum would affect how that int is interpreted when deserializing.
|
||||||
COMPOSED(ColumnConstraints.serializer, new DuplicatesChecker()),
|
COMPOSED(ColumnConstraints.serializer, new DuplicatesChecker()),
|
||||||
FUNCTION(FunctionColumnConstraint.serializer, FunctionColumnConstraint.getSatisfiabilityCheckers()),
|
FUNCTION(FunctionColumnConstraint.serializer, ConstraintResolver.getConstraintFunctionSatisfiabilityCheckers()),
|
||||||
SCALAR(ScalarColumnConstraint.serializer, new ScalarColumnConstraintSatisfiabilityChecker()),
|
SCALAR(ScalarColumnConstraint.serializer, new ScalarColumnConstraintSatisfiabilityChecker()),
|
||||||
UNARY_FUNCTION(UnaryFunctionColumnConstraint.serializer, UnaryFunctionColumnConstraint.Functions.values());
|
UNARY_FUNCTION(UnaryFunctionColumnConstraint.serializer, ConstraintResolver.getUnarySatisfiabilityCheckers());
|
||||||
|
|
||||||
private final MetadataSerializer<?> serializer;
|
private final MetadataSerializer<?> serializer;
|
||||||
private final SatisfiabilityChecker[] satisfiabilityCheckers;
|
private final SatisfiabilityChecker[] satisfiabilityCheckers;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.constraints;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users implementing this interface and integrating it with SPI (putting JAR on a class path and
|
||||||
|
* adding it to META-INF/services/ConstraintProvider) will enrich Cassandra with their custom constraints.
|
||||||
|
*/
|
||||||
|
public interface ConstraintProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Tries to instantiate {@link UnaryConstraintFunction} with given arguments.
|
||||||
|
* <p>
|
||||||
|
* An implementation of this method should always return new object for each method call. Do not
|
||||||
|
* cache constraint instances and do not return them! Create a new instance every time. Do not re-use it.
|
||||||
|
*
|
||||||
|
* @param functionName name of function
|
||||||
|
* @param arguments arguments to the function
|
||||||
|
* @return unary constraint function when possible to create with this provider, empty optional otherwise.
|
||||||
|
*/
|
||||||
|
Optional<UnaryConstraintFunction> getUnaryConstraint(String functionName, List<String> arguments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to instantiate {@link ConstraintFunction} with given arguments.
|
||||||
|
* <p>
|
||||||
|
* An implementation of this method should always return new object for each method call. Do not
|
||||||
|
* cache constraint instances and do not return them! Create a new instance every time. Do not re-use it.
|
||||||
|
*
|
||||||
|
* @param functionName name of function
|
||||||
|
* @param arguments arguments to the function
|
||||||
|
* @return constraint function when possible to create with this provider, empty optional otherwise.
|
||||||
|
*/
|
||||||
|
Optional<ConstraintFunction> getConstraintFunction(String functionName, List<String> arguments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of satisfiability checkers for all unary constraints this provider is responsible for
|
||||||
|
*/
|
||||||
|
List<? extends SatisfiabilityChecker> getUnaryConstraintSatisfiabilityCheckers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of satisfiability checkers for all function constraints this provider is responsible for
|
||||||
|
*/
|
||||||
|
List<? extends SatisfiabilityChecker> getConstraintFunctionSatisfiabilityCheckers();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.cql3.constraints;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.cql3.constraints.SatisfiabilityChecker.UnaryFunctionSatisfiabilityChecker;
|
||||||
|
import org.apache.cassandra.utils.LocalizeString;
|
||||||
|
|
||||||
|
public class ConstraintResolver
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ConstraintResolver.class);
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static ConstraintProvider customConstraintProvider = ServiceLoader.load(ConstraintProvider.class).findFirst().orElse(null);
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
if (customConstraintProvider != null)
|
||||||
|
logger.info("Found custom constraint provider {}", customConstraintProvider.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UnaryFunctions implements UnaryFunctionSatisfiabilityChecker
|
||||||
|
{
|
||||||
|
NOT_NULL(NotNullConstraint::new),
|
||||||
|
JSON(JsonConstraint::new);
|
||||||
|
|
||||||
|
public final Function<List<String>, ConstraintFunction> functionCreator;
|
||||||
|
|
||||||
|
UnaryFunctions(Function<List<String>, ConstraintFunction> functionCreator)
|
||||||
|
{
|
||||||
|
this.functionCreator = functionCreator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Functions
|
||||||
|
{
|
||||||
|
LENGTH(LengthConstraint::new),
|
||||||
|
OCTET_LENGTH(OctetLengthConstraint::new),
|
||||||
|
REGEXP(RegexpConstraint::new);
|
||||||
|
|
||||||
|
public final Function<List<String>, ConstraintFunction> functionCreator;
|
||||||
|
|
||||||
|
Functions(Function<List<String>, ConstraintFunction> functionCreator)
|
||||||
|
{
|
||||||
|
this.functionCreator = functionCreator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns implementation of function constraint. First, it iterates over custom functions, when not found,
|
||||||
|
* then it will look into built-in ones. If it is not found in either, throws an exception.
|
||||||
|
*
|
||||||
|
* @param functionName name of function of get an instance of a constraint of
|
||||||
|
* @param arguments arguments for constraint
|
||||||
|
* @return new instance of constraint for given name
|
||||||
|
* @throws InvalidConstraintDefinitionException in case constraint can not be resolved.
|
||||||
|
*/
|
||||||
|
public static ConstraintFunction getConstraintFunction(String functionName, List<String> arguments)
|
||||||
|
{
|
||||||
|
if (customConstraintProvider != null)
|
||||||
|
{
|
||||||
|
Optional<ConstraintFunction> maybeConstraint = customConstraintProvider.getConstraintFunction(functionName, arguments);
|
||||||
|
if (maybeConstraint.isPresent())
|
||||||
|
return maybeConstraint.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConstraintResolver.getEnum(Functions.class, functionName)
|
||||||
|
.map(c -> c.functionCreator.apply(arguments))
|
||||||
|
.orElseThrow(() -> new InvalidConstraintDefinitionException("Unrecognized constraint function: " + functionName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns implementation of unary function constraint. First, it iterates over built-in functions, when not found,
|
||||||
|
* then it will look into custom constraint provider, if any. If custom provider is not set or if it is not found
|
||||||
|
* there either, throws an exception.
|
||||||
|
*
|
||||||
|
* @param functionName name of function of get an instance of a constraint of
|
||||||
|
* @param arguments arguments for constraint
|
||||||
|
* @return new instance of constraint for given name
|
||||||
|
* @throws InvalidConstraintDefinitionException in case constraint can not be resolved.
|
||||||
|
*/
|
||||||
|
public static ConstraintFunction getUnaryConstraintFunction(String functionName, List<String> arguments)
|
||||||
|
{
|
||||||
|
if (customConstraintProvider != null)
|
||||||
|
{
|
||||||
|
Optional<UnaryConstraintFunction> maybeConstraint = customConstraintProvider.getUnaryConstraint(functionName, arguments);
|
||||||
|
if (maybeConstraint.isPresent())
|
||||||
|
return maybeConstraint.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConstraintResolver.getEnum(UnaryFunctions.class, functionName)
|
||||||
|
.map(c -> c.functionCreator.apply(arguments))
|
||||||
|
.orElseThrow(() -> new InvalidConstraintDefinitionException("Unrecognized constraint function: " + functionName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SatisfiabilityChecker[] getConstraintFunctionSatisfiabilityCheckers()
|
||||||
|
{
|
||||||
|
List<SatisfiabilityChecker> checkers = new ArrayList<>(Arrays.asList(FunctionColumnConstraint.getSatisfiabilityCheckers()));
|
||||||
|
|
||||||
|
if (customConstraintProvider != null)
|
||||||
|
{
|
||||||
|
List<? extends SatisfiabilityChecker> checkersCustom = customConstraintProvider.getConstraintFunctionSatisfiabilityCheckers();
|
||||||
|
if (checkersCustom != null)
|
||||||
|
checkers.addAll(checkersCustom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkers.toArray(new SatisfiabilityChecker[checkers.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SatisfiabilityChecker[] getUnarySatisfiabilityCheckers()
|
||||||
|
{
|
||||||
|
List<SatisfiabilityChecker> checkers = new ArrayList<>(Arrays.asList(UnaryFunctions.values()));
|
||||||
|
|
||||||
|
if (customConstraintProvider != null)
|
||||||
|
{
|
||||||
|
List<? extends SatisfiabilityChecker> checkersCustom = customConstraintProvider.getUnaryConstraintSatisfiabilityCheckers();
|
||||||
|
if (checkersCustom != null)
|
||||||
|
checkers.addAll(checkersCustom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkers.toArray(new SatisfiabilityChecker[checkers.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> Optional<T> getEnum(Class<T> enumClass, String functionName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Optional.of(Enum.valueOf(enumClass, LocalizeString.toUpperCaseLocalized(functionName)));
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e)
|
||||||
|
{
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,10 +22,10 @@ import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
import org.apache.cassandra.cql3.Operator;
|
import org.apache.cassandra.cql3.Operator;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintResolver.Functions;
|
||||||
import org.apache.cassandra.db.TypeSizes;
|
import org.apache.cassandra.db.TypeSizes;
|
||||||
import org.apache.cassandra.db.marshal.AbstractType;
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
import org.apache.cassandra.io.util.DataInputPlus;
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
|
|
@ -55,7 +55,7 @@ public class FunctionColumnConstraint extends AbstractFunctionConstraint<Functio
|
||||||
this.term = term;
|
this.term = term;
|
||||||
if (arguments == null)
|
if (arguments == null)
|
||||||
arguments = new ArrayList<>();
|
arguments = new ArrayList<>();
|
||||||
function = createConstraintFunction(functionName.toCQLString(), arguments);
|
function = ConstraintResolver.getConstraintFunction(functionName.toCQLString(), arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionColumnConstraint prepare()
|
public FunctionColumnConstraint prepare()
|
||||||
|
|
@ -76,25 +76,6 @@ public class FunctionColumnConstraint extends AbstractFunctionConstraint<Functio
|
||||||
return satisfiabilityCheckers;
|
return satisfiabilityCheckers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Functions
|
|
||||||
{
|
|
||||||
LENGTH(LengthConstraint::new),
|
|
||||||
OCTET_LENGTH(OctetLengthConstraint::new),
|
|
||||||
REGEXP(RegexpConstraint::new);
|
|
||||||
|
|
||||||
private final Function<List<String>, ConstraintFunction> functionCreator;
|
|
||||||
|
|
||||||
Functions(Function<List<String>, ConstraintFunction> functionCreator)
|
|
||||||
{
|
|
||||||
this.functionCreator = functionCreator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConstraintFunction createConstraintFunction(String functionName, List<String> args)
|
|
||||||
{
|
|
||||||
return getEnum(Functions.class, functionName).functionCreator.apply(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FunctionColumnConstraint(ConstraintFunction function, Operator relationType, String term)
|
private FunctionColumnConstraint(ConstraintFunction function, Operator relationType, String term)
|
||||||
{
|
{
|
||||||
super(relationType, term);
|
super(relationType, term);
|
||||||
|
|
@ -211,7 +192,7 @@ public class FunctionColumnConstraint extends AbstractFunctionConstraint<Functio
|
||||||
ConstraintFunction function;
|
ConstraintFunction function;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
function = createConstraintFunction(functionName, args);
|
function = ConstraintResolver.getConstraintFunction(functionName, args);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,14 @@ public class JsonConstraint extends UnaryConstraintFunction
|
||||||
|
|
||||||
public static final String FUNCTION_NAME = "JSON";
|
public static final String FUNCTION_NAME = "JSON";
|
||||||
|
|
||||||
|
public JsonConstraint(String name, List<String> args)
|
||||||
|
{
|
||||||
|
super(name, args);
|
||||||
|
}
|
||||||
|
|
||||||
public JsonConstraint(List<String> args)
|
public JsonConstraint(List<String> args)
|
||||||
{
|
{
|
||||||
super(FUNCTION_NAME, args);
|
this(FUNCTION_NAME, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,14 @@ public class LengthConstraint extends ConstraintFunction
|
||||||
private static final String NAME = "LENGTH";
|
private static final String NAME = "LENGTH";
|
||||||
private static final List<AbstractType<?>> SUPPORTED_TYPES = List.of(BytesType.instance, UTF8Type.instance, AsciiType.instance);
|
private static final List<AbstractType<?>> SUPPORTED_TYPES = List.of(BytesType.instance, UTF8Type.instance, AsciiType.instance);
|
||||||
|
|
||||||
|
public LengthConstraint(String name, List<String> args)
|
||||||
|
{
|
||||||
|
super(name, args);
|
||||||
|
}
|
||||||
|
|
||||||
public LengthConstraint(List<String> args)
|
public LengthConstraint(List<String> args)
|
||||||
{
|
{
|
||||||
super(NAME, args);
|
this(NAME, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,9 @@ package org.apache.cassandra.cql3.constraints;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.cassandra.cql3.Operator;
|
import org.apache.cassandra.cql3.Operator;
|
||||||
|
import org.apache.cassandra.cql3.constraints.SatisfiabilityChecker.UnaryFunctionSatisfiabilityChecker;
|
||||||
|
|
||||||
public abstract class UnaryConstraintFunction extends ConstraintFunction
|
public abstract class UnaryConstraintFunction extends ConstraintFunction implements UnaryFunctionSatisfiabilityChecker
|
||||||
{
|
{
|
||||||
public UnaryConstraintFunction(String name, List<String> args)
|
public UnaryConstraintFunction(String name, List<String> args)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,12 @@ import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||||
import org.apache.cassandra.cql3.Operator;
|
import org.apache.cassandra.cql3.Operator;
|
||||||
import org.apache.cassandra.cql3.constraints.SatisfiabilityChecker.UnaryFunctionSatisfiabilityChecker;
|
import org.apache.cassandra.cql3.constraints.ConstraintResolver.UnaryFunctions;
|
||||||
import org.apache.cassandra.db.TypeSizes;
|
import org.apache.cassandra.db.TypeSizes;
|
||||||
import org.apache.cassandra.db.marshal.AbstractType;
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
import org.apache.cassandra.io.util.DataInputPlus;
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
|
|
@ -56,12 +55,12 @@ public class UnaryFunctionColumnConstraint extends AbstractFunctionConstraint<Un
|
||||||
|
|
||||||
public Raw(ColumnIdentifier functionName, List<String> arguments)
|
public Raw(ColumnIdentifier functionName, List<String> arguments)
|
||||||
{
|
{
|
||||||
function = createConstraintFunction(functionName.toString(), arguments);
|
function = ConstraintResolver.getUnaryConstraintFunction(functionName.toString(), arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Raw(ColumnIdentifier functionName)
|
public Raw(ColumnIdentifier functionName)
|
||||||
{
|
{
|
||||||
function = createConstraintFunction(functionName.toString(), List.of());
|
function = ConstraintResolver.getUnaryConstraintFunction(functionName.toString(), List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnaryFunctionColumnConstraint prepare()
|
public UnaryFunctionColumnConstraint prepare()
|
||||||
|
|
@ -70,24 +69,6 @@ public class UnaryFunctionColumnConstraint extends AbstractFunctionConstraint<Un
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Functions implements UnaryFunctionSatisfiabilityChecker
|
|
||||||
{
|
|
||||||
NOT_NULL(NotNullConstraint::new),
|
|
||||||
JSON(JsonConstraint::new);
|
|
||||||
|
|
||||||
private final Function<List<String>, ConstraintFunction> functionCreator;
|
|
||||||
|
|
||||||
Functions(Function<List<String>, ConstraintFunction> functionCreator)
|
|
||||||
{
|
|
||||||
this.functionCreator = functionCreator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConstraintFunction createConstraintFunction(String functionName, List<String> arguments)
|
|
||||||
{
|
|
||||||
return getEnum(Functions.class, functionName).functionCreator.apply(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnaryFunctionColumnConstraint(ConstraintFunction function)
|
public UnaryFunctionColumnConstraint(ConstraintFunction function)
|
||||||
{
|
{
|
||||||
super(null, null);
|
super(null, null);
|
||||||
|
|
@ -134,7 +115,7 @@ public class UnaryFunctionColumnConstraint extends AbstractFunctionConstraint<Un
|
||||||
@Override
|
@Override
|
||||||
public boolean enablesDuplicateDefinitions(String name)
|
public boolean enablesDuplicateDefinitions(String name)
|
||||||
{
|
{
|
||||||
return Functions.valueOf(name).enableDuplicateDefinitions();
|
return UnaryFunctions.valueOf(name).enableDuplicateDefinitions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -209,7 +190,7 @@ public class UnaryFunctionColumnConstraint extends AbstractFunctionConstraint<Un
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public ConstraintFunction getConstraintFunction(String functionName, List<String> args)
|
public ConstraintFunction getConstraintFunction(String functionName, List<String> args)
|
||||||
{
|
{
|
||||||
return createConstraintFunction(functionName, args);
|
return ConstraintResolver.getUnaryConstraintFunction(functionName, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* 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.constraints;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.cassandra.constraints.ConstraintsProviderTest.CustomConstraintProvider.MyCustomConstraint;
|
||||||
|
import org.apache.cassandra.constraints.ConstraintsProviderTest.CustomConstraintProvider.MyCustomUnaryConstraint;
|
||||||
|
import org.apache.cassandra.cql3.Operator;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintFunction;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintProvider;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintResolver;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintResolver.Functions;
|
||||||
|
import org.apache.cassandra.cql3.constraints.ConstraintResolver.UnaryFunctions;
|
||||||
|
import org.apache.cassandra.cql3.constraints.InvalidConstraintDefinitionException;
|
||||||
|
import org.apache.cassandra.cql3.constraints.JsonConstraint;
|
||||||
|
import org.apache.cassandra.cql3.constraints.LengthConstraint;
|
||||||
|
import org.apache.cassandra.cql3.constraints.SatisfiabilityChecker;
|
||||||
|
import org.apache.cassandra.cql3.constraints.UnaryConstraintFunction;
|
||||||
|
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.cql3.constraints.AbstractFunctionSatisfiabilityChecker.FUNCTION_SATISFIABILITY_CHECKER;
|
||||||
|
import static org.apache.cassandra.cql3.constraints.ConstraintResolver.customConstraintProvider;
|
||||||
|
import static org.apache.cassandra.cql3.constraints.ConstraintResolver.getConstraintFunction;
|
||||||
|
import static org.apache.cassandra.cql3.constraints.ConstraintResolver.getUnaryConstraintFunction;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
public class ConstraintsProviderTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testContraintProvider()
|
||||||
|
{
|
||||||
|
customConstraintProvider = new CustomConstraintProvider();
|
||||||
|
|
||||||
|
ConstraintFunction constraintFunction = getConstraintFunction(MyCustomConstraint.NAME, List.of());
|
||||||
|
assertNotNull(constraintFunction);
|
||||||
|
constraintFunction.evaluate(UTF8Type.instance,
|
||||||
|
Operator.EQ,
|
||||||
|
Integer.toString(MyCustomConstraint.NAME.length()),
|
||||||
|
UTF8Type.instance.fromString(MyCustomConstraint.NAME));
|
||||||
|
|
||||||
|
ConstraintFunction unaryConstraint = getUnaryConstraintFunction(MyCustomUnaryConstraint.NAME, List.of());
|
||||||
|
assertNotNull(unaryConstraint);
|
||||||
|
unaryConstraint.evaluate(UTF8Type.instance, UTF8Type.instance.fromString("{\"a\": 4, \"b\": 10}"));
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> getConstraintFunction("not_existing", List.of()))
|
||||||
|
.isInstanceOf(InvalidConstraintDefinitionException.class);
|
||||||
|
|
||||||
|
SatisfiabilityChecker[] functionSatCheckers = ConstraintResolver.getConstraintFunctionSatisfiabilityCheckers();
|
||||||
|
assertNotNull(functionSatCheckers);
|
||||||
|
// in built + these in provider
|
||||||
|
assertEquals(functionSatCheckers.length,
|
||||||
|
Functions.values().length + customConstraintProvider.getConstraintFunctionSatisfiabilityCheckers().size());
|
||||||
|
|
||||||
|
SatisfiabilityChecker[] unarySatCheckers = ConstraintResolver.getUnarySatisfiabilityCheckers();
|
||||||
|
assertNotNull(unarySatCheckers);
|
||||||
|
// in built + these in provider
|
||||||
|
assertEquals(unarySatCheckers.length,
|
||||||
|
UnaryFunctions.values().length + customConstraintProvider.getUnaryConstraintSatisfiabilityCheckers().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CustomConstraintProvider implements ConstraintProvider
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Optional<UnaryConstraintFunction> getUnaryConstraint(String functionName, List<String> arguments)
|
||||||
|
{
|
||||||
|
if (!functionName.equalsIgnoreCase(MyCustomUnaryConstraint.NAME))
|
||||||
|
return Optional.empty();
|
||||||
|
|
||||||
|
return Optional.of(new MyCustomUnaryConstraint(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ConstraintFunction> getConstraintFunction(String functionName, List<String> arguments)
|
||||||
|
{
|
||||||
|
if (!functionName.equalsIgnoreCase(MyCustomConstraint.NAME))
|
||||||
|
return Optional.empty();
|
||||||
|
|
||||||
|
return Optional.of(new MyCustomConstraint(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends SatisfiabilityChecker> getUnaryConstraintSatisfiabilityCheckers()
|
||||||
|
{
|
||||||
|
return List.of(new MyCustomUnaryConstraint(List.of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends SatisfiabilityChecker> getConstraintFunctionSatisfiabilityCheckers()
|
||||||
|
{
|
||||||
|
return List.of((constraints, columnMetadata) ->
|
||||||
|
FUNCTION_SATISFIABILITY_CHECKER.check(MyCustomConstraint.NAME,
|
||||||
|
constraints,
|
||||||
|
columnMetadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as length constraint, just under different name to prove the point
|
||||||
|
*/
|
||||||
|
public static class MyCustomConstraint extends LengthConstraint
|
||||||
|
{
|
||||||
|
public static final String NAME = "MY_CUSTOM_CONSTRAINT";
|
||||||
|
|
||||||
|
public MyCustomConstraint(List<String> arguments)
|
||||||
|
{
|
||||||
|
super(NAME, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as JSON constraint, just under different name to prove the point
|
||||||
|
*/
|
||||||
|
public static class MyCustomUnaryConstraint extends JsonConstraint
|
||||||
|
{
|
||||||
|
public static final String NAME = "MY_CUSTOM_UNARY_CONSTRAINT";
|
||||||
|
|
||||||
|
public MyCustomUnaryConstraint(List<String> arguments)
|
||||||
|
{
|
||||||
|
super(NAME, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue