diff --git a/community/consistency-check/src/main/java/org/neo4j/consistency/checking/SchemaRecordCheck.java b/community/consistency-check/src/main/java/org/neo4j/consistency/checking/SchemaRecordCheck.java index de4a445fd4b47..85fb463887e5d 100644 --- a/community/consistency-check/src/main/java/org/neo4j/consistency/checking/SchemaRecordCheck.java +++ b/community/consistency-check/src/main/java/org/neo4j/consistency/checking/SchemaRecordCheck.java @@ -37,8 +37,6 @@ import org.neo4j.kernel.impl.store.record.RelationshipTypeTokenRecord; import org.neo4j.storageengine.api.schema.SchemaRule; -import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.UNIQUE; - /** * Note that this class builds up an in-memory representation of the complete schema store by being used in * multiple phases. @@ -156,7 +154,7 @@ public void checkConstraintRule( ConstraintRule rule, DynamicRecord record, { checkSchema( rule, record, records, engine ); - if ( rule.getConstraintDescriptor().type() == UNIQUE ) + if ( rule.getConstraintDescriptor().type().enforcesUniqueness() ) { DynamicRecord previousObligation = indexObligations.put( rule.getOwnedIndex(), record.clone() ); if ( previousObligation != null ) @@ -201,7 +199,7 @@ public void checkIndexRule( IndexRule rule, DynamicRecord record, RecordAccess r public void checkConstraintRule( ConstraintRule rule, DynamicRecord record, RecordAccess records, CheckerEngine engine ) { - if ( rule.getConstraintDescriptor().type() == UNIQUE ) + if ( rule.getConstraintDescriptor().type().enforcesUniqueness() ) { DynamicRecord obligation = constraintObligations.get( rule.getId() ); if ( obligation == null ) diff --git a/community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/MandatoryProperties.java b/community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/MandatoryProperties.java index 481d690381612..36016aff86ae6 100644 --- a/community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/MandatoryProperties.java +++ b/community/consistency-check/src/main/java/org/neo4j/consistency/checking/full/MandatoryProperties.java @@ -40,8 +40,6 @@ import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.unsafe.impl.batchimport.Utils; -import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.EXISTS; - public class MandatoryProperties { private final PrimitiveIntObjectMap nodes = Primitive.intObjectMap(); @@ -54,7 +52,7 @@ public MandatoryProperties( StoreAccess storeAccess ) SchemaStorage schemaStorage = new SchemaStorage( storeAccess.getSchemaStore() ); for ( ConstraintRule rule : constraintsIgnoringMalformed( schemaStorage ) ) { - if ( rule.getConstraintDescriptor().type() == EXISTS ) + if ( rule.getConstraintDescriptor().type().enforcesPropertyExistence() ) { rule.schema().processWith( constraintRecorder ); } diff --git a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/ast/QueryTagger.scala b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/ast/QueryTagger.scala index 577bf7e687d82..f2288fd6640c1 100644 --- a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/ast/QueryTagger.scala +++ b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/ast/QueryTagger.scala @@ -290,6 +290,7 @@ object QueryTagger extends QueryTagger[String] { lift[ASTNode] { case x: CreateIndex => Set(CreateIndexTag) case x: DropIndex => Set(DropIndexTag) + case x: CreateNodeKeyConstraint => Set(CreateConstraintTag) case x: CreateUniquePropertyConstraint => Set(CreateConstraintTag) case x: CreateNodePropertyExistenceConstraint => Set(CreateConstraintTag) case x: CreateRelationshipPropertyExistenceConstraint => Set(CreateConstraintTag) diff --git a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/executionplan/procs/ProcedureCallOrSchemaCommandPlanBuilder.scala b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/executionplan/procs/ProcedureCallOrSchemaCommandPlanBuilder.scala index ed90ad37ac54c..ea3e5ee4d651b 100644 --- a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/executionplan/procs/ProcedureCallOrSchemaCommandPlanBuilder.scala +++ b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/executionplan/procs/ProcedureCallOrSchemaCommandPlanBuilder.scala @@ -51,6 +51,20 @@ case object ProcedureCallOrSchemaCommandPlanBuilder extends Phase[CompilerContex Some(ProcedureCallExecutionPlan(signature, args, resolved.callResultTypes, resolved.callResultIndices, context.notificationLogger.notifications, context.typeConverter.asPublicType)) + // CREATE CONSTRAINT ON (node:Label) ASSERT (node.prop1,node.prop2) IS NODE KEY + case CreateNodeKeyConstraint(node, label, props) => + Some(PureSideEffectExecutionPlan("CreateNodeKeyConstraint", SCHEMA_WRITE, (ctx) => { + val propertyKeyIds = props.map(p => propertyToId(ctx)(p.propertyKey)) + ctx.createNodeKeyConstraint(IndexDescriptor(labelToId(ctx)(label), propertyKeyIds)) + })) + + // DROP CONSTRAINT ON (node:Label) ASSERT (node.prop1,node.prop2) IS NODE KEY + case DropNodeKeyConstraint(_, label, props) => + Some(PureSideEffectExecutionPlan("DropNodeKeyConstraint", SCHEMA_WRITE, (ctx) => { + val propertyKeyIds = props.map(p => propertyToId(ctx)(p.propertyKey)) + ctx.dropNodeKeyConstraint(IndexDescriptor(labelToId(ctx)(label), propertyKeyIds)) + })) + // CREATE CONSTRAINT ON (node:Label) ASSERT node.prop IS UNIQUE // CREATE CONSTRAINT ON (node:Label) ASSERT (node.prop1,node.prop2) IS UNIQUE case CreateUniquePropertyConstraint(node, label, props) => diff --git a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/DelegatingQueryContext.scala b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/DelegatingQueryContext.scala index fb4119a6a38f1..56ef112095fbd 100644 --- a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/DelegatingQueryContext.scala +++ b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/DelegatingQueryContext.scala @@ -111,6 +111,12 @@ class DelegatingQueryContext(val inner: QueryContext) extends QueryContext { override def getOrCreateFromSchemaState[K, V](key: K, creator: => V): V = singleDbHit(inner.getOrCreateFromSchemaState(key, creator)) + override def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean = + singleDbHit(inner.createNodeKeyConstraint(descriptor)) + + override def dropNodeKeyConstraint(descriptor: IndexDescriptor) = + singleDbHit(inner.dropNodeKeyConstraint(descriptor)) + override def createUniqueConstraint(descriptor: IndexDescriptor): Boolean = singleDbHit(inner.createUniqueConstraint(descriptor)) diff --git a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContext.scala b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContext.scala index 8f8fb5b3141c1..26801a1fd3e17 100644 --- a/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContext.scala +++ b/community/cypher/cypher-compiler-3.2/src/main/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContext.scala @@ -101,6 +101,11 @@ trait QueryContext extends TokenContext { def getOrCreateFromSchemaState[K, V](key: K, creator: => V): V + /* return true if the constraint was created, false if preexisting, throws if failed */ + def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean + + def dropNodeKeyConstraint(descriptor: IndexDescriptor) + /* return true if the constraint was created, false if preexisting, throws if failed */ def createUniqueConstraint(descriptor: IndexDescriptor): Boolean diff --git a/community/cypher/cypher-compiler-3.2/src/test/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContextAdaptation.scala b/community/cypher/cypher-compiler-3.2/src/test/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContextAdaptation.scala index 21691db62b00e..b94d7aa9cec63 100644 --- a/community/cypher/cypher-compiler-3.2/src/test/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContextAdaptation.scala +++ b/community/cypher/cypher-compiler-3.2/src/test/scala/org/neo4j/cypher/internal/compiler/v3_2/spi/QueryContextAdaptation.scala @@ -44,6 +44,8 @@ trait QueryContextAdaptation { override def createUniqueConstraint(descriptor: IndexDescriptor): Boolean = ??? + override def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean = ??? + override def getOrCreateRelTypeId(relTypeName: String): Int = ??? override def getPropertiesForRelationship(relId: Long): scala.Iterator[Int] = ??? @@ -80,6 +82,8 @@ trait QueryContextAdaptation { override def dropUniqueConstraint(descriptor: IndexDescriptor): Unit = ??? + override def dropNodeKeyConstraint(descriptor: IndexDescriptor): Unit = ??? + // Check if a runtime value is a node, relationship, path or some such value returned from override def isGraphKernelResultValue(v: Any): Boolean = ??? diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_2/ExceptionTranslatingQueryContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_2/ExceptionTranslatingQueryContext.scala index 2a6cd795266eb..9b73e27df2d4e 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_2/ExceptionTranslatingQueryContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/v3_2/ExceptionTranslatingQueryContext.scala @@ -111,6 +111,12 @@ class ExceptionTranslatingQueryContext(val inner: QueryContext) extends QueryCon override def getOrCreateFromSchemaState[K, V](key: K, creator: => V): V = translateException(inner.getOrCreateFromSchemaState(key, creator)) + override def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean = + translateException(inner.createNodeKeyConstraint(descriptor)) + + override def dropNodeKeyConstraint(descriptor: IndexDescriptor) = + translateException(inner.dropNodeKeyConstraint(descriptor)) + override def createUniqueConstraint(descriptor: IndexDescriptor): Boolean = translateException(inner.createUniqueConstraint(descriptor)) diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_2/TransactionBoundQueryContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_2/TransactionBoundQueryContext.scala index 5079f84ff58db..54e879cb11d43 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_2/TransactionBoundQueryContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_2/TransactionBoundQueryContext.scala @@ -481,6 +481,16 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional override def dropIndexRule(descriptor: IndexDescriptor) = transactionalContext.statement.schemaWriteOperations().indexDrop(descriptor) + override def createNodeKeyConstraint(descriptor: IndexDescriptor): Boolean = try { + transactionalContext.statement.schemaWriteOperations().nodeKeyConstraintCreate(descriptor) + true + } catch { + case existing: AlreadyConstrainedException => false + } + + override def dropNodeKeyConstraint(descriptor: IndexDescriptor) = + transactionalContext.statement.schemaWriteOperations().constraintDrop(ConstraintDescriptorFactory.uniqueForSchema(descriptor)) + override def createUniqueConstraint(descriptor: IndexDescriptor): Boolean = try { transactionalContext.statement.schemaWriteOperations().uniquePropertyConstraintCreate(descriptor) true diff --git a/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/ast/Command.scala b/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/ast/Command.scala index 61e9a72e35f75..cf76261308bdf 100644 --- a/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/ast/Command.scala +++ b/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/ast/Command.scala @@ -89,6 +89,10 @@ trait RelationshipPropertyConstraintCommand extends PropertyConstraintCommand { def relType: RelTypeName } +case class CreateNodeKeyConstraint(variable: Variable, label: LabelName, properties: Seq[Property])(val position: InputPosition) extends UniquePropertyConstraintCommand + +case class DropNodeKeyConstraint(variable: Variable, label: LabelName, properties: Seq[Property])(val position: InputPosition) extends UniquePropertyConstraintCommand + case class CreateUniquePropertyConstraint(variable: Variable, label: LabelName, properties: Seq[Property])(val position: InputPosition) extends UniquePropertyConstraintCommand case class DropUniquePropertyConstraint(variable: Variable, label: LabelName, properties: Seq[Property])(val position: InputPosition) extends UniquePropertyConstraintCommand diff --git a/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/parser/Command.scala b/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/parser/Command.scala index d5c9d08df2ab3..2059074c8ca78 100644 --- a/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/parser/Command.scala +++ b/community/cypher/frontend-3.2/src/main/scala/org/neo4j/cypher/internal/frontend/v3_2/parser/Command.scala @@ -32,11 +32,13 @@ trait Command extends Parser def Command: Rule1[ast.Command] = rule( CreateUniqueConstraint | CreateUniqueCompositeConstraint + | CreateNodeKeyConstraint | CreateNodePropertyExistenceConstraint | CreateRelationshipPropertyExistenceConstraint | CreateIndex | DropUniqueConstraint | DropUniqueCompositeConstraint + | DropNodeKeyConstraint | DropNodePropertyExistenceConstraint | DropRelationshipPropertyExistenceConstraint | DropIndex @@ -63,6 +65,10 @@ trait Command extends Parser group(keyword("CREATE") ~~ UniqueCompositeConstraintSyntax) ~~>> (ast.CreateUniquePropertyConstraint(_, _, _)) } + def CreateNodeKeyConstraint: Rule1[ast.CreateNodeKeyConstraint] = rule { + group(keyword("CREATE") ~~ NodeKeyConstraintSyntax) ~~>> (ast.CreateNodeKeyConstraint(_, _, _)) + } + def CreateNodePropertyExistenceConstraint: Rule1[ast.CreateNodePropertyExistenceConstraint] = rule { group(keyword("CREATE") ~~ NodePropertyExistenceConstraintSyntax) ~~>> (ast.CreateNodePropertyExistenceConstraint(_, _, _)) } @@ -80,6 +86,10 @@ trait Command extends Parser group(keyword("DROP") ~~ UniqueCompositeConstraintSyntax) ~~>> (ast.DropUniquePropertyConstraint(_, _, _)) } + def DropNodeKeyConstraint: Rule1[ast.DropNodeKeyConstraint] = rule { + group(keyword("DROP") ~~ NodeKeyConstraintSyntax) ~~>> (ast.DropNodeKeyConstraint(_, _, _)) + } + def DropNodePropertyExistenceConstraint: Rule1[ast.DropNodePropertyExistenceConstraint] = rule { group(keyword("DROP") ~~ NodePropertyExistenceConstraintSyntax) ~~>> (ast.DropNodePropertyExistenceConstraint(_, _, _)) } @@ -94,6 +104,9 @@ trait Command extends Parser ) ~~> (_.toIndexedSeq)) } + private def NodeKeyConstraintSyntax: Rule3[Variable, LabelName, Seq[Property]] = keyword("CONSTRAINT ON") ~~ "(" ~~ Variable ~~ NodeLabel ~~ ")" ~~ + keyword("ASSERT") ~~ "(" ~~ PropertyExpressions ~~ ")" ~~ keyword("IS NODE KEY") + private def UniqueConstraintSyntax: Rule3[Variable, LabelName, Property] = keyword("CONSTRAINT ON") ~~ "(" ~~ Variable ~~ NodeLabel ~~ ")" ~~ keyword("ASSERT") ~~ PropertyExpression ~~ keyword("IS UNIQUE") diff --git a/community/graphdb-api/src/main/java/org/neo4j/graphdb/schema/ConstraintType.java b/community/graphdb-api/src/main/java/org/neo4j/graphdb/schema/ConstraintType.java index 44d039259e3ed..f2788e108391e 100644 --- a/community/graphdb-api/src/main/java/org/neo4j/graphdb/schema/ConstraintType.java +++ b/community/graphdb-api/src/main/java/org/neo4j/graphdb/schema/ConstraintType.java @@ -28,5 +28,6 @@ public enum ConstraintType UNIQUENESS, NODE_PROPERTY_EXISTENCE, RELATIONSHIP_PROPERTY_EXISTENCE, + NODE_KEY ; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/SchemaWriteOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/api/SchemaWriteOperations.java index 3c08f470c5367..d6189272647d1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/SchemaWriteOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/SchemaWriteOperations.java @@ -29,6 +29,7 @@ import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -52,6 +53,9 @@ NewIndexDescriptor indexCreate( LabelSchemaDescriptor schemaDescriptor ) */ void uniqueIndexDrop( NewIndexDescriptor descriptor ) throws DropIndexFailureException; + NodeKeyConstraintDescriptor nodeKeyConstraintCreate( LabelSchemaDescriptor descriptor ) + throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException; + UniquenessConstraintDescriptor uniquePropertyConstraintCreate( LabelSchemaDescriptor descriptor ) throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException, RepeatedPropertyInCompositeSchemaException; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/NodeKeyConstraint.java b/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/NodeKeyConstraint.java new file mode 100644 index 0000000000000..4eb680a50997b --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/NodeKeyConstraint.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.constraints; + +import java.util.Arrays; + +import org.neo4j.kernel.api.TokenNameLookup; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; +import org.neo4j.kernel.api.schema_new.SchemaUtil; +import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; +import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; + +/** + * Description of uniqueness and existence constraint over nodes given a label and a property key id. + * + * @deprecated use {@link NodeKeyConstraintDescriptor} instead. + */ +public class NodeKeyConstraint extends NodePropertyConstraint +{ + public NodeKeyConstraint( LabelSchemaDescriptor descriptor ) + { + super( descriptor ); + } + + public NewIndexDescriptor indexDescriptor() + { + return NewIndexDescriptorFactory.forSchema( descriptor ); + } + + @Override + public void added( ChangeVisitor visitor ) + { + visitor.visitAddedNodeKeyConstraint( this ); + } + + @Override + public void removed( ChangeVisitor visitor ) + { + visitor.visitRemovedNodeKeyConstraint( this ); + } + + @Override + public String userDescription( TokenNameLookup tokenNameLookup ) + { + String labelName = labelName( tokenNameLookup ); + String boundIdentifier = labelName.toLowerCase(); + String properties = + SchemaUtil.niceProperties( tokenNameLookup, descriptor.getPropertyIds(), boundIdentifier + "." ); + if ( descriptor.getPropertyIds().length > 1 ) + { + properties = "(" + properties + ")"; + } + return String.format( "CONSTRAINT ON ( %s:%s ) ASSERT %s IS NODE KEY", boundIdentifier, labelName, properties ); + } + + @Override + public String toString() + { + return String.format( "CONSTRAINT ON ( n:label[%s] ) ASSERT n.property[%s] IS NODE KEY", + descriptor.getLabelId(), Arrays.toString( descriptor.getPropertyIds() ) ); + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/PropertyConstraint.java b/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/PropertyConstraint.java index d8315669476e6..ea691fb0e521f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/PropertyConstraint.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/constraints/PropertyConstraint.java @@ -38,6 +38,10 @@ interface ChangeVisitor void visitRemovedUniquePropertyConstraint( UniquenessConstraint constraint ); + void visitAddedNodeKeyConstraint( NodeKeyConstraint constraint ); + + void visitRemovedNodeKeyConstraint( NodeKeyConstraint constraint ); + void visitAddedNodePropertyExistenceConstraint( NodePropertyExistenceConstraint constraint ) throws CreateConstraintFailureException; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/LabelSchemaDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/LabelSchemaDescriptor.java index f04763aa5c36f..6079fabfefcdb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/LabelSchemaDescriptor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/LabelSchemaDescriptor.java @@ -102,4 +102,24 @@ public LabelSchemaDescriptor schema() { return this; } + + public int compareTo( SchemaDescriptor o ) + { + if ( o instanceof LabelSchemaDescriptor ) + { + LabelSchemaDescriptor other = (LabelSchemaDescriptor) o; + if ( labelId == other.getLabelId() ) + { + return SchemaUtil.comparePropertyKeyIds( propertyIds, other.getPropertyIds() ); + } + else + { + return labelId - other.labelId; + } + } + else + { + return -1; + } + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/RelationTypeSchemaDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/RelationTypeSchemaDescriptor.java index e36eb3b9a8d90..1c96544e23b77 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/RelationTypeSchemaDescriptor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/RelationTypeSchemaDescriptor.java @@ -92,4 +92,25 @@ public int hashCode() { return Arrays.hashCode( propertyIds ) + 31 * relTypeId; } + + @Override + public int compareTo( SchemaDescriptor o ) + { + if ( o instanceof RelationTypeSchemaDescriptor ) + { + RelationTypeSchemaDescriptor other = (RelationTypeSchemaDescriptor) o; + if ( relTypeId == other.getRelTypeId() ) + { + return SchemaUtil.comparePropertyKeyIds( propertyIds, other.getPropertyIds() ); + } + else + { + return relTypeId - other.getRelTypeId(); + } + } + else + { + return -1; + } + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaDescriptor.java index bd4a55d20f42f..c727a71b4bd2c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaDescriptor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaDescriptor.java @@ -33,7 +33,7 @@ * how this is done in eg. LabelSchemaDescriptor, and the SchemaProcessor and SchemaComputer interfaces need to be * extended with methods taking the new concrete type as argument. */ -public interface SchemaDescriptor +public interface SchemaDescriptor extends Comparable { /** * Computes some value by feeding this object into the given SchemaComputer. diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaUtil.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaUtil.java index 8bff59e1a79d9..a8c09f52d4e94 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaUtil.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/SchemaUtil.java @@ -64,4 +64,23 @@ public String propertyKeyGetName( int propertyKeyId ) return format( "property[%d]", propertyKeyId ); } }; + + public static int comparePropertyKeyIds( int[] these, int[] those ) + { + if ( these.length == those.length ) + { + for ( int i = 0; i < these.length; i++ ) + { + if ( these[i] != those[i] ) + { + return these[i] - those[i]; + } + } + return 0; + } + else + { + return these.length - those.length; + } + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintBoundary.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintBoundary.java index 6f023f53142d7..2ac2feae71a98 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintBoundary.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintBoundary.java @@ -19,6 +19,7 @@ */ package org.neo4j.kernel.api.schema_new.constaints; +import org.neo4j.kernel.api.constraints.NodeKeyConstraint; import org.neo4j.kernel.api.constraints.NodePropertyConstraint; import org.neo4j.kernel.api.constraints.NodePropertyExistenceConstraint; import org.neo4j.kernel.api.constraints.PropertyConstraint; @@ -76,6 +77,9 @@ public PropertyConstraint computeSpecific( LabelSchemaDescriptor schema ) case EXISTS: return new NodePropertyExistenceConstraint( schema ); + case UNIQUE_EXISTS: + return new NodeKeyConstraint( schema ); + default: throw new UnsupportedOperationException( "Although we cannot get here, this has not been implemented." ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptor.java index 09173dd77c30d..a54cf10cddae0 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptor.java @@ -32,8 +32,28 @@ public abstract class ConstraintDescriptor implements SchemaDescriptor.Supplier { public enum Type { - UNIQUE, - EXISTS + UNIQUE( true, false ), + EXISTS( false, true ), + UNIQUE_EXISTS( true, true ); + + private final boolean isUnique; + private final boolean mustExist; + + Type( boolean isUnique, boolean mustExist ) + { + this.isUnique = isUnique; + this.mustExist = mustExist; + } + + public boolean enforcesUniqueness() + { + return isUnique; + } + + public boolean enforcesPropertyExistence() + { + return mustExist; + } } public interface Supplier diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactory.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactory.java index a272a5b1e42dc..ede7ef62be972 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactory.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactory.java @@ -45,6 +45,11 @@ public static UniquenessConstraintDescriptor uniqueForLabel( int labelId, int... return new UniquenessConstraintDescriptor( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) ); } + public static NodeKeyConstraintDescriptor nodeKeyForLabel( int labelId, int... propertyIds ) + { + return new NodeKeyConstraintDescriptor( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) ); + } + public static ConstraintDescriptor existsForSchema( SchemaDescriptor schema ) { return schema.computeWith( convertToExistenceConstraint ); @@ -65,6 +70,11 @@ public static UniquenessConstraintDescriptor uniqueForSchema( SchemaDescriptor s return schema.computeWith( convertToUniquenessConstraint ); } + public static NodeKeyConstraintDescriptor nodeKeyForSchema( SchemaDescriptor schema ) + { + return schema.computeWith( convertToNodeKeyConstraint ); + } + private static SchemaComputer convertToExistenceConstraint = new SchemaComputer() { @@ -100,4 +110,24 @@ public UniquenessConstraintDescriptor computeSpecific( RelationTypeSchemaDescrip ) ); } }; + + private static SchemaComputer convertToNodeKeyConstraint = + new SchemaComputer() + { + @Override + public NodeKeyConstraintDescriptor computeSpecific( LabelSchemaDescriptor schema ) + { + return new NodeKeyConstraintDescriptor( schema ); + } + + @Override + public NodeKeyConstraintDescriptor computeSpecific( RelationTypeSchemaDescriptor schema ) + { + throw new UnsupportedOperationException( + format( "Cannot create node key constraint for schema '%s' of type %s", + schema.userDescription( SchemaUtil.idTokenNameLookup ), + schema.getClass().getSimpleName() + ) ); + } + }; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/IndexBackedConstraintDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/IndexBackedConstraintDescriptor.java new file mode 100644 index 0000000000000..2165a031c9318 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/IndexBackedConstraintDescriptor.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.schema_new.constaints; + +import org.neo4j.kernel.api.TokenNameLookup; +import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; +import org.neo4j.kernel.api.schema_new.SchemaUtil; +import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; +import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; + +public abstract class IndexBackedConstraintDescriptor extends ConstraintDescriptor implements LabelSchemaDescriptor.Supplier +{ + private final LabelSchemaDescriptor schema; + + IndexBackedConstraintDescriptor( Type type, LabelSchemaDescriptor schema ) + { + super( type ); + this.schema = schema; + } + + @Override + public LabelSchemaDescriptor schema() + { + return schema; + } + + public NewIndexDescriptor ownedIndexDescriptor() + { + return NewIndexDescriptorFactory.uniqueForSchema( schema ); + } + + @Override + public String prettyPrint( TokenNameLookup tokenNameLookup ) + { + String labelName = escapeLabelOrRelTyp( tokenNameLookup.labelGetName( schema.getLabelId() ) ); + String nodeName = labelName.toLowerCase(); + String properties = SchemaUtil.niceProperties( tokenNameLookup, schema.getPropertyIds(), nodeName + "." ); + if ( schema().getPropertyIds().length > 1 ) + { + properties = "(" + properties + ")"; + } + return String.format( "CONSTRAINT ON ( %s:%s ) ASSERT %s IS %s", nodeName, labelName, properties, + constraintTypeText() ); + } + + protected abstract String constraintTypeText(); +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/NodeKeyConstraintDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/NodeKeyConstraintDescriptor.java new file mode 100644 index 0000000000000..ad5ff5393237f --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/NodeKeyConstraintDescriptor.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.schema_new.constaints; + +import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; + +public class NodeKeyConstraintDescriptor extends IndexBackedConstraintDescriptor +{ + NodeKeyConstraintDescriptor( LabelSchemaDescriptor schema ) + { + super( Type.UNIQUE_EXISTS, schema ); + } + + @Override + protected String constraintTypeText() + { + return "NODE KEY"; + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/UniquenessConstraintDescriptor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/UniquenessConstraintDescriptor.java index 14f8c6f057b6e..5fcdbef61e2dc 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/UniquenessConstraintDescriptor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/schema_new/constaints/UniquenessConstraintDescriptor.java @@ -19,44 +19,18 @@ */ package org.neo4j.kernel.api.schema_new.constaints; -import org.neo4j.kernel.api.TokenNameLookup; import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; -import org.neo4j.kernel.api.schema_new.LabelSchemaSupplier; -import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; -import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; -public class UniquenessConstraintDescriptor extends ConstraintDescriptor implements LabelSchemaSupplier +public class UniquenessConstraintDescriptor extends IndexBackedConstraintDescriptor { - private final LabelSchemaDescriptor schema; - UniquenessConstraintDescriptor( LabelSchemaDescriptor schema ) { - super( Type.UNIQUE ); - this.schema = schema; + super( Type.UNIQUE, schema ); } @Override - public LabelSchemaDescriptor schema() + protected String constraintTypeText() { - return schema; - } - - public NewIndexDescriptor ownedIndexDescriptor() - { - return NewIndexDescriptorFactory.uniqueForSchema( schema ); - } - - @Override - public String prettyPrint( TokenNameLookup tokenNameLookup ) - { - String labelName = escapeLabelOrRelTyp( tokenNameLookup.labelGetName( schema.getLabelId() ) ); - String nodeName = labelName.toLowerCase(); - // awaiting the OpenCypher decision of constraint syntax - String propertyName = schema.getPropertyIds().length == 1 ? - tokenNameLookup.propertyKeyGetName( schema.getPropertyId() ) : - schema.userDescription( tokenNameLookup ); - - return String.format( "CONSTRAINT ON ( %s:%s ) ASSERT %s.%s IS UNIQUE", - nodeName, labelName, nodeName, propertyName ); + return "UNIQUE"; } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionState.java b/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionState.java index 56e88c079ed37..18bbd6c0d7329 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionState.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/txstate/TransactionState.java @@ -24,6 +24,7 @@ import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema_new.OrderedPropertyValues; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.IndexBackedConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.storageengine.api.txstate.ReadableTransactionState; @@ -87,7 +88,7 @@ void relationshipDoReplaceProperty( long relationshipId, void constraintDoAdd( ConstraintDescriptor constraint ); - void constraintDoAdd( UniquenessConstraintDescriptor constraint, long indexId ); + void constraintDoAdd( IndexBackedConstraintDescriptor constraint, long indexId ); void constraintDoDrop( ConstraintDescriptor constraint ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java index 6c70f69295163..cb731c349ea2e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java @@ -57,6 +57,7 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -80,7 +81,6 @@ import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_PROPERTY_KEY; import static org.neo4j.kernel.api.exceptions.schema.ConstraintValidationException.Phase.VALIDATION; import static org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates.hasProperty; -import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.UNIQUE; import static org.neo4j.kernel.impl.locking.ResourceTypes.INDEX_ENTRY; import static org.neo4j.kernel.impl.locking.ResourceTypes.indexEntryResourceId; @@ -118,7 +118,7 @@ public boolean nodeAddLabel( KernelStatement state, long nodeId, int labelId ) while ( constraints.hasNext() ) { ConstraintDescriptor constraint = constraints.next(); - if ( constraint.type() == UNIQUE ) + if ( constraint.type().enforcesUniqueness() ) { UniquenessConstraintDescriptor uniqueConstraint = (UniquenessConstraintDescriptor) constraint; ExactPredicate[] propertyValues = getAllPropertyValues( state, uniqueConstraint.schema(), node ); @@ -557,6 +557,13 @@ public void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor descripto schemaWriteOperations.uniqueIndexDrop( state, descriptor ); } + @Override + public NodeKeyConstraintDescriptor nodeKeyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) + throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException + { + return schemaWriteOperations.nodeKeyConstraintCreate( state, descriptor ); + } + @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperations.java index baba04770645f..63332d192eb6d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperations.java @@ -42,6 +42,7 @@ import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -115,7 +116,7 @@ public NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescrip throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException { assertValidDescriptor( descriptor, OperationContext.INDEX_CREATION ); - assertIndexDoesNotExist( state, OperationContext.INDEX_CREATION, descriptor ); + assertUniqueIndexDoesNotExist( state, OperationContext.INDEX_CREATION, descriptor ); return schemaWriteDelegate.indexCreate( state, descriptor ); } @@ -150,6 +151,20 @@ public void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor index ) t schemaWriteDelegate.uniqueIndexDrop( state, index ); } + @Override + public NodeKeyConstraintDescriptor nodeKeyConstraintCreate( + KernelStatement state, LabelSchemaDescriptor descriptor ) + throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException + { + ConstraintDescriptor constraint = ConstraintDescriptorFactory.nodeKeyForSchema( descriptor ); + assertConstraintDoesNotExist( state, constraint ); + + // It is not allowed to create node key constraints on indexed label/property pairs + assertUniqueIndexDoesNotExist( state, OperationContext.CONSTRAINT_CREATION, descriptor ); + + return schemaWriteDelegate.nodeKeyConstraintCreate( state, descriptor ); + } + @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) @@ -161,7 +176,7 @@ public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( assertConstraintDoesNotExist( state, constraint ); // It is not allowed to create uniqueness constraints on indexed label/property pairs - assertIndexDoesNotExist( state, OperationContext.CONSTRAINT_CREATION, descriptor ); + assertUniqueIndexDoesNotExist( state, OperationContext.CONSTRAINT_CREATION, descriptor ); return schemaWriteDelegate.uniquePropertyConstraintCreate( state, descriptor ); } @@ -203,7 +218,7 @@ public void constraintDrop( KernelStatement state, ConstraintDescriptor descript schemaWriteDelegate.constraintDrop( state, descriptor ); } - private void assertIndexDoesNotExist( KernelStatement state, OperationContext context, + private void assertUniqueIndexDoesNotExist( KernelStatement state, OperationContext context, LabelSchemaDescriptor descriptor ) throws AlreadyIndexedException, AlreadyConstrainedException { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java index 0293f68fffcd7..99bff0becd1d6 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java @@ -46,6 +46,7 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -343,6 +344,15 @@ private void lockRelationshipNodes( KernelStatement state, long startNodeId, lon } } + @Override + public NodeKeyConstraintDescriptor nodeKeyConstraintCreate( KernelStatement state,LabelSchemaDescriptor descriptor ) + throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException + { + acquireExclusiveSchemaLock( state ); + state.assertOpen(); + return schemaWriteDelegate.nodeKeyConstraintCreate( state, descriptor ); + } + @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state,LabelSchemaDescriptor descriptor ) throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java index 484a2a0080fa7..857532bd22055 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java @@ -82,6 +82,7 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -1000,6 +1001,14 @@ public void indexDrop( NewIndexDescriptor descriptor ) throws DropIndexFailureEx schemaWrite().indexDrop( statement, descriptor ); } + @Override + public NodeKeyConstraintDescriptor nodeKeyConstraintCreate( LabelSchemaDescriptor descriptor ) + throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException + { + statement.assertOpen(); + return schemaWrite().nodeKeyConstraintCreate( statement, descriptor ); + } + @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( LabelSchemaDescriptor descriptor ) throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java index 595310435421f..2140e82f574d1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java @@ -19,9 +19,11 @@ */ package org.neo4j.kernel.impl.api; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.Map; +import java.util.TreeMap; import org.neo4j.collection.primitive.Primitive; import org.neo4j.collection.primitive.PrimitiveIntCollection; @@ -68,7 +70,9 @@ import org.neo4j.kernel.api.schema_new.SchemaUtil; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; +import org.neo4j.kernel.api.schema_new.constaints.IndexBackedConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -585,11 +589,10 @@ public void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor descripto state.txState().indexDoDrop( descriptor ); } - @Override - public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) + private IndexBackedConstraintDescriptor indexBackedConstraintCreate( KernelStatement state, IndexBackedConstraintDescriptor constraint ) throws CreateConstraintFailureException { - UniquenessConstraintDescriptor constraint = ConstraintDescriptorFactory.uniqueForSchema( descriptor ); + LabelSchemaDescriptor descriptor = constraint.schema(); try { if ( state.hasTxStateWithChanges() && @@ -622,6 +625,25 @@ public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStat } } + @Override + public NodeKeyConstraintDescriptor nodeKeyConstraintCreate( KernelStatement state, + LabelSchemaDescriptor descriptor ) + throws CreateConstraintFailureException + { + NodeKeyConstraintDescriptor constraint = ConstraintDescriptorFactory.nodeKeyForSchema( descriptor ); + indexBackedConstraintCreate( state, constraint ); + return constraint; + } + + @Override + public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) + throws CreateConstraintFailureException + { + UniquenessConstraintDescriptor constraint = ConstraintDescriptorFactory.uniqueForSchema( descriptor ); + indexBackedConstraintCreate( state, constraint ); + return constraint; + } + @Override public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( KernelStatement state, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/SchemaWriteOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/SchemaWriteOperations.java index e5e3e3eb4875f..fd7a98f64a23c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/SchemaWriteOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/SchemaWriteOperations.java @@ -29,6 +29,7 @@ import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -52,6 +53,9 @@ NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor des */ void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor descriptor ) throws DropIndexFailureException; + NodeKeyConstraintDescriptor nodeKeyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) + throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException; + UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException, RepeatedPropertyInCompositeSchemaException; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java index 9270126b1d398..58e53b003fc7e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java @@ -42,7 +42,7 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; -import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.IndexBackedConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.api.txstate.RelationshipChangeVisitorAdapter; import org.neo4j.kernel.api.txstate.TransactionState; @@ -158,7 +158,7 @@ void setMap( TxState state, Map map ) private PrimitiveLongSet nodesDeletedInTx; private PrimitiveLongSet relationshipsDeletedInTx; - private Map createdConstraintIndexesByConstraint; + private Map createdConstraintIndexesByConstraint; private Map>> indexUpdates; @@ -911,7 +911,7 @@ private GraphState getOrCreateGraphState() } @Override - public void constraintDoAdd( UniquenessConstraintDescriptor constraint, long indexId ) + public void constraintDoAdd( IndexBackedConstraintDescriptor constraint, long indexId ) { constraintsChangesDiffSets().add( constraint ); createdConstraintIndexesByConstraint().put( constraint, indexId ); @@ -962,9 +962,9 @@ private DiffSets constraintsChangesDiffSets() public void constraintDoDrop( ConstraintDescriptor constraint ) { constraintsChangesDiffSets().remove( constraint ); - if ( constraint.type() == ConstraintDescriptor.Type.UNIQUE ) + if ( constraint.type().enforcesUniqueness() ) { - indexDoDrop( getIndexForUniqueConstraint( (UniquenessConstraintDescriptor)constraint ) ); + indexDoDrop( getIndexForIndexBackedConstraint( (IndexBackedConstraintDescriptor) constraint ) ); } changed(); } @@ -980,7 +980,7 @@ public Iterable constraintIndexesCreatedInTx() { if ( createdConstraintIndexesByConstraint != null && !createdConstraintIndexesByConstraint.isEmpty() ) { - return map( this::getIndexForUniqueConstraint, createdConstraintIndexesByConstraint.keySet() ); + return map( this::getIndexForIndexBackedConstraint, createdConstraintIndexesByConstraint.keySet() ); } return Iterables.empty(); } @@ -1272,7 +1272,7 @@ private DiffSets getIndexUpdatesForScan( LabelSchemaDescriptor schema ) return diffs; } - private Map createdConstraintIndexesByConstraint() + private Map createdConstraintIndexesByConstraint() { if ( createdConstraintIndexesByConstraint == null ) { @@ -1281,7 +1281,7 @@ private Map createdConstraintIndexesByCons return createdConstraintIndexesByConstraint; } - private NewIndexDescriptor getIndexForUniqueConstraint( UniquenessConstraintDescriptor constraint ) + private NewIndexDescriptor getIndexForIndexBackedConstraint( IndexBackedConstraintDescriptor constraint ) { return constraint.ownedIndexDescriptor(); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/ConstraintSemantics.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/ConstraintSemantics.java index c1281f4d6c5ef..78232e9362808 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/ConstraintSemantics.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/ConstraintSemantics.java @@ -27,6 +27,7 @@ import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.impl.store.record.ConstraintRule; import org.neo4j.storageengine.api.NodeItem; @@ -51,6 +52,9 @@ void validateRelationshipPropertyExistenceConstraint( Cursor a ConstraintRule createUniquenessConstraintRule( long ruleId, UniquenessConstraintDescriptor descriptor, long indexId ); + ConstraintRule createNodeKeyConstraintRule( long ruleId, NodeKeyConstraintDescriptor descriptor, long indexId ) + throws CreateConstraintFailureException; + ConstraintRule createExistenceConstraint( long ruleId, ConstraintDescriptor descriptor ) throws CreateConstraintFailureException; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/StandardConstraintSemantics.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/StandardConstraintSemantics.java index fa80b878ac038..06f48d65690da 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/StandardConstraintSemantics.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/constraints/StandardConstraintSemantics.java @@ -29,6 +29,7 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.impl.store.record.ConstraintRule; import org.neo4j.storageengine.api.NodeItem; @@ -37,18 +38,17 @@ import org.neo4j.storageengine.api.txstate.ReadableTransactionState; import org.neo4j.storageengine.api.txstate.TxStateVisitor; -import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.UNIQUE; - public class StandardConstraintSemantics implements ConstraintSemantics { - public static final String ERROR_MESSAGE = "Property existence constraint requires Neo4j Enterprise Edition"; + public static final String ERROR_MESSAGE_EXISTS = "Property existence constraint requires Neo4j Enterprise Edition"; + public static final String ERROR_MESSAGE_NODE_KEY = "Node Key constraint requires Neo4j Enterprise Edition"; @Override public void validateNodePropertyExistenceConstraint( Iterator> allNodes, LabelSchemaDescriptor descriptor, BiPredicate hasProperty ) throws CreateConstraintFailureException { - throw propertyExistenceConstraintsNotAllowed( descriptor ); + throw propertyExistenceConstraintsNotAllowed( descriptor, ERROR_MESSAGE_NODE_KEY ); } @Override @@ -56,32 +56,35 @@ public void validateRelationshipPropertyExistenceConstraint( Cursor hasPropertyCheck ) throws CreateConstraintFailureException { - throw propertyExistenceConstraintsNotAllowed( descriptor ); + throw propertyExistenceConstraintsNotAllowed( descriptor, ERROR_MESSAGE_EXISTS ); } @Override public ConstraintDescriptor readConstraint( ConstraintRule rule ) { ConstraintDescriptor desc = rule.getConstraintDescriptor(); - if ( desc.type() == UNIQUE ) + switch ( desc.type() ) { - return desc; + case EXISTS: + return readNonStandardConstraint( rule, ERROR_MESSAGE_EXISTS ); + case UNIQUE_EXISTS: + return readNonStandardConstraint( rule, ERROR_MESSAGE_NODE_KEY ); } - return readNonStandardConstraint( rule ); + return desc; } - protected ConstraintDescriptor readNonStandardConstraint( ConstraintRule rule ) + protected ConstraintDescriptor readNonStandardConstraint( ConstraintRule rule, String errorMessage ) { // When opening a store in Community Edition that contains a Property Existence Constraint - throw new IllegalStateException( ERROR_MESSAGE ); + throw new IllegalStateException( errorMessage ); } - private CreateConstraintFailureException propertyExistenceConstraintsNotAllowed( SchemaDescriptor descriptor ) + private CreateConstraintFailureException propertyExistenceConstraintsNotAllowed( SchemaDescriptor descriptor, String errorMessage ) { // When creating a Property Existence Constraint in Community Edition return new CreateConstraintFailureException( ConstraintDescriptorFactory.existsForSchema( descriptor ), - new IllegalStateException( ERROR_MESSAGE ) ); + new IllegalStateException( errorMessage ) ); } @Override @@ -91,11 +94,18 @@ public ConstraintRule createUniquenessConstraintRule( return ConstraintRule.constraintRule( ruleId, descriptor, indexId ); } + @Override + public ConstraintRule createNodeKeyConstraintRule( + long ruleId, NodeKeyConstraintDescriptor descriptor, long indexId ) throws CreateConstraintFailureException + { + throw propertyExistenceConstraintsNotAllowed( descriptor.schema(), ERROR_MESSAGE_NODE_KEY ); + } + @Override public ConstraintRule createExistenceConstraint( long ruleId, ConstraintDescriptor descriptor ) throws CreateConstraintFailureException { - throw propertyExistenceConstraintsNotAllowed( descriptor.schema() ); + throw propertyExistenceConstraintsNotAllowed( descriptor.schema(), ERROR_MESSAGE_EXISTS ); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/InternalSchemaActions.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/InternalSchemaActions.java index 66a025c927d86..04055ad7ac9c4 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/InternalSchemaActions.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/InternalSchemaActions.java @@ -42,7 +42,11 @@ public interface InternalSchemaActions ConstraintDefinition createPropertyUniquenessConstraint( IndexDefinition indexDefinition ) throws IllegalTokenNameException, TooManyLabelsException, CreateConstraintFailureException, - AlreadyConstrainedException, AlreadyIndexedException; + AlreadyConstrainedException, AlreadyIndexedException; + + ConstraintDefinition createNodeKeyConstraint( IndexDefinition indexDefinition ) + throws IllegalTokenNameException, TooManyLabelsException, CreateConstraintFailureException, + AlreadyConstrainedException, AlreadyIndexedException; ConstraintDefinition createPropertyExistenceConstraint( Label label, String... propertyKey ) throws IllegalTokenNameException, TooManyLabelsException, CreateConstraintFailureException, @@ -53,6 +57,8 @@ ConstraintDefinition createPropertyExistenceConstraint( RelationshipType type, S void dropPropertyUniquenessConstraint( Label label, String[] properties ); + void dropNodeKeyConstraint( Label label, String[] properties ); + void dropNodePropertyExistenceConstraint( Label label, String[] properties ); void dropRelationshipPropertyExistenceConstraint( RelationshipType type, String propertyKey ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/NodeKeyConstraintDefinition.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/NodeKeyConstraintDefinition.java new file mode 100644 index 0000000000000..afb6707225128 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/NodeKeyConstraintDefinition.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2017 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.coreapi.schema; + +import org.neo4j.graphdb.schema.ConstraintType; +import org.neo4j.graphdb.schema.IndexDefinition; + +import static java.lang.String.format; + +public class NodeKeyConstraintDefinition extends NodeConstraintDefinition +{ + public NodeKeyConstraintDefinition( InternalSchemaActions actions, IndexDefinition indexDefinition ) + { + super( actions, indexDefinition ); + } + + @Override + public void drop() + { + assertInUnterminatedTransaction(); + actions.dropPropertyUniquenessConstraint( label, propertyKeys ); + } + + @Override + public ConstraintType getConstraintType() + { + assertInUnterminatedTransaction(); + return ConstraintType.NODE_KEY; + } + + @Override + public String toString() + { + return format( "ON (%1$s:%2$s) ASSERT %3$s IS UNIQUE", + label.name().toLowerCase(), label.name(), propertyText() ); + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImpl.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImpl.java index e050ac7d3cc18..2694feb6adead 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImpl.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/coreapi/schema/SchemaImpl.java @@ -64,6 +64,7 @@ import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.RelExistenceConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; @@ -388,6 +389,11 @@ else if ( constraint instanceof UniquenessConstraintDescriptor ) return new UniquenessConstraintDefinition( actions, new IndexDefinitionImpl( actions, label, propertyKeys, true ) ); } + else if ( constraint instanceof NodeKeyConstraintDescriptor ) + { + return new NodeKeyConstraintDefinition( actions, new IndexDefinitionImpl( actions, label, + propertyKeys, true ) ); + } } else if ( constraint instanceof RelExistenceConstraintDescriptor ) { @@ -505,6 +511,41 @@ public ConstraintDefinition createPropertyUniquenessConstraint( IndexDefinition } } + @Override + public ConstraintDefinition createNodeKeyConstraint( IndexDefinition indexDefinition ) + { + try ( Statement statement = ctxSupplier.get() ) + { + try + { + int labelId = statement.tokenWriteOperations().labelGetOrCreateForName( + indexDefinition.getLabel().name() ); + int[] propertyKeyIds = getOrCreatePropertyKeyIds( + statement.tokenWriteOperations(), indexDefinition ); + statement.schemaWriteOperations().nodeKeyConstraintCreate( + SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) ); + return new NodeKeyConstraintDefinition( this, indexDefinition ); + } + catch ( AlreadyConstrainedException | CreateConstraintFailureException | AlreadyIndexedException e ) + { + throw new ConstraintViolationException( + e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); + } + catch ( IllegalTokenNameException e ) + { + throw new IllegalArgumentException( e ); + } + catch ( TooManyLabelsException e ) + { + throw new IllegalStateException( e ); + } + catch ( InvalidTransactionTypeKernelException e ) + { + throw new InvalidTransactionTypeException( e.getMessage(), e ); + } + } + } + @Override public ConstraintDefinition createPropertyExistenceConstraint( Label label, String... propertyKeys ) { @@ -595,6 +636,30 @@ public void dropPropertyUniquenessConstraint( Label label, String[] properties ) } } + @Override + public void dropNodeKeyConstraint( Label label, String[] properties ) + { + try ( Statement statement = ctxSupplier.get() ) + { + try + { + int labelId = statement.readOperations().labelGetForName( label.name() ); + int[] propertyKeyIds = PropertyNameUtils.getPropertyIds( statement.readOperations(), properties ); + statement.schemaWriteOperations().constraintDrop( + ConstraintDescriptorFactory.nodeKeyForLabel( labelId, propertyKeyIds ) ); + } + catch ( DropConstraintFailureException e ) + { + throw new ConstraintViolationException( + e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); + } + catch ( InvalidTransactionTypeKernelException e ) + { + throw new ConstraintViolationException( e.getMessage(), e ); + } + } + } + @Override public void dropNodePropertyExistenceConstraint( Label label, String[] properties ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/TransactionToRecordStateVisitor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/TransactionToRecordStateVisitor.java index c3aa0b3748680..7a90693299b55 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/TransactionToRecordStateVisitor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/TransactionToRecordStateVisitor.java @@ -29,6 +29,8 @@ import org.neo4j.kernel.api.index.SchemaIndexProvider; import org.neo4j.kernel.api.properties.DefinedProperty; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeExistenceConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.impl.api.index.SchemaIndexProviderMap; @@ -202,16 +204,16 @@ public void visitAddedConstraint( ConstraintDescriptor constraint ) throws Creat switch ( constraint.type() ) { case UNIQUE: - UniquenessConstraintDescriptor uniqueConstraint = (UniquenessConstraintDescriptor) constraint; - IndexRule indexRule = schemaStorage.indexGetForSchema( uniqueConstraint.ownedIndexDescriptor() ); - recordState.createSchemaRule( constraintSemantics.createUniquenessConstraintRule( - constraintId, uniqueConstraint, indexRule.getId() ) ); - recordState.setConstraintIndexOwner( indexRule, constraintId ); + visitAddedUniquenessConstraint( (UniquenessConstraintDescriptor) constraint, constraintId ); + break; + + case UNIQUE_EXISTS: + visitAddedNodeKeyConstraint( (NodeKeyConstraintDescriptor) constraint, constraintId ); break; case EXISTS: - recordState.createSchemaRule( constraintSemantics.createExistenceConstraint( - schemaStorage.newRuleId(), constraint ) ); + recordState.createSchemaRule( + constraintSemantics.createExistenceConstraint( schemaStorage.newRuleId(), constraint ) ); break; default: @@ -219,6 +221,23 @@ public void visitAddedConstraint( ConstraintDescriptor constraint ) throws Creat } } + private void visitAddedUniquenessConstraint(UniquenessConstraintDescriptor uniqueConstraint, long constraintId) + { + IndexRule indexRule = schemaStorage.indexGetForSchema( uniqueConstraint.ownedIndexDescriptor() ); + recordState.createSchemaRule( constraintSemantics.createUniquenessConstraintRule( + constraintId, uniqueConstraint, indexRule.getId() ) ); + recordState.setConstraintIndexOwner( indexRule, constraintId ); + } + + private void visitAddedNodeKeyConstraint(NodeKeyConstraintDescriptor uniqueConstraint, long constraintId) + throws CreateConstraintFailureException + { + IndexRule indexRule = schemaStorage.indexGetForSchema( uniqueConstraint.ownedIndexDescriptor() ); + recordState.createSchemaRule( constraintSemantics.createNodeKeyConstraintRule( + constraintId, uniqueConstraint, indexRule.getId() ) ); + recordState.setConstraintIndexOwner( indexRule, constraintId ); + } + @Override public void visitRemovedConstraint( ConstraintDescriptor constraint ) { @@ -237,7 +256,7 @@ public void visitRemovedConstraint( ConstraintDescriptor constraint ) { throw new IllegalStateException( "Multiple constraints found for specified label and property." ); } - if ( constraint.type() == ConstraintDescriptor.Type.UNIQUE ) + if ( constraint.type().enforcesUniqueness() ) { // Remove the index for the constraint as well visitRemovedIndex( ((UniquenessConstraintDescriptor)constraint).ownedIndexDescriptor() ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/ConstraintRule.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/ConstraintRule.java index 5a979ba237b32..c230a47945111 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/ConstraintRule.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/ConstraintRule.java @@ -21,6 +21,8 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.IndexBackedConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.storageengine.api.schema.SchemaRule; @@ -38,7 +40,7 @@ public static ConstraintRule constraintRule( } public static ConstraintRule constraintRule( - long id, UniquenessConstraintDescriptor descriptor, long ownedIndexRule ) + long id, IndexBackedConstraintDescriptor descriptor, long ownedIndexRule ) { return new ConstraintRule( id, descriptor, ownedIndexRule ); } @@ -50,7 +52,7 @@ public static ConstraintRule constraintRule( } public static ConstraintRule constraintRule( - long id, UniquenessConstraintDescriptor descriptor, long ownedIndexRule, String name ) + long id, IndexBackedConstraintDescriptor descriptor, long ownedIndexRule, String name ) { return new ConstraintRule( id, descriptor, ownedIndexRule, name ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleDeserializer2_0to3_1.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleDeserializer2_0to3_1.java index 42c8b51d237dd..dea86463effb1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleDeserializer2_0to3_1.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleDeserializer2_0to3_1.java @@ -84,6 +84,8 @@ private static SchemaRule newRule( Kind kind, long id, int labelId, ByteBuffer b return readNodePropertyExistenceConstraintRule( id, labelId, buffer ); case RELATIONSHIP_PROPERTY_EXISTENCE_CONSTRAINT: return readRelPropertyExistenceConstraintRule( id, labelId, buffer ); + case NODE_KEY_CONSTRAINT: + return readNodeKeyConstraintRule( id, labelId, buffer ); default: throw new IllegalArgumentException( kind.name() ); } @@ -138,6 +140,13 @@ public static ConstraintRule readUniquenessConstraintRule( long id, int labelId, readOwnedIndexRule( buffer ) ); } + public static ConstraintRule readNodeKeyConstraintRule( long id, int labelId, ByteBuffer buffer ) + { + return new ConstraintRule( id, + ConstraintDescriptorFactory.nodeKeyForLabel( labelId, readConstraintPropertyKeys( buffer ) ), + readOwnedIndexRule( buffer ) ); + } + public static ConstraintRule readNodePropertyExistenceConstraintRule( long id, int labelId, ByteBuffer buffer ) { return new ConstraintRule( id, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleSerialization.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleSerialization.java index 9094703d44e19..46b221a625686 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleSerialization.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/SchemaRuleSerialization.java @@ -31,6 +31,7 @@ import org.neo4j.kernel.api.schema_new.SchemaProcessor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.UniquenessConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; @@ -50,7 +51,7 @@ public class SchemaRuleSerialization private static final byte GENERAL_INDEX = 31, UNIQUE_INDEX = 32; // Constraint type - private static final byte EXISTS_CONSTRAINT = 61, UNIQUE_CONSTRAINT = 62; + private static final byte EXISTS_CONSTRAINT = 61, UNIQUE_CONSTRAINT = 62, UNIQUE_EXISTS_CONSTRAINT = 63; // Schema type private static final byte SIMPLE_LABEL = 91, SIMPLE_REL_TYPE = 92; @@ -154,6 +155,11 @@ public static byte[] serialize( ConstraintRule constraintRule ) target.putLong( constraintRule.getOwnedIndex() ); break; + case UNIQUE_EXISTS: + target.put( UNIQUE_EXISTS_CONSTRAINT ); + target.putLong( constraintRule.getOwnedIndex() ); + break; + default: throw new UnsupportedOperationException( format( "Got unknown index descriptor type '%s'.", constraintDescriptor.type() ) ); @@ -202,7 +208,7 @@ public static int lengthOf( ConstraintRule constraintRule ) length += 1; // constraint type ConstraintDescriptor constraintDescriptor = constraintRule.getConstraintDescriptor(); - if ( constraintDescriptor.type() == ConstraintDescriptor.Type.UNIQUE ) + if ( constraintDescriptor.type().enforcesUniqueness() ) { length += 8; // owned index id } @@ -275,11 +281,18 @@ private static ConstraintRule readConstraintRule( long id, ByteBuffer source ) t return ConstraintRule.constraintRule( id, ConstraintDescriptorFactory.existsForSchema( schema ), name ); case UNIQUE_CONSTRAINT: - long ownedIndex = source.getLong(); + long ownedUniqueIndex = source.getLong(); schema = readSchema( source ); UniquenessConstraintDescriptor descriptor = ConstraintDescriptorFactory.uniqueForSchema( schema ); name = readRuleName( id, ConstraintRule.class, source ); - return ConstraintRule.constraintRule( id, descriptor, ownedIndex, name ); + return ConstraintRule.constraintRule( id, descriptor, ownedUniqueIndex, name ); + + case UNIQUE_EXISTS_CONSTRAINT: + long ownedNodeKeyIndex = source.getLong(); + schema = readSchema( source ); + NodeKeyConstraintDescriptor nodeKeyConstraintDescriptor = ConstraintDescriptorFactory.nodeKeyForSchema( schema ); + name = readRuleName( id, ConstraintRule.class, source ); + return ConstraintRule.constraintRule( id, nodeKeyConstraintDescriptor, ownedNodeKeyIndex, name ); default: throw new MalformedSchemaRuleException( format( "Got unknown constraint rule type '%d'.", constraintRuleType ) ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/IntegrityValidator.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/IntegrityValidator.java index 699986ff08e97..6a2335297ae2e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/IntegrityValidator.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/IntegrityValidator.java @@ -82,7 +82,7 @@ public void validateSchemaRule( SchemaRule schemaRule ) throws TransactionFailur if ( schemaRule instanceof ConstraintRule ) { ConstraintRule constraintRule = (ConstraintRule) schemaRule; - if ( constraintRule.getConstraintDescriptor().type() == ConstraintDescriptor.Type.UNIQUE ) + if ( constraintRule.getConstraintDescriptor().type().enforcesUniqueness() ) { try { diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/SchemaRule.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/SchemaRule.java index 00e10effc5fba..f63e77c8c7fa7 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/SchemaRule.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/SchemaRule.java @@ -107,7 +107,8 @@ public enum Kind CONSTRAINT_INDEX_RULE( "Constraint index" ), UNIQUENESS_CONSTRAINT( "Uniqueness constraint" ), NODE_PROPERTY_EXISTENCE_CONSTRAINT( "Node property existence constraint" ), - RELATIONSHIP_PROPERTY_EXISTENCE_CONSTRAINT( "Relationship property existence constraint" ); + RELATIONSHIP_PROPERTY_EXISTENCE_CONSTRAINT( "Relationship property existence constraint" ), + NODE_KEY_CONSTRAINT( "Node key constraint" ); private static final Kind[] ALL = values(); @@ -156,6 +157,8 @@ public static Kind map( ConstraintDescriptor descriptor ) { case UNIQUE: return UNIQUENESS_CONSTRAINT; + case UNIQUE_EXISTS: + return NODE_KEY_CONSTRAINT; case EXISTS: return descriptor.schema().computeWith( existenceKindMapper ); default: diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchInserterImpl.java b/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchInserterImpl.java index 4b72843d3ddc0..b4549eb8aa8a5 100644 --- a/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchInserterImpl.java +++ b/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchInserterImpl.java @@ -52,6 +52,8 @@ import org.neo4j.io.pagecache.PageCache; import org.neo4j.io.pagecache.tracing.PageCacheTracer; import org.neo4j.io.pagecache.tracing.cursor.PageCursorTracerSupplier; +import org.neo4j.kernel.api.constraints.NodeKeyConstraint; +import org.neo4j.kernel.api.constraints.UniquenessConstraint; import org.neo4j.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException; @@ -71,6 +73,7 @@ import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; +import org.neo4j.kernel.api.schema_new.constaints.IndexBackedConstraintDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; import org.neo4j.kernel.configuration.Config; @@ -90,6 +93,7 @@ import org.neo4j.kernel.impl.coreapi.schema.IndexCreatorImpl; import org.neo4j.kernel.impl.coreapi.schema.IndexDefinitionImpl; import org.neo4j.kernel.impl.coreapi.schema.InternalSchemaActions; +import org.neo4j.kernel.impl.coreapi.schema.NodeKeyConstraintDefinition; import org.neo4j.kernel.impl.coreapi.schema.NodePropertyExistenceConstraintDefinition; import org.neo4j.kernel.impl.coreapi.schema.RelationshipPropertyExistenceConstraintDefinition; import org.neo4j.kernel.impl.coreapi.schema.UniquenessConstraintDefinition; @@ -382,14 +386,23 @@ private void validateIndexCanBeCreated( int labelId, int[] propertyKeyIds ) private void validateUniquenessConstraintCanBeCreated( int labelId, int[] propertyKeyIds ) { verifyIndexOrUniquenessConstraintCanBeCreated( labelId, propertyKeyIds, - "It is not allowed to create uniqueness constraints and indexes on the same {label;property}" ); + "It is not allowed to create node keys, uniqueness constraints or indexes on the same {label;property}" ); + } + + private void validateNodeKeyConstraintCanBeCreated( int labelId, int[] propertyKeyIds ) + { + verifyIndexOrUniquenessConstraintCanBeCreated( labelId, propertyKeyIds, + "It is not allowed to create node keys, uniqueness constraints or indexes on the same {label;property}" ); } private void verifyIndexOrUniquenessConstraintCanBeCreated( int labelId, int[] propertyKeyIds, String errorMessage ) { LabelSchemaDescriptor schemaDescriptor = SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ); - ConstraintDescriptor constraintDescriptor = ConstraintDescriptorFactory.uniqueForLabel( labelId, propertyKeyIds ); - if ( schemaCache.hasIndexRule( schemaDescriptor ) || schemaCache.hasConstraintRule( constraintDescriptor ) ) + ConstraintDescriptor constraintDescriptor = ConstraintDescriptorFactory.uniqueForSchema( schemaDescriptor ); + ConstraintDescriptor nodeKeyDescriptor = ConstraintDescriptorFactory.nodeKeyForSchema( schemaDescriptor ); + if ( schemaCache.hasIndexRule( schemaDescriptor ) || + schemaCache.hasConstraintRule( constraintDescriptor ) || + schemaCache.hasConstraintRule( nodeKeyDescriptor ) ) { throw new ConstraintViolationException( errorMessage ); } @@ -549,7 +562,8 @@ public ConstraintCreator createDeferredConstraint( Label label ) return new BaseNodeConstraintCreator( new BatchSchemaActions(), label ); } - private void createUniquenessConstraintRule( LabelSchemaDescriptor schemaDescriptor ) + private void createUniqueIndexAndOwningConstraint( NewIndexDescriptor indexDescriptor, + IndexBackedConstraintDescriptor constraintDescriptor ) { // TODO: Do not create duplicate index @@ -558,16 +572,16 @@ private void createUniquenessConstraintRule( LabelSchemaDescriptor schemaDescrip IndexRule indexRule = IndexRule.constraintIndexRule( - indexRuleId, - NewIndexDescriptorFactory.uniqueForSchema( schemaDescriptor ), - this.schemaIndexProviders.getDefaultProvider().getProviderDescriptor(), - constraintRuleId + indexRuleId, + indexDescriptor, + this.schemaIndexProviders.getDefaultProvider().getProviderDescriptor(), + constraintRuleId ); ConstraintRule constraintRule = ConstraintRule.constraintRule( - constraintRuleId, - ConstraintDescriptorFactory.uniqueForSchema( schemaDescriptor ), - indexRuleId + constraintRuleId, + constraintDescriptor, + indexRuleId ); for ( DynamicRecord record : schemaStore.allocateFrom( constraintRule ) ) @@ -584,6 +598,20 @@ private void createUniquenessConstraintRule( LabelSchemaDescriptor schemaDescrip flushStrategy.forceFlush(); } + private void createUniquenessConstraintRule( LabelSchemaDescriptor descriptor ) + { + createUniqueIndexAndOwningConstraint( + NewIndexDescriptorFactory.uniqueForSchema( descriptor ), + ConstraintDescriptorFactory.uniqueForSchema( descriptor ) ); + } + + private void createNodeKeyConstraintRule( LabelSchemaDescriptor descriptor ) + { + createUniqueIndexAndOwningConstraint( + NewIndexDescriptorFactory.uniqueForSchema( descriptor ), + ConstraintDescriptorFactory.nodeKeyForSchema( descriptor ) ); + } + private void createNodePropertyExistenceConstraintRule( int labelId, int... propertyKeyIds ) { SchemaRule rule = ConstraintRule.constraintRule( schemaStore.nextId(), @@ -1126,6 +1154,18 @@ public ConstraintDefinition createPropertyUniquenessConstraint( IndexDefinition return new UniquenessConstraintDefinition( this, indexDefinition ); } + @Override + public ConstraintDefinition createNodeKeyConstraint( IndexDefinition indexDefinition ) + { + int labelId = getOrCreateLabelId( indexDefinition.getLabel().name() ); + int[] propertyKeyIds = getOrCreatePropertyKeyIds( indexDefinition.getPropertyKeys() ); + LabelSchemaDescriptor descriptor = SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ); + + validateNodeKeyConstraintCanBeCreated( labelId, propertyKeyIds ); + createNodeKeyConstraintRule( descriptor ); + return new NodeKeyConstraintDefinition( this, indexDefinition ); + } + @Override public ConstraintDefinition createPropertyExistenceConstraint( Label label, String... propertyKeys ) { @@ -1157,6 +1197,12 @@ public void dropPropertyUniquenessConstraint( Label label, String[] properties ) throw unsupportedException(); } + @Override + public void dropNodeKeyConstraint( Label label, String[] properties ) + { + throw unsupportedException(); + } + @Override public void dropNodePropertyExistenceConstraint( Label label, String[] properties ) { diff --git a/community/kernel/src/test/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactoryTest.java b/community/kernel/src/test/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactoryTest.java index 608a0f94b713f..53d8113028fd7 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactoryTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/api/schema_new/constaints/ConstraintDescriptorFactoryTest.java @@ -57,6 +57,16 @@ public void shouldCreateUniqueConstraintDescriptors() assertThat( desc.schema(), equalTo( SchemaDescriptorFactory.forLabel( LABEL_ID, 1 ) ) ); } + @Test + public void shouldCreateNodeKeyConstraintDescriptors() + { + ConstraintDescriptor desc; + + desc = ConstraintDescriptorFactory.nodeKeyForLabel( LABEL_ID, 1 ); + assertThat( desc.type(), equalTo( ConstraintDescriptor.Type.UNIQUE_EXISTS ) ); + assertThat( desc.schema(), equalTo( SchemaDescriptorFactory.forLabel( LABEL_ID, 1 ) ) ); + } + @Test public void shouldCreateConstraintDescriptorsFromSchema() { @@ -66,6 +76,10 @@ public void shouldCreateConstraintDescriptorsFromSchema() assertThat( desc.type(), equalTo( ConstraintDescriptor.Type.UNIQUE ) ); assertThat( desc.schema(), equalTo( SchemaDescriptorFactory.forLabel( LABEL_ID, 1 ) ) ); + desc = ConstraintDescriptorFactory.nodeKeyForSchema( SchemaDescriptorFactory.forLabel( LABEL_ID, 1 ) ); + assertThat( desc.type(), equalTo( ConstraintDescriptor.Type.UNIQUE_EXISTS ) ); + assertThat( desc.schema(), equalTo( SchemaDescriptorFactory.forLabel( LABEL_ID, 1 ) ) ); + desc = ConstraintDescriptorFactory.existsForSchema( SchemaDescriptorFactory.forRelType( REL_TYPE_ID, 1 ) ); assertThat( desc.type(), equalTo( ConstraintDescriptor.Type.EXISTS) ); assertThat( desc.schema(), equalTo( SchemaDescriptorFactory.forRelType( REL_TYPE_ID, 1 ) ) ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/SchemaCacheTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/SchemaCacheTest.java index 8ff653070bf1d..90dd48cb2df34 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/SchemaCacheTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/SchemaCacheTest.java @@ -46,7 +46,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.neo4j.helpers.collection.Iterators.asSet; -import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.EXISTS; import static org.neo4j.kernel.impl.api.index.TestSchemaIndexProviderDescriptor.PROVIDER_DESCRIPTOR; public class SchemaCacheTest @@ -285,9 +284,9 @@ private static SchemaCache newSchemaCache( SchemaRule... rules ) private static class ConstraintSemantics extends StandardConstraintSemantics { @Override - protected ConstraintDescriptor readNonStandardConstraint( ConstraintRule rule ) + protected ConstraintDescriptor readNonStandardConstraint( ConstraintRule rule, String errorMessage ) { - if ( rule.getConstraintDescriptor().type() != EXISTS ) + if ( !rule.getConstraintDescriptor().type().enforcesPropertyExistence() ) { throw new IllegalStateException( "Unsupported constraint type: " + rule ); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/SchemaStorageTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/SchemaStorageTest.java index 63d65fce96512..1eab1ec8ca730 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/SchemaStorageTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/SchemaStorageTest.java @@ -33,23 +33,37 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import org.neo4j.graphdb.ConstraintViolationException; import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.InvalidTransactionTypeException; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.schema.ConstraintDefinition; import org.neo4j.graphdb.schema.IndexCreator; import org.neo4j.helpers.collection.Iterators; import org.neo4j.kernel.api.ReadOperations; import org.neo4j.kernel.api.Statement; +import org.neo4j.kernel.api.StatementTokenNameLookup; import org.neo4j.kernel.api.TokenNameLookup; +import org.neo4j.kernel.api.exceptions.InvalidTransactionTypeKernelException; +import org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException; +import org.neo4j.kernel.api.exceptions.schema.AlreadyIndexedException; +import org.neo4j.kernel.api.exceptions.schema.CreateConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DuplicateSchemaRuleException; +import org.neo4j.kernel.api.exceptions.schema.IllegalTokenNameException; import org.neo4j.kernel.api.exceptions.schema.SchemaRuleNotFoundException; +import org.neo4j.kernel.api.exceptions.schema.TooManyLabelsException; +import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory; import org.neo4j.kernel.api.schema_new.SchemaDescriptorPredicates; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor; import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.coreapi.schema.IndexDefinitionImpl; +import org.neo4j.kernel.impl.coreapi.schema.InternalSchemaActions; +import org.neo4j.kernel.impl.coreapi.schema.NodeKeyConstraintDefinition; import org.neo4j.kernel.impl.storageengine.impl.recordstorage.RecordStorageEngine; import org.neo4j.kernel.impl.store.record.ConstraintRule; import org.neo4j.kernel.impl.store.record.IndexRule; @@ -64,8 +78,10 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Mockito.mock; import static org.neo4j.helpers.collection.Iterators.asSet; import static org.neo4j.kernel.impl.api.index.inmemory.InMemoryIndexProviderFactory.PROVIDER_DESCRIPTOR; +import static org.neo4j.kernel.impl.coreapi.schema.PropertyNameUtils.getOrCreatePropertyKeyIds; public class SchemaStorageTest { @@ -312,7 +328,7 @@ public void shouldThrowExceptionOnRelationshipDuplicateRuleFound() private TokenNameLookup getDefaultTokenNameLookup() { - TokenNameLookup tokenNameLookup = Mockito.mock( TokenNameLookup.class ); + TokenNameLookup tokenNameLookup = mock( TokenNameLookup.class ); Mockito.when( tokenNameLookup.labelGetName( labelId( LABEL1 ) ) ).thenReturn( LABEL1 ); Mockito.when( tokenNameLookup.propertyKeyGetName( propId( PROP1 ) ) ).thenReturn( PROP1 ); Mockito.when( tokenNameLookup.relationshipTypeGetName( typeId( TYPE1 ) ) ).thenReturn( TYPE1 ); @@ -426,6 +442,45 @@ private Consumer uniquenessConstraint( String label, Strin return db -> db.schema().constraintFor( Label.label( label ) ).assertPropertyIsUnique( prop ).create(); } + private Consumer nodeKeyConstraint( String label, String prop ) + { + return db -> createNodeKeyConstraint( label, prop ); + } + + private ConstraintDefinition createNodeKeyConstraint( String label, String prop ) + { + Statement statement = dependencyResolver().resolveDependency( ThreadToStatementContextBridge.class ).get(); + InternalSchemaActions actions = mock( InternalSchemaActions.class ); + IndexDefinitionImpl indexDefinition = new IndexDefinitionImpl( actions, Label.label( label ), prop, true ); + try + { + int labelId = statement.tokenWriteOperations().labelGetOrCreateForName( + indexDefinition.getLabel().name() ); + int[] propertyKeyIds = getOrCreatePropertyKeyIds( + statement.tokenWriteOperations(), indexDefinition ); + statement.schemaWriteOperations().nodeKeyConstraintCreate( + SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) ); + return new NodeKeyConstraintDefinition( actions, indexDefinition ); + } + catch ( AlreadyConstrainedException | CreateConstraintFailureException | AlreadyIndexedException e ) + { + throw new ConstraintViolationException( + e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); + } + catch ( IllegalTokenNameException e ) + { + throw new IllegalArgumentException( e ); + } + catch ( TooManyLabelsException e ) + { + throw new IllegalStateException( e ); + } + catch ( InvalidTransactionTypeKernelException e ) + { + throw new InvalidTransactionTypeException( e.getMessage(), e ); + } + } + @SafeVarargs private static void createSchema( Consumer... creators ) { diff --git a/community/kernel/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertTest.java b/community/kernel/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertTest.java index 56476b15b4795..2b0f086ef3dc4 100644 --- a/community/kernel/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertTest.java +++ b/community/kernel/src/test/java/org/neo4j/unsafe/batchinsert/internal/BatchInsertTest.java @@ -1325,7 +1325,7 @@ public void shouldNotAllowDuplicatedUniquenessConstraints() throws Exception { // Then assertEquals( - "It is not allowed to create uniqueness constraints and indexes on the same {label;property}", + "It is not allowed to create node keys, uniqueness constraints or indexes on the same {label;property}", e.getMessage() ); } } diff --git a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/CompositeConstraintAcceptanceTest.scala b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/CompositeConstraintAcceptanceTest.scala index ad67c60478f0f..2df509d81537f 100644 --- a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/CompositeConstraintAcceptanceTest.scala +++ b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/CompositeConstraintAcceptanceTest.scala @@ -19,16 +19,22 @@ */ package org.neo4j.internal.cypher.acceptance +import org.neo4j.cypher.javacompat.internal.GraphDatabaseCypherService import org.neo4j.cypher.{CypherExecutionException, ExecutionEngineFunSuite, NewPlannerTestSupport} import org.neo4j.graphdb.{ConstraintViolationException, Node} import org.neo4j.kernel.GraphDatabaseQueryService +import org.neo4j.test.{TestEnterpriseGraphDatabaseFactory, TestGraphDatabaseFactory} import org.scalatest.matchers.{MatchResult, Matcher} import scala.collection.JavaConverters._ class CompositeConstraintAcceptanceTest extends ExecutionEngineFunSuite with NewPlannerTestSupport { - test("should be able to create and remove composite uniquness constraints") { + override protected def createGraphDatabase(): GraphDatabaseCypherService = { + new GraphDatabaseCypherService(new TestEnterpriseGraphDatabaseFactory().newImpermanentDatabase(databaseConfig().asJava)) + } + + test("should be able to create and remove composite uniqueness constraints") { // When executeWithCostPlannerAndInterpretedRuntimeOnly("CREATE CONSTRAINT ON (n:Person) ASSERT n.email IS UNIQUE") executeWithCostPlannerAndInterpretedRuntimeOnly("CREATE CONSTRAINT ON (n:Person) ASSERT (n.firstname,n.lastname) IS UNIQUE") @@ -88,6 +94,36 @@ class CompositeConstraintAcceptanceTest extends ExecutionEngineFunSuite with New } } + test("should be able to create and remove single property NODE KEY") { + // When + executeWithCostPlannerAndInterpretedRuntimeOnly("CREATE CONSTRAINT ON (n:Person) ASSERT (n.email) IS NODE KEY") + + // Then + graph should haveConstraints("NODE_KEY:Person(email)") + + // When + executeWithCostPlannerAndInterpretedRuntimeOnly("DROP CONSTRAINT ON (n:Person) ASSERT (n.email) IS NODE KEY") + + // Then + graph should not(haveConstraints("NODE_KEY:Person(email)")) + } + + test("should be able to create and remove multiple property NODE KEY") { + // When + executeWithCostPlannerAndInterpretedRuntimeOnly("CREATE CONSTRAINT ON (n:Person) ASSERT (n.email) IS NODE KEY") + executeWithCostPlannerAndInterpretedRuntimeOnly("CREATE CONSTRAINT ON (n:Person) ASSERT (n.firstname,n.lastname) IS NODE KEY") + + // Then + graph should haveConstraints("NODE_KEY:Person(email)", "NODE_KEY:Person(firstname,lastname") + + // When + executeWithCostPlannerAndInterpretedRuntimeOnly("DROP CONSTRAINT ON (n:Person) ASSERT (n.firstname,n.lastname) IS NODE KEY") + + // Then + graph should haveConstraints("NODE_KEY:Person(email)") + graph should not(haveConstraints("NODE_KEY:Person(firstname,lastname)")) + } + case class haveConstraints(expectedConstraints: String*) extends Matcher[GraphDatabaseQueryService] { def apply(graph: GraphDatabaseQueryService): MatchResult = { graph.inTx { diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/EnterpriseConstraintSemantics.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/EnterpriseConstraintSemantics.java index f48c8d7ed6a55..a87b9daa6b48e 100644 --- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/EnterpriseConstraintSemantics.java +++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/EnterpriseConstraintSemantics.java @@ -33,6 +33,7 @@ import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema_new.SchemaProcessor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; +import org.neo4j.kernel.api.schema_new.constaints.NodeKeyConstraintDescriptor; import org.neo4j.kernel.impl.constraints.StandardConstraintSemantics; import org.neo4j.kernel.impl.store.record.ConstraintRule; import org.neo4j.storageengine.api.NodeItem; @@ -42,20 +43,26 @@ import org.neo4j.storageengine.api.txstate.TxStateVisitor; import static org.neo4j.kernel.api.exceptions.schema.ConstraintValidationException.Phase.VERIFICATION; -import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.EXISTS; public class EnterpriseConstraintSemantics extends StandardConstraintSemantics { @Override - protected ConstraintDescriptor readNonStandardConstraint( ConstraintRule rule ) + protected ConstraintDescriptor readNonStandardConstraint( ConstraintRule rule, String errorMessage ) { - if ( rule.getConstraintDescriptor().type() != EXISTS ) + if ( !rule.getConstraintDescriptor().type().enforcesPropertyExistence() ) { throw new IllegalStateException( "Unsupported constraint type: " + rule ); } return rule.getConstraintDescriptor(); } + @Override + public ConstraintRule createNodeKeyConstraintRule( + long ruleId, NodeKeyConstraintDescriptor descriptor, long indexId ) + { + return ConstraintRule.constraintRule( ruleId, descriptor, indexId ); + } + @Override public ConstraintRule createExistenceConstraint( long ruleId, ConstraintDescriptor descriptor ) { @@ -141,7 +148,7 @@ private static class ExistenceConstraintCollector implements SchemaProcessor void addIfRelevant( ConstraintDescriptor constraint ) { - if ( constraint.type() == EXISTS ) + if ( constraint.type().enforcesPropertyExistence() ) { constraint.schema().processWith( this ); } diff --git a/enterprise/kernel/src/test/java/org/neo4j/SchemaHelper.java b/enterprise/kernel/src/test/java/org/neo4j/SchemaHelper.java index 0e9a9d26ce988..2179491b24ddd 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/SchemaHelper.java +++ b/enterprise/kernel/src/test/java/org/neo4j/SchemaHelper.java @@ -53,6 +53,16 @@ public static void createUniquenessConstraint( GraphDatabaseService db, String l db.execute( String.format( "CREATE CONSTRAINT ON (n:`%s`) ASSERT n.`%s` IS UNIQUE", label, property ) ); } + public static void createNodeKeyConstraint( GraphDatabaseService db, Label label, String property ) + { + createNodeKeyConstraint( db, label.name(), property ); + } + + public static void createNodeKeyConstraint( GraphDatabaseService db, String label, String property ) + { + db.execute( String.format( "CREATE CONSTRAINT ON (n:`%s`) ASSERT (n.`%s`) IS NODE KEY", label, property ) ); + } + public static void createNodePropertyExistenceConstraint( GraphDatabaseService db, Label label, String property ) { createNodePropertyExistenceConstraint( db, label.name(), property ); diff --git a/enterprise/kernel/src/test/java/org/neo4j/graphdb/SchemaWithPECAcceptanceTest.java b/enterprise/kernel/src/test/java/org/neo4j/graphdb/SchemaWithPECAcceptanceTest.java index 620d186f87c3c..7e79442603120 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/graphdb/SchemaWithPECAcceptanceTest.java +++ b/enterprise/kernel/src/test/java/org/neo4j/graphdb/SchemaWithPECAcceptanceTest.java @@ -28,6 +28,7 @@ import org.neo4j.graphdb.schema.IndexDefinition; import org.neo4j.kernel.impl.coreapi.schema.IndexDefinitionImpl; import org.neo4j.kernel.impl.coreapi.schema.InternalSchemaActions; +import org.neo4j.kernel.impl.coreapi.schema.NodeKeyConstraintDefinition; import org.neo4j.kernel.impl.coreapi.schema.NodePropertyExistenceConstraintDefinition; import org.neo4j.kernel.impl.coreapi.schema.RelationshipPropertyExistenceConstraintDefinition; import org.neo4j.kernel.impl.coreapi.schema.UniquenessConstraintDefinition; @@ -45,7 +46,9 @@ public class SchemaWithPECAcceptanceTest private GraphDatabaseService db; private Label label = Labels.MY_LABEL; + private Label label2 = Labels.MY_OTHER_LABEL; private String propertyKey = "my_property_key"; + private String propertyKey2 = "my_other_property"; private enum Labels implements Label { @@ -91,10 +94,12 @@ public void shouldListAddedConstraintsByLabel() throws Exception // GIVEN ConstraintDefinition constraint1 = createUniquenessConstraint( label, propertyKey ); ConstraintDefinition constraint2 = createNodePropertyExistenceConstraint( label, propertyKey ); + ConstraintDefinition constraint3 = createNodeKeyConstraint( label, propertyKey2 ); + createNodeKeyConstraint( label2, propertyKey2 ); createNodePropertyExistenceConstraint( Labels.MY_OTHER_LABEL, propertyKey ); // WHEN THEN - assertThat( getConstraints( db, label ), containsOnly( constraint1, constraint2 ) ); + assertThat( getConstraints( db, label ), containsOnly( constraint1, constraint2, constraint3 ) ); } @Test @@ -115,9 +120,10 @@ public void shouldListAddedConstraints() throws Exception ConstraintDefinition constraint1 = createUniquenessConstraint( label, propertyKey ); ConstraintDefinition constraint2 = createNodePropertyExistenceConstraint( label, propertyKey ); ConstraintDefinition constraint3 = createRelationshipPropertyExistenceConstraint( Types.MY_TYPE, propertyKey ); + ConstraintDefinition constraint4 = createNodeKeyConstraint( label, propertyKey2 ); // WHEN THEN - assertThat( getConstraints( db ), containsOnly( constraint1, constraint2, constraint3 ) ); + assertThat( getConstraints( db ), containsOnly( constraint1, constraint2, constraint3, constraint4 ) ); } private ConstraintDefinition createUniquenessConstraint( Label label, String propertyKey ) @@ -129,6 +135,15 @@ private ConstraintDefinition createUniquenessConstraint( Label label, String pro return new UniquenessConstraintDefinition( actions, index ); } + private ConstraintDefinition createNodeKeyConstraint( Label label, String propertyKey ) + { + SchemaHelper.createNodeKeyConstraint( db, label, propertyKey ); + SchemaHelper.awaitIndexes( db ); + InternalSchemaActions actions = mock( InternalSchemaActions.class ); + IndexDefinition index = new IndexDefinitionImpl( actions, label, new String[]{propertyKey}, true ); + return new NodeKeyConstraintDefinition( actions, index ); + } + private ConstraintDefinition createNodePropertyExistenceConstraint( Label label, String propertyKey ) { SchemaHelper.createNodePropertyExistenceConstraint( db, label, propertyKey ); diff --git a/enterprise/kernel/src/test/java/org/neo4j/graphdb/StartupConstraintSemanticsTest.java b/enterprise/kernel/src/test/java/org/neo4j/graphdb/StartupConstraintSemanticsTest.java index d7bb580777cc2..8a61d5ae4eb5f 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/graphdb/StartupConstraintSemanticsTest.java +++ b/enterprise/kernel/src/test/java/org/neo4j/graphdb/StartupConstraintSemanticsTest.java @@ -64,7 +64,7 @@ public void shouldNotAllowOpeningADatabaseWithPECInCommunityEdition() throws Exc { Throwable error = Exceptions.rootCause( e ); assertThat( error, instanceOf( IllegalStateException.class ) ); - assertEquals( StandardConstraintSemantics.ERROR_MESSAGE, error.getMessage() ); + assertEquals( StandardConstraintSemantics.ERROR_MESSAGE_EXISTS, error.getMessage() ); } finally { diff --git a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerSchemaWithPECTest.java b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerSchemaWithPECTest.java index f8684c267d59f..9f6203acb34b9 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerSchemaWithPECTest.java +++ b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StorageLayerSchemaWithPECTest.java @@ -52,6 +52,8 @@ public void shouldListAllConstraints() // Given SchemaHelper.createUniquenessConstraint( db, label1, propertyKey ); SchemaHelper.createUniquenessConstraint( db, label2, propertyKey ); + SchemaHelper.createNodeKeyConstraint( db, label1, otherPropertyKey ); + SchemaHelper.createNodeKeyConstraint( db, label2, otherPropertyKey ); SchemaHelper.createNodePropertyExistenceConstraint( db, label2, propertyKey ); SchemaHelper.createRelPropertyExistenceConstraint( db, relType1, propertyKey ); @@ -66,10 +68,13 @@ public void shouldListAllConstraints() int labelId2 = labelId( label2 ); int relTypeId = relationshipTypeId( relType1 ); int propKeyId = propertyKeyId( propertyKey ); + int propKeyId2 = propertyKeyId( otherPropertyKey ); assertThat( constraints, containsInAnyOrder( ConstraintDescriptorFactory.uniqueForLabel( labelId1, propKeyId ), ConstraintDescriptorFactory.uniqueForLabel( labelId2, propKeyId ), + ConstraintDescriptorFactory.nodeKeyForLabel( labelId1, propKeyId2 ), + ConstraintDescriptorFactory.nodeKeyForLabel( labelId2, propKeyId2 ), ConstraintDescriptorFactory.existsForLabel( labelId2, propKeyId ), ConstraintDescriptorFactory.existsForRelType( relTypeId, propKeyId ) ) ); @@ -83,6 +88,8 @@ public void shouldListAllConstraintsForLabel() SchemaHelper.createNodePropertyExistenceConstraint( db, label2, propertyKey ); SchemaHelper.createUniquenessConstraint( db, label1, propertyKey ); + SchemaHelper.createNodeKeyConstraint( db, label1, otherPropertyKey ); + SchemaHelper.createNodeKeyConstraint( db, label2, otherPropertyKey ); SchemaHelper.awaitIndexes( db ); @@ -92,6 +99,7 @@ public void shouldListAllConstraintsForLabel() // Then Set expectedConstraints = asSet( uniqueConstraintDescriptor( label1, propertyKey ), + nodeKeyConstraintDescriptor( label1, otherPropertyKey ), nodePropertyExistenceDescriptor( label1, propertyKey ) ); assertEquals( expectedConstraints, constraints ); @@ -101,8 +109,10 @@ public void shouldListAllConstraintsForLabel() public void shouldListAllConstraintsForLabelAndProperty() { // Given - SchemaHelper.createUniquenessConstraint( db, label1, propertyKey ); + SchemaHelper.createUniquenessConstraint( db, label2, propertyKey ); SchemaHelper.createUniquenessConstraint( db, label1, otherPropertyKey ); + SchemaHelper.createNodeKeyConstraint( db, label1, propertyKey ); + SchemaHelper.createNodeKeyConstraint( db, label2, otherPropertyKey ); SchemaHelper.createNodePropertyExistenceConstraint( db, label1, propertyKey ); SchemaHelper.createNodePropertyExistenceConstraint( db, label2, propertyKey ); @@ -115,7 +125,7 @@ public void shouldListAllConstraintsForLabelAndProperty() // Then Set expected = asSet( - uniqueConstraintDescriptor( label1, propertyKey ), + nodeKeyConstraintDescriptor( label1, propertyKey ), nodePropertyExistenceDescriptor( label1, propertyKey ) ); assertEquals( expected, constraints ); @@ -171,6 +181,13 @@ private ConstraintDescriptor uniqueConstraintDescriptor( Label label, String pro return ConstraintDescriptorFactory.uniqueForLabel( labelId, propKeyId ); } + private ConstraintDescriptor nodeKeyConstraintDescriptor( Label label, String propertyKey ) + { + int labelId = labelId( label ); + int propKeyId = propertyKeyId( propertyKey ); + return ConstraintDescriptorFactory.nodeKeyForLabel( labelId, propKeyId ); + } + private ConstraintDescriptor nodePropertyExistenceDescriptor( Label label, String propertyKey ) { int labelId = labelId( label ); diff --git a/enterprise/kernel/src/test/java/org/neo4j/shell/PECListingIT.java b/enterprise/kernel/src/test/java/org/neo4j/shell/PECListingIT.java index 8d22b796a10b9..fd176cc5261c0 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/shell/PECListingIT.java +++ b/enterprise/kernel/src/test/java/org/neo4j/shell/PECListingIT.java @@ -138,6 +138,7 @@ public void shouldHaveCorrectIndentationsInSchemaListing() throws Exception // WHEN SchemaHelper.createUniquenessConstraint( db, label, "name" ); +// SchemaHelper.createNodeKeyConstraint( db, label, "surname" ); SchemaHelper.createNodePropertyExistenceConstraint( db, label, "name" ); SchemaHelper.createRelPropertyExistenceConstraint( db, relType, "since" ); @@ -147,8 +148,10 @@ public void shouldHaveCorrectIndentationsInSchemaListing() throws Exception executeCommand( "schema", "Indexes", " ON :Person\\(name\\) ONLINE \\(for uniqueness constraint\\)", +// " ON :Person\\(surname\\) ONLINE \\(for uniqueness constraint\\)", "Constraints", " ON \\(person:Person\\) ASSERT person.name IS UNIQUE", +// " ON \\(person:Person\\) ASSERT person.surname IS NODE KEY", " ON \\(person:Person\\) ASSERT exists\\(person.name\\)", " ON \\(\\)-\\[knows:KNOWS\\]-\\(\\) ASSERT exists\\(knows.since\\)" ); }