Skip to content

Commit

Permalink
ConstraintDescriptor subclasses for increased type safety
Browse files Browse the repository at this point in the history
 - Pushed final boundary in ConstrainSemantics
 - Fixed some trailing TODOs
  • Loading branch information
fickludd committed Feb 24, 2017
1 parent 095cae4 commit 6089e18
Show file tree
Hide file tree
Showing 23 changed files with 300 additions and 168 deletions.
Expand Up @@ -20,21 +20,15 @@
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.RelationTypeSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaComputer;
import org.neo4j.kernel.api.schema_new.SchemaDescriptor;
import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor;
import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptorFactory;

import static java.lang.String.format;
import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.UNIQUE;

/**
* Internal representation of a graph constraint, including the schema unit it targets (eg. label-property combination)
* and the how that schema unit is constrained (eg. "has to exist", or "must be unique").
*/
public class ConstraintDescriptor implements SchemaDescriptor.Supplier
public abstract class ConstraintDescriptor implements SchemaDescriptor.Supplier
{
public enum Type { UNIQUE, EXISTS }

Expand All @@ -43,31 +37,16 @@ public interface Supplier
ConstraintDescriptor getConstraintDescriptor();
}

private final SchemaDescriptor schema;
private final ConstraintDescriptor.Type type;

ConstraintDescriptor( SchemaDescriptor schema, Type type )
ConstraintDescriptor( Type type )
{
this.schema = schema;
this.type = type;
}

// METHODS

@Override
public SchemaDescriptor schema()
{
return schema;
}

public NewIndexDescriptor ownedIndexDescriptor()
{
if ( type == UNIQUE && schema instanceof LabelSchemaDescriptor )
{
return NewIndexDescriptorFactory.uniqueForSchema( (LabelSchemaDescriptor)schema );
}
throw new IllegalStateException( "Only unique constraints on label-property combinations are allowed" );
}
public abstract SchemaDescriptor schema();

public Type type()
{
Expand All @@ -80,21 +59,21 @@ public Type type()
*/
public String userDescription( TokenNameLookup tokenNameLookup )
{
return format( "Constraint( %s, %s )", type.name(), schema.userDescription( tokenNameLookup ) );
return format( "Constraint( %s, %s )", type.name(), schema().userDescription( tokenNameLookup ) );
}

/**
* Checks whether a constraint descriptor Supplier supplies this constraint descriptor.
* @param supplier supplier to get a constraint descriptor from
* @return true if the supplied constraint descriptor equals this constraint descriptor
*/
public boolean isSame( Supplier supplier )
public final boolean isSame( Supplier supplier )
{
return this.equals( supplier.getConstraintDescriptor() );
}

@Override
public boolean equals( Object o )
public final boolean equals( Object o )
{
if ( o != null && o instanceof ConstraintDescriptor )
{
Expand All @@ -105,69 +84,23 @@ public boolean equals( Object o )
}

@Override
public int hashCode()
public final int hashCode()
{
return type.hashCode() & schema.hashCode();
return type.hashCode() & schema().hashCode();
}

// PRETTY PRINTING

public String prettyPrint( TokenNameLookup tokenNameLookup )
{
return schema.computeWith( new ConstraintPrettyPrinter( tokenNameLookup ) );
}
public abstract String prettyPrint( TokenNameLookup tokenNameLookup );

private class ConstraintPrettyPrinter implements SchemaComputer<String>
String escapeLabelOrRelTyp( String name )
{
private final TokenNameLookup tokenNameLookup;

ConstraintPrettyPrinter( TokenNameLookup tokenNameLookup )
{
this.tokenNameLookup = tokenNameLookup;
}

@Override
public String computeSpecific( LabelSchemaDescriptor schema )
{
assert schema.getPropertyIds().length == 1;
String labelName = labelName( schema.getLabelId(), tokenNameLookup );
String nodeName = labelName.toLowerCase();
String propertyName = tokenNameLookup.propertyKeyGetName( schema.getPropertyIds()[0] );
if ( type == UNIQUE )
{
return String.format( "CONSTRAINT ON ( %s:%s ) ASSERT %s.%s IS UNIQUE",
nodeName, labelName, nodeName, propertyName );
}
else
{
return String.format( "CONSTRAINT ON ( %s:%s ) ASSERT exists(%s.%s)",
nodeName, labelName, nodeName, propertyName );
}
if (name.contains( ":" )) {
return "`" + name + "`";
}

@Override
public String computeSpecific( RelationTypeSchemaDescriptor schema )
{
assert schema.getPropertyIds().length == 1;
String typeName = tokenNameLookup.relationshipTypeGetName( schema.getRelTypeId() );
String relName = typeName.toLowerCase();
String propertyName = tokenNameLookup.propertyKeyGetName( schema.getPropertyIds()[0] );
return String.format( "CONSTRAINT ON ()-[ %s:%s ]-() ASSERT exists(%s.%s)",
relName, typeName, relName, propertyName );
}

private String labelName( int labelId, TokenNameLookup tokenNameLookup )
else
{
String labelName = tokenNameLookup.labelGetName( labelId );
//if the labelName contains a `:` we must escape it to avoid disambiguation,
//e.g. CONSTRAINT on foo:bar:foo:bar
if (labelName.contains( ":" )) {
return "`" + labelName + "`";
}
else
{
return labelName;
}
return name;
}
}
}
Expand Up @@ -19,41 +19,75 @@
*/
package org.neo4j.kernel.api.schema_new.constaints;

import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaComputer;
import org.neo4j.kernel.api.schema_new.SchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory;
import org.neo4j.kernel.api.schema_new.SchemaUtil;

import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.EXISTS;
import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.UNIQUE;
import static java.lang.String.format;

public class ConstraintDescriptorFactory
{
public static ConstraintDescriptor existsForLabel( int labelId, int... propertyIds )
public static NodeExistenceConstraintDescriptor existsForLabel( int labelId, int... propertyIds )
{
return existsForSchema( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) );
return new NodeExistenceConstraintDescriptor( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) );
}

public static ConstraintDescriptor existsForRelType( int relTypeId, int... propertyIds )
public static RelExistenceConstraintDescriptor existsForRelType( int relTypeId, int... propertyIds )
{
return existsForSchema( SchemaDescriptorFactory.forRelType( relTypeId, propertyIds ) );
return new RelExistenceConstraintDescriptor( SchemaDescriptorFactory.forRelType( relTypeId, propertyIds ) );
}

public static ConstraintDescriptor uniqueForLabel( int labelId, int... propertyIds )
public static UniquenessConstraintDescriptor uniqueForLabel( int labelId, int... propertyIds )
{
return uniqueForSchema( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) );
}

