Skip to content

Commit

Permalink
Check for repeated properties in composite indexes and constraints
Browse files Browse the repository at this point in the history
  • Loading branch information
fickludd committed Mar 15, 2017
1 parent 42d8404 commit 196a19f
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 88 deletions.
Expand Up @@ -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,
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
@@ -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 <http://www.gnu.org/licenses/>.
*/
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 ) );

}
}
Expand Up @@ -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;
Expand Down Expand Up @@ -539,7 +540,7 @@ public Cursor<RelationshipItem> nodeGetRelationships( KernelStatement statement,

@Override
public NewIndexDescriptor indexCreate( KernelStatement state, LabelSchemaDescriptor descriptor )
throws AlreadyIndexedException, AlreadyConstrainedException
throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException
{
return schemaWriteOperations.indexCreate( state, descriptor );
}
Expand All @@ -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 );
}
Expand All @@ -567,7 +569,7 @@ public UniquenessConstraintDescriptor uniquePropertyConstraintCreate( KernelStat
public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate(
KernelStatement state,
LabelSchemaDescriptor descriptor
) throws AlreadyConstrainedException, CreateConstraintFailureException
) throws AlreadyConstrainedException, CreateConstraintFailureException, RepeatedPropertyInCompositeSchemaException
{
Iterator<Cursor<NodeItem>> nodes = new NodeLoadingIterator( nodesGetForLabel( state, descriptor.getLabelId() ),
( id ) -> nodeCursorById( state, id ) );
Expand All @@ -580,7 +582,7 @@ public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate(
public RelExistenceConstraintDescriptor relationshipPropertyExistenceConstraintCreate(
KernelStatement state,
RelationTypeSchemaDescriptor descriptor
) throws AlreadyConstrainedException, CreateConstraintFailureException
) throws AlreadyConstrainedException, CreateConstraintFailureException, RepeatedPropertyInCompositeSchemaException
{
try ( Cursor<RelationshipItem> cursor = relationshipCursorGetAll( state ) )
{
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 );
}

Expand Down Expand Up @@ -151,30 +156,37 @@ 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 );
}

@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 );
Expand All @@ -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
{
Expand Down Expand Up @@ -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 );
}
}
}
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -989,7 +990,7 @@ public Property graphRemoveProperty( int propertyKeyId )
// <SchemaWrite>
@Override
public NewIndexDescriptor indexCreate( LabelSchemaDescriptor descriptor )
throws AlreadyIndexedException, AlreadyConstrainedException
throws AlreadyIndexedException, AlreadyConstrainedException, RepeatedPropertyInCompositeSchemaException
{
statement.assertOpen();
return schemaWrite().indexCreate( statement, descriptor );
Expand All @@ -1004,15 +1005,17 @@ 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 );
}

@Override
public NodeExistenceConstraintDescriptor nodePropertyExistenceConstraintCreate( LabelSchemaDescriptor descriptor )
throws CreateConstraintFailureException, AlreadyConstrainedException
throws CreateConstraintFailureException, AlreadyConstrainedException,
RepeatedPropertyInCompositeSchemaException
{
statement.assertOpen();
return schemaWrite().nodePropertyExistenceConstraintCreate( statement, descriptor );
Expand All @@ -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 );
Expand Down

0 comments on commit 196a19f

Please sign in to comment.