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; package org.neo4j.kernel.api.schema_new.constaints;


import org.neo4j.kernel.api.TokenNameLookup; 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.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 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) * 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"). * 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 } public enum Type { UNIQUE, EXISTS }


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


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


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


// METHODS // METHODS


@Override public abstract SchemaDescriptor schema();
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 Type type() public Type type()
{ {
Expand All @@ -80,21 +59,21 @@ public Type type()
*/ */
public String userDescription( TokenNameLookup tokenNameLookup ) 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. * Checks whether a constraint descriptor Supplier supplies this constraint descriptor.
* @param supplier supplier to get a constraint descriptor from * @param supplier supplier to get a constraint descriptor from
* @return true if the supplied constraint descriptor equals this constraint descriptor * @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() ); return this.equals( supplier.getConstraintDescriptor() );
} }


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


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


// PRETTY PRINTING // PRETTY PRINTING


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


private class ConstraintPrettyPrinter implements SchemaComputer<String> String escapeLabelOrRelTyp( String name )
{ {
private final TokenNameLookup tokenNameLookup; if (name.contains( ":" )) {

return "`" + name + "`";
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 );
}
} }

else
@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 )
{ {
String labelName = tokenNameLookup.labelGetName( labelId ); return name;
//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;
}
} }
} }
} }
Expand Up @@ -19,41 +19,75 @@
*/ */
package org.neo4j.kernel.api.schema_new.constaints; 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.SchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory; 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 java.lang.String.format;
import static org.neo4j.kernel.api.schema_new.constaints.ConstraintDescriptor.Type.UNIQUE;


public class ConstraintDescriptorFactory 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 ) ); return new UniquenessConstraintDescriptor( SchemaDescriptorFactory.forLabel( labelId, propertyIds ) );
}

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


public static ConstraintDescriptor existsForSchema( SchemaDescriptor schema ) 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.