public static ConstraintDescriptor uniqueForRelType( int relTypeId, int... propertyIds )
{
return uniqueForSchema( SchemaDescriptorFactory.forRelType( relTypeId, propertyIds ) );
return new UniquenessConstraintDescriptor( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) );
}

public static ConstraintDescriptor existsForSchema( SchemaDescriptor schema )
{
return new ConstraintDescriptor( schema, EXISTS );
return schema.computeWith( convertToExistenceConstraint );
}

public static ConstraintDescriptor uniqueForSchema( SchemaDescriptor schema )
public static UniquenessConstraintDescriptor uniqueForSchema( SchemaDescriptor schema )
{
return new ConstraintDescriptor( schema, UNIQUE );
return schema.computeWith( convertToUniquenessConstraint );
}

private static SchemaComputer<ConstraintDescriptor> convertToExistenceConstraint =
new SchemaComputer<ConstraintDescriptor>()
{
@Override
public ConstraintDescriptor computeSpecific( LabelSchemaDescriptor schema )
{
return new NodeExistenceConstraintDescriptor( schema );
}

@Override
public ConstraintDescriptor computeSpecific( RelationTypeSchemaDescriptor schema )
{
return new RelExistenceConstraintDescriptor( schema );
}
};

private static SchemaComputer<UniquenessConstraintDescriptor> convertToUniquenessConstraint =
new SchemaComputer<UniquenessConstraintDescriptor>()
{
@Override
public UniquenessConstraintDescriptor computeSpecific( LabelSchemaDescriptor schema )
{
return new UniquenessConstraintDescriptor( schema );
}

@Override
public UniquenessConstraintDescriptor computeSpecific( RelationTypeSchemaDescriptor schema )
{
throw new UnsupportedOperationException(
format( "Cannot create uniqueness constraint for schema '%s' of type %s",
schema.userDescription( SchemaUtil.idTokenNameLookup ),
schema.getClass().getSimpleName()
) );
}
};
}
@@ -0,0 +1,51 @@
/*
* 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.schema_new.constaints;

import org.neo4j.kernel.api.TokenNameLookup;
import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor;

public class NodeExistenceConstraintDescriptor extends ConstraintDescriptor
{
private LabelSchemaDescriptor schema;

NodeExistenceConstraintDescriptor( LabelSchemaDescriptor schema )
{
super( Type.EXISTS );
this.schema = schema;
}

@Override
public LabelSchemaDescriptor schema()
{
return schema;
}

@Override
public String prettyPrint( TokenNameLookup tokenNameLookup )
{
String labelName = escapeLabelOrRelTyp( tokenNameLookup.labelGetName( schema.getLabelId() ) );
String nodeName = labelName.toLowerCase();
String propertyName = tokenNameLookup.propertyKeyGetName( schema.getPropertyId() );

return String.format( "CONSTRAINT ON ( %s:%s ) ASSERT exists(%s.%s)",
nodeName, labelName, nodeName, propertyName );
}
}
@@ -0,0 +1,51 @@
/*
* 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.schema_new.constaints;

import org.neo4j.kernel.api.TokenNameLookup;
import org.neo4j.kernel.api.schema_new.RelationTypeSchemaDescriptor;

public class RelExistenceConstraintDescriptor extends ConstraintDescriptor
{
private final RelationTypeSchemaDescriptor schema;

RelExistenceConstraintDescriptor( RelationTypeSchemaDescriptor schema )
{
super( Type.EXISTS );
this.schema = schema;
}

@Override
public RelationTypeSchemaDescriptor schema()
{
return schema;
}

@Override
public String prettyPrint( TokenNameLookup tokenNameLookup )
{
String typeName = escapeLabelOrRelTyp( tokenNameLookup.relationshipTypeGetName( schema.getRelTypeId() ) );
String relName = typeName.toLowerCase();
String propertyName = tokenNameLookup.propertyKeyGetName( schema.getPropertyId() );

return String.format( "CONSTRAINT ON ()-[ %s:%s ]-() ASSERT exists(%s.%s)",
relName, typeName, relName, propertyName );
}
}

0 comments on commit 6089e18

Please sign in to comment.