diff --git a/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java b/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java index 746ad2ec53dc3..ea657e9e9d5ef 100644 --- a/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java +++ b/community/common/src/main/java/org/neo4j/kernel/api/exceptions/Status.java @@ -298,6 +298,8 @@ public Code code() enum Schema implements Status { // client errors + RepeatedPropertyInCompositeSchema( ClientError, + "Unable to create composite index or constraint because a property was specified in several positions." ), ConstraintAlreadyExists( ClientError, "Unable to perform operation because it would clash with a pre-existing constraint." ), ConstraintNotFound( ClientError, 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 82e28467d0438..3c08f470c5367 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 @@ -24,6 +24,7 @@ import org.neo4j.kernel.api.exceptions.schema.CreateConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropIndexFailureException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; 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; @@ -40,7 +41,7 @@ public interface SchemaWriteOperations * @param schemaDescriptor */ NewIndexDescriptor indexCreate( LabelSchemaDescriptor schemaDescriptor ) - throws AlreadyIndexedException, AlreadyConstrainedException; + throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException; /** Drops a {@link NewIndexDescriptor} from the database */ void indexDrop( NewIndexDescriptor descriptor ) throws DropIndexFailureException; @@ -52,14 +53,17 @@ NewIndexDescriptor indexCreate( LabelSchemaDescriptor schemaDescriptor ) void uniqueIndexDrop( NewIndexDescriptor descriptor ) throws DropIndexFailureException; UniquenessConstraintDescriptor uniquePropertyConstraintCreate( LabelSchemaDescriptor descriptor ) - throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException; + throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException, + RepeatedPropertyInCompositeSchemaException; NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( LabelSchemaDescriptor descriptor ) - throws CreateConstraintFailureException, AlreadyConstrainedException; + throws CreateConstraintFailureException, AlreadyConstrainedException, + RepeatedPropertyInCompositeSchemaException; RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate( - RelationTypeSchemaDescriptor relationshipPropertyDescriptor ) throws - CreateConstraintFailureException, AlreadyConstrainedException; + RelationTypeSchemaDescriptor relationshipPropertyDescriptor ) + throws CreateConstraintFailureException, AlreadyConstrainedException, + RepeatedPropertyInCompositeSchemaException; void constraintDrop( ConstraintDescriptor constraint ) throws DropConstraintFailureException; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedPropertyInCompositeSchemaException.java b/community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedPropertyInCompositeSchemaException.java new file mode 100644 index 0000000000000..89a17e3b4bb5b --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/exceptions/schema/RepeatedPropertyInCompositeSchemaException.java @@ -0,0 +1,68 @@ +/* + * 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.exceptions.schema; + +import org.neo4j.kernel.api.TokenNameLookup; +import org.neo4j.kernel.api.exceptions.Status; +import org.neo4j.kernel.api.schema_new.SchemaDescriptor; +import org.neo4j.kernel.api.schema_new.SchemaUtil; + +public class RepeatedPropertyInCompositeSchemaException extends SchemaKernelException +{ + private final SchemaDescriptor schema; + private final OperationContext context; + + public RepeatedPropertyInCompositeSchemaException( SchemaDescriptor schema, OperationContext context ) + { + super( Status.Schema.RepeatedPropertyInCompositeSchema, format( + schema, context, SchemaUtil.idTokenNameLookup ) ); + this.schema = schema; + this.context = context; + } + + @Override + public String getUserMessage( TokenNameLookup tokenNameLookup ) + { + return format( schema, context, tokenNameLookup ); + } + + private static String format( + SchemaDescriptor schema, OperationContext context, TokenNameLookup tokenNameLookup ) + { + String schemaName; + switch ( context ) + { + case INDEX_CREATION: + schemaName = "Index"; + break; + + case CONSTRAINT_CREATION: + schemaName = "Constraint"; + break; + + default: + schemaName = "Schema object"; + break; + } + return String.format( "%s on %s includes a property more than once.", + schemaName, schema.userDescription( tokenNameLookup ) ); + + } +} 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 d382e66c66c65..6c70f69295163 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 @@ -44,6 +44,7 @@ import org.neo4j.kernel.api.exceptions.schema.DropConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropIndexFailureException; import org.neo4j.kernel.api.exceptions.schema.IndexBrokenKernelException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.UnableToValidateConstraintException; import org.neo4j.kernel.api.exceptions.schema.UniquePropertyValueValidationException; import org.neo4j.kernel.api.properties.DefinedProperty; @@ -539,7 +540,7 @@ public Cursor nodeGetRelationships( KernelStatement statement, @Override public NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyIndexedException, AlreadyConstrainedException + throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException { return schemaWriteOperations.indexCreate( state, descriptor ); } @@ -558,7 +559,8 @@ public void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor descripto @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException + throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException, + RepeatedPropertyInCompositeSchemaException { return schemaWriteOperations.uniquePropertyConstraintCreate( state, descriptor ); } @@ -567,7 +569,7 @@ public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStat public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor - ) throws AlreadyConstrainedException, CreateConstraintFailureException + ) throws AlreadyConstrainedException, CreateConstraintFailureException, RepeatedPropertyInCompositeSchemaException { Iterator> nodes = new NodeLoadingIterator( nodesGetForLabel( state, descriptor.getLabelId() ), ( id ) -> nodeCursorById( state, id ) ); @@ -580,7 +582,7 @@ public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( public RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate( KernelStatement state, RelationTypeSchemaDescriptor descriptor - ) throws AlreadyConstrainedException, CreateConstraintFailureException + ) throws AlreadyConstrainedException, CreateConstraintFailureException, RepeatedPropertyInCompositeSchemaException { try ( Cursor cursor = relationshipCursorGetAll( state ) ) { 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 ee7bcf3187cba..eef367e33c091 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 @@ -19,7 +19,8 @@ */ package org.neo4j.kernel.impl.api; -import org.neo4j.kernel.api.schema.NodePropertyDescriptor; +import java.util.Arrays; + import org.neo4j.kernel.api.Statement; import org.neo4j.kernel.api.StatementTokenNameLookup; import org.neo4j.kernel.api.constraints.UniquenessConstraint; @@ -32,11 +33,14 @@ import org.neo4j.kernel.api.exceptions.schema.IndexBelongsToConstraintException; import org.neo4j.kernel.api.exceptions.schema.NoSuchConstraintException; import org.neo4j.kernel.api.exceptions.schema.NoSuchIndexException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.SchemaKernelException.OperationContext; import org.neo4j.kernel.api.exceptions.schema.TooManyLabelsException; +import org.neo4j.kernel.api.schema.NodePropertyDescriptor; import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor; import org.neo4j.kernel.api.schema_new.SchemaBoundary; +import org.neo4j.kernel.api.schema_new.SchemaDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintBoundary; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor; import org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptorFactory; @@ -111,9 +115,10 @@ public void relationshipTypeCreateForName( KernelStatement state, String relatio @Override public NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyIndexedException, AlreadyConstrainedException + throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException { - checkIndexExistence( state, OperationContext.INDEX_CREATION, SchemaBoundary.map( descriptor ) ); + assertValidDescriptor( descriptor, OperationContext.INDEX_CREATION ); + assertIndexDoesNotExist( state, OperationContext.INDEX_CREATION, SchemaBoundary.map( descriptor ) ); return schemaWriteDelegate.indexCreate( state, descriptor ); } @@ -151,21 +156,25 @@ public void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor index ) t @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException + throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException, + RepeatedPropertyInCompositeSchemaException { + assertValidDescriptor( descriptor, OperationContext.CONSTRAINT_CREATION ); ConstraintDescriptor constraint = ConstraintDescriptorFactory.uniqueForSchema( descriptor ); assertConstraintDoesNotExist( state, constraint ); // It is not allowed to create uniqueness constraints on indexed label/property pairs - checkIndexExistence( state, OperationContext.CONSTRAINT_CREATION, SchemaBoundary.map( descriptor ) ); + assertIndexDoesNotExist( state, OperationContext.CONSTRAINT_CREATION, SchemaBoundary.map( descriptor ) ); return schemaWriteDelegate.uniquePropertyConstraintCreate( state, descriptor ); } @Override public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( KernelStatement state, - LabelSchemaDescriptor descriptor) throws AlreadyConstrainedException, CreateConstraintFailureException + LabelSchemaDescriptor descriptor) throws AlreadyConstrainedException, CreateConstraintFailureException, + RepeatedPropertyInCompositeSchemaException { + assertValidDescriptor( descriptor, OperationContext.CONSTRAINT_CREATION ); ConstraintDescriptor constraint = ConstraintDescriptorFactory.existsForSchema( descriptor ); assertConstraintDoesNotExist( state, constraint ); return schemaWriteDelegate.nodePropertyExistenceConstraintCreate( state, descriptor ); @@ -173,8 +182,11 @@ public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( @Override public RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate( KernelStatement state, - RelationTypeSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException + RelationTypeSchemaDescriptor descriptor ) + throws AlreadyConstrainedException, CreateConstraintFailureException, + RepeatedPropertyInCompositeSchemaException { + assertValidDescriptor( descriptor, OperationContext.CONSTRAINT_CREATION ); ConstraintDescriptor constraint = ConstraintDescriptorFactory.existsForSchema( descriptor ); assertConstraintDoesNotExist( state, constraint ); return schemaWriteDelegate.relationshipPropertyExistenceConstraintCreate( state, descriptor ); @@ -194,7 +206,7 @@ public void constraintDrop( KernelStatement state, ConstraintDescriptor descript schemaWriteDelegate.constraintDrop( state, descriptor ); } - private void checkIndexExistence( KernelStatement state, OperationContext context, + private void assertIndexDoesNotExist( KernelStatement state, OperationContext context, NodePropertyDescriptor descriptor ) throws AlreadyIndexedException, AlreadyConstrainedException { @@ -238,4 +250,14 @@ private void assertConstraintExists( KernelStatement state, ConstraintDescriptor throw new NoSuchConstraintException( ConstraintBoundary.map( constraint ) ); } } + + private void assertValidDescriptor( SchemaDescriptor descriptor, OperationContext context ) + throws RepeatedPropertyInCompositeSchemaException + { + int numUnique = Arrays.stream( descriptor.getPropertyIds() ).distinct().toArray().length; + if ( numUnique != descriptor.getPropertyIds().length ) + { + throw new RepeatedPropertyInCompositeSchemaException( descriptor, context ); + } + } } 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 0fa1a9d3f3dfb..e11a1ad7e5c61 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 @@ -36,6 +36,7 @@ import org.neo4j.kernel.api.exceptions.schema.CreateConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropIndexFailureException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.SchemaRuleNotFoundException; import org.neo4j.kernel.api.index.InternalIndexState; import org.neo4j.kernel.api.properties.DefinedProperty; @@ -127,7 +128,7 @@ public boolean nodeRemoveLabel( KernelStatement state, long nodeId, int labelId @Override public NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyIndexedException, AlreadyConstrainedException + throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException { acquireExclusiveSchemaLock( state ); state.assertOpen(); @@ -345,7 +346,8 @@ private void lockRelationshipNodes( KernelStatement state, long startNodeId, lon @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state,LabelSchemaDescriptor descriptor ) - throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException + throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException, + RepeatedPropertyInCompositeSchemaException { acquireExclusiveSchemaLock( state ); state.assertOpen(); @@ -354,7 +356,8 @@ public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStat @Override public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( KernelStatement state, - LabelSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException + LabelSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException, + RepeatedPropertyInCompositeSchemaException { acquireExclusiveSchemaLock( state ); state.assertOpen(); @@ -363,7 +366,9 @@ public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( @Override public RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate( KernelStatement state, - RelationTypeSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException + RelationTypeSchemaDescriptor descriptor ) + throws AlreadyConstrainedException, CreateConstraintFailureException, + RepeatedPropertyInCompositeSchemaException { acquireExclusiveSchemaLock( state ); state.assertOpen(); 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 02a80a998267e..774135709ed60 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 @@ -63,6 +63,7 @@ import org.neo4j.kernel.api.exceptions.schema.DuplicateSchemaRuleException; import org.neo4j.kernel.api.exceptions.schema.IllegalTokenNameException; import org.neo4j.kernel.api.exceptions.schema.IndexBrokenKernelException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.SchemaRuleNotFoundException; import org.neo4j.kernel.api.exceptions.schema.TooManyLabelsException; import org.neo4j.kernel.api.index.InternalIndexState; @@ -989,7 +990,7 @@ public Property graphRemoveProperty( int propertyKeyId ) // @Override public NewIndexDescriptor indexCreate( LabelSchemaDescriptor descriptor ) - throws AlreadyIndexedException, AlreadyConstrainedException + throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException { statement.assertOpen(); return schemaWrite().indexCreate( statement, descriptor ); @@ -1004,7 +1005,8 @@ public void indexDrop( NewIndexDescriptor descriptor ) throws DropIndexFailureEx @Override public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( LabelSchemaDescriptor descriptor ) - throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException + throws CreateConstraintFailureException, AlreadyConstrainedException, AlreadyIndexedException, + RepeatedPropertyInCompositeSchemaException { statement.assertOpen(); return schemaWrite().uniquePropertyConstraintCreate( statement, descriptor ); @@ -1012,7 +1014,8 @@ public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( LabelSchem @Override public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( LabelSchemaDescriptor descriptor ) - throws CreateConstraintFailureException, AlreadyConstrainedException + throws CreateConstraintFailureException, AlreadyConstrainedException, + RepeatedPropertyInCompositeSchemaException { statement.assertOpen(); return schemaWrite().nodePropertyExistenceConstraintCreate( statement, descriptor ); @@ -1021,7 +1024,8 @@ public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( @Override public RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate( RelationTypeSchemaDescriptor descriptor ) - throws CreateConstraintFailureException, AlreadyConstrainedException + throws CreateConstraintFailureException, AlreadyConstrainedException, + RepeatedPropertyInCompositeSchemaException { statement.assertOpen(); return schemaWrite().relationshipPropertyExistenceConstraintCreate( statement, descriptor ); 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 0fc1c47a47ba0..e5e3e3eb4875f 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 @@ -24,6 +24,7 @@ import org.neo4j.kernel.api.exceptions.schema.CreateConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropIndexFailureException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; 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; @@ -40,7 +41,7 @@ public interface SchemaWriteOperations * {@code labelId}. */ NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyIndexedException, AlreadyConstrainedException; + throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException; /** Drops a {@link NewIndexDescriptor} from the database */ void indexDrop( KernelStatement state, NewIndexDescriptor descriptor ) throws DropIndexFailureException; @@ -52,13 +53,17 @@ NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor des void uniqueIndexDrop( KernelStatement state, NewIndexDescriptor descriptor ) throws DropIndexFailureException; UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStatement state, LabelSchemaDescriptor descriptor ) - throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException; + throws AlreadyConstrainedException, CreateConstraintFailureException, AlreadyIndexedException, + RepeatedPropertyInCompositeSchemaException; NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( KernelStatement state, - LabelSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException; + LabelSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException, + RepeatedPropertyInCompositeSchemaException; RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate( KernelStatement state, - RelationTypeSchemaDescriptor descriptor ) throws AlreadyConstrainedException, CreateConstraintFailureException; + RelationTypeSchemaDescriptor descriptor ) + throws AlreadyConstrainedException, CreateConstraintFailureException, + RepeatedPropertyInCompositeSchemaException; void constraintDrop( KernelStatement state, ConstraintDescriptor constraint ) throws DropConstraintFailureException; } 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 7fc7f448190e4..72741d70de3b5 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 @@ -59,11 +59,11 @@ import org.neo4j.kernel.api.exceptions.schema.DropConstraintFailureException; import org.neo4j.kernel.api.exceptions.schema.DropIndexFailureException; import org.neo4j.kernel.api.exceptions.schema.IllegalTokenNameException; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.SchemaRuleNotFoundException; import org.neo4j.kernel.api.exceptions.schema.TooManyLabelsException; import org.neo4j.kernel.api.index.InternalIndexState; import org.neo4j.kernel.api.schema.NodeMultiPropertyDescriptor; -import org.neo4j.kernel.api.schema.NodePropertyDescriptor; import org.neo4j.kernel.api.schema.RelationshipPropertyDescriptor; import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor; import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory; @@ -429,7 +429,7 @@ public IndexDefinition createIndexDefinition( Label label, String... propertyKey statement.schemaWriteOperations().indexCreate( descriptor ); return indexDefinition; } - catch ( AlreadyIndexedException | AlreadyConstrainedException e ) + catch ( AlreadyIndexedException | AlreadyConstrainedException | RepeatedPropertyInCompositeSchemaException e ) { throw new ConstraintViolationException( e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); @@ -490,7 +490,8 @@ public ConstraintDefinition createPropertyUniquenessConstraint( IndexDefinition SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) ); return new UniquenessConstraintDefinition( this, indexDefinition ); } - catch ( AlreadyConstrainedException | CreateConstraintFailureException | AlreadyIndexedException e ) + catch ( AlreadyConstrainedException | CreateConstraintFailureException | AlreadyIndexedException | + RepeatedPropertyInCompositeSchemaException e ) { throw new ConstraintViolationException( e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); @@ -524,7 +525,8 @@ public ConstraintDefinition createPropertyExistenceConstraint( Label label, Stri SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) ); return new NodePropertyExistenceConstraintDefinition( this, label, propertyKeys ); } - catch ( AlreadyConstrainedException | CreateConstraintFailureException e ) + catch ( AlreadyConstrainedException | CreateConstraintFailureException | + RepeatedPropertyInCompositeSchemaException e ) { throw new ConstraintViolationException( e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); @@ -558,7 +560,8 @@ public ConstraintDefinition createPropertyExistenceConstraint( RelationshipType SchemaDescriptorFactory.forRelType( typeId, propertyKeyId ) ); return new RelationshipPropertyExistenceConstraintDefinition( this, type, propertyKey ); } - catch ( AlreadyConstrainedException | CreateConstraintFailureException e ) + catch ( AlreadyConstrainedException | CreateConstraintFailureException | + RepeatedPropertyInCompositeSchemaException e ) { throw new ConstraintViolationException( e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperationsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperationsTest.java index 36592278fb019..97f09f173609e 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperationsTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/DataIntegrityValidatingStatementOperationsTest.java @@ -19,6 +19,7 @@ */ package org.neo4j.kernel.impl.api; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -27,7 +28,7 @@ import java.util.Iterator; -import org.neo4j.kernel.api.schema.NodePropertyDescriptor; +import org.neo4j.kernel.api.exceptions.schema.RepeatedPropertyInCompositeSchemaException; import org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException; import org.neo4j.kernel.api.exceptions.schema.AlreadyIndexedException; import org.neo4j.kernel.api.exceptions.schema.DropIndexFailureException; @@ -63,21 +64,30 @@ public class DataIntegrityValidatingStatementOperationsTest LabelSchemaDescriptor descriptor = SchemaDescriptorFactory.forLabel( 0, 7 ); NewIndexDescriptor index = NewIndexDescriptorFactory.forLabel( 0, 7 ); NewIndexDescriptor uniqueIndex = NewIndexDescriptorFactory.uniqueForLabel( 0, 7 ); + private SchemaReadOperations innerRead; + private SchemaWriteOperations innerWrite; + private KeyWriteOperations innerKeyWrite; + private DataIntegrityValidatingStatementOperations ops; + + @Before + public void setup() + { + innerKeyWrite = mock( KeyWriteOperations.class ); + innerRead = mock( SchemaReadOperations.class ); + innerWrite = mock( SchemaWriteOperations.class ); + ops = new DataIntegrityValidatingStatementOperations( innerKeyWrite, innerRead, innerWrite ); + } @Test public void shouldDisallowReAddingIndex() throws Exception { // GIVEN - SchemaReadOperations innerRead = mock( SchemaReadOperations.class ); - SchemaWriteOperations innerWrite = mock( SchemaWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, innerRead, innerWrite ); when( innerRead.indexGetForLabelAndPropertyKey( state, SchemaBoundary.map( descriptor ) ) ).thenReturn( index ); // WHEN try { - ctx.indexCreate( state, descriptor ); + ops.indexCreate( state, descriptor ); fail( "Should have thrown exception." ); } catch ( AlreadyIndexedException e ) @@ -93,16 +103,12 @@ public void shouldDisallowReAddingIndex() throws Exception public void shouldDisallowAddingIndexWhenConstraintIndexExists() throws Exception { // GIVEN - SchemaReadOperations innerRead = mock( SchemaReadOperations.class ); - SchemaWriteOperations innerWrite = mock( SchemaWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, innerRead, innerWrite ); when( innerRead.indexGetForLabelAndPropertyKey( state, SchemaBoundary.map( descriptor ) ) ).thenReturn( uniqueIndex ); // WHEN try { - ctx.indexCreate( state, descriptor ); + ops.indexCreate( state, descriptor ); fail( "Should have thrown exception." ); } catch ( AlreadyConstrainedException e ) @@ -118,16 +124,12 @@ public void shouldDisallowAddingIndexWhenConstraintIndexExists() throws Exceptio public void shouldDisallowDroppingIndexThatDoesNotExist() throws Exception { // GIVEN - SchemaReadOperations innerRead = mock( SchemaReadOperations.class ); - SchemaWriteOperations innerWrite = mock( SchemaWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, innerRead, innerWrite ); when( innerRead.indexGetForLabelAndPropertyKey( state, SchemaBoundary.map( descriptor ) ) ).thenReturn( null ); // WHEN try { - ctx.indexDrop( state, index ); + ops.indexDrop( state, index ); fail( "Should have thrown exception." ); } catch ( DropIndexFailureException e ) @@ -143,16 +145,12 @@ public void shouldDisallowDroppingIndexThatDoesNotExist() throws Exception public void shouldDisallowDroppingIndexWhenConstraintIndexExists() throws Exception { // GIVEN - SchemaReadOperations innerRead = mock( SchemaReadOperations.class ); - SchemaWriteOperations innerWrite = mock( SchemaWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, innerRead, innerWrite ); when( innerRead.indexGetForLabelAndPropertyKey( state, SchemaBoundary.map( descriptor ) ) ).thenReturn( uniqueIndex ); // WHEN try { - ctx.indexDrop( state, index ); + ops.indexDrop( state, index ); fail( "Should have thrown exception." ); } catch ( DropIndexFailureException e ) @@ -168,16 +166,12 @@ public void shouldDisallowDroppingIndexWhenConstraintIndexExists() throws Except public void shouldDisallowDroppingConstraintIndexThatDoesNotExists() throws Exception { // GIVEN - SchemaReadOperations innerRead = mock( SchemaReadOperations.class ); - SchemaWriteOperations innerWrite = mock( SchemaWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, innerRead, innerWrite ); when( innerRead.indexGetForLabelAndPropertyKey( state, SchemaBoundary.map( descriptor ) ) ).thenReturn( uniqueIndex ); // WHEN try { - ctx.indexDrop( state, index ); + ops.indexDrop( state, index ); fail( "Should have thrown exception." ); } catch ( DropIndexFailureException e ) @@ -193,16 +187,12 @@ public void shouldDisallowDroppingConstraintIndexThatDoesNotExists() throws Exce public void shouldDisallowDroppingConstraintIndexThatIsReallyJustRegularIndex() throws Exception { // GIVEN - SchemaReadOperations innerRead = mock( SchemaReadOperations.class ); - SchemaWriteOperations innerWrite = mock( SchemaWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, innerRead, innerWrite ); when( innerRead.indexGetForLabelAndPropertyKey( state, SchemaBoundary.map( descriptor ) ) ).thenReturn( uniqueIndex ); // WHEN try { - ctx.indexDrop( state, index ); + ops.indexDrop( state, index ); fail( "Should have thrown exception." ); } catch ( DropIndexFailureException e ) @@ -217,13 +207,9 @@ public void shouldDisallowDroppingConstraintIndexThatIsReallyJustRegularIndex() @Test public void shouldDisallowNullOrEmptyPropertyKey() throws Exception { - KeyWriteOperations inner = mock( KeyWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( inner, null, null ); - try { - ctx.propertyKeyGetOrCreateForName( state, null ); + ops.propertyKeyGetOrCreateForName( state, null ); fail( "Should not be able to create null property key" ); } catch ( IllegalTokenNameException e ) @@ -232,7 +218,7 @@ public void shouldDisallowNullOrEmptyPropertyKey() throws Exception try { - ctx.propertyKeyGetOrCreateForName( state, "" ); + ops.propertyKeyGetOrCreateForName( state, "" ); fail( "Should not be able to create empty property key" ); } catch ( IllegalTokenNameException e ) @@ -243,13 +229,9 @@ public void shouldDisallowNullOrEmptyPropertyKey() throws Exception @Test public void shouldDisallowNullOrEmptyLabelName() throws Exception { - KeyWriteOperations inner = mock( KeyWriteOperations.class ); - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( inner, null, null ); - try { - ctx.labelGetOrCreateForName( state, null ); + ops.labelGetOrCreateForName( state, null ); fail( "Should not be able to create null label" ); } catch ( IllegalTokenNameException e ) @@ -258,7 +240,7 @@ public void shouldDisallowNullOrEmptyLabelName() throws Exception try { - ctx.labelGetOrCreateForName( state, "" ); + ops.labelGetOrCreateForName( state, "" ); fail( "Should not be able to create empty label" ); } catch ( IllegalTokenNameException e ) @@ -269,23 +251,37 @@ public void shouldDisallowNullOrEmptyLabelName() throws Exception @Test( expected = SchemaKernelException.class ) public void shouldFailInvalidLabelNames() throws Exception { - // Given - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, null, null ); - - // When - ctx.labelGetOrCreateForName( state, "" ); + ops.labelGetOrCreateForName( state, "" ); } @Test( expected = SchemaKernelException.class ) public void shouldFailOnNullLabel() throws Exception { - // Given - DataIntegrityValidatingStatementOperations ctx = - new DataIntegrityValidatingStatementOperations( null, null, null ); + ops.labelGetOrCreateForName( state, null ); + } - // When - ctx.labelGetOrCreateForName( state, null ); + @Test( expected = RepeatedPropertyInCompositeSchemaException.class ) + public void shouldFailIndexCreateOnRepeatedPropertyId() throws Exception + { + ops.indexCreate( state, SchemaDescriptorFactory.forLabel( 0, 1, 1 ) ); + } + + @Test( expected = RepeatedPropertyInCompositeSchemaException.class ) + public void shouldFailNodeExistenceCreateOnRepeatedPropertyId() throws Exception + { + ops.nodePropertyExistenceConstraintCreate( state, SchemaDescriptorFactory.forLabel( 0, 1, 1 ) ); + } + + @Test( expected = RepeatedPropertyInCompositeSchemaException.class ) + public void shouldFailRelExistenceCreateOnRepeatedPropertyId() throws Exception + { + ops.relationshipPropertyExistenceConstraintCreate( state, SchemaDescriptorFactory.forRelType( 0, 1, 1 ) ); + } + + @Test( expected = RepeatedPropertyInCompositeSchemaException.class ) + public void shouldFailUniquenessCreateOnRepeatedPropertyId() throws Exception + { + ops.uniquePropertyConstraintCreate( state, SchemaDescriptorFactory.forLabel( 0, 1, 1 ) ); } @SafeVarargs