Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.neo4j.kernel.impl.api.state.TxState Maven / Gradle / Ivy
/*
* Copyright (c) 2002-2015 "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.api.state;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.neo4j.collection.primitive.Primitive;
import org.neo4j.collection.primitive.PrimitiveIntIterator;
import org.neo4j.collection.primitive.PrimitiveIntObjectMap;
import org.neo4j.collection.primitive.PrimitiveLongIterator;
import org.neo4j.collection.primitive.PrimitiveLongSet;
import org.neo4j.cursor.Cursor;
import org.neo4j.function.Consumer;
import org.neo4j.function.Function;
import org.neo4j.function.Predicate;
import org.neo4j.graphdb.Direction;
import org.neo4j.helpers.collection.Iterables;
import org.neo4j.kernel.api.constraints.NodePropertyConstraint;
import org.neo4j.kernel.api.constraints.NodePropertyExistenceConstraint;
import org.neo4j.kernel.api.constraints.PropertyConstraint;
import org.neo4j.kernel.api.constraints.RelationshipPropertyConstraint;
import org.neo4j.kernel.api.constraints.RelationshipPropertyExistenceConstraint;
import org.neo4j.kernel.api.constraints.UniquenessConstraint;
import org.neo4j.kernel.api.cursor.LabelItem;
import org.neo4j.kernel.api.cursor.NodeItem;
import org.neo4j.kernel.api.cursor.PropertyItem;
import org.neo4j.kernel.api.cursor.RelationshipItem;
import org.neo4j.kernel.api.exceptions.schema.ConstraintValidationKernelException;
import org.neo4j.kernel.api.exceptions.schema.CreateConstraintFailureException;
import org.neo4j.kernel.api.index.IndexDescriptor;
import org.neo4j.kernel.api.procedures.ProcedureDescriptor;
import org.neo4j.kernel.api.procedures.ProcedureSignature;
import org.neo4j.kernel.api.properties.DefinedProperty;
import org.neo4j.kernel.api.properties.Property;
import org.neo4j.kernel.api.txstate.ReadableTxState;
import org.neo4j.kernel.api.txstate.RelationshipChangeVisitorAdapter;
import org.neo4j.kernel.api.txstate.TransactionState;
import org.neo4j.kernel.api.txstate.TxStateVisitor;
import org.neo4j.kernel.impl.api.RelationshipVisitor;
import org.neo4j.kernel.impl.api.cursor.TxAllPropertyCursor;
import org.neo4j.kernel.impl.api.cursor.TxIteratorNodeCursor;
import org.neo4j.kernel.impl.api.cursor.TxIteratorRelationshipCursor;
import org.neo4j.kernel.impl.api.cursor.TxLabelCursor;
import org.neo4j.kernel.impl.api.cursor.TxSingleLabelCursor;
import org.neo4j.kernel.impl.api.cursor.TxSingleNodeCursor;
import org.neo4j.kernel.impl.api.cursor.TxSinglePropertyCursor;
import org.neo4j.kernel.impl.api.cursor.TxSingleRelationshipCursor;
import org.neo4j.kernel.impl.api.store.RelationshipIterator;
import org.neo4j.kernel.impl.util.InstanceCache;
import org.neo4j.kernel.impl.util.diffsets.DiffSets;
import org.neo4j.kernel.impl.util.diffsets.DiffSetsVisitor;
import org.neo4j.kernel.impl.util.diffsets.ReadableDiffSets;
import org.neo4j.kernel.impl.util.diffsets.ReadableRelationshipDiffSets;
import org.neo4j.kernel.impl.util.diffsets.RelationshipDiffSets;
import static org.neo4j.collection.primitive.PrimitiveLongCollections.toPrimitiveIterator;
import static org.neo4j.helpers.collection.Iterables.map;
import static org.neo4j.kernel.api.properties.Property.numberProperty;
import static org.neo4j.kernel.impl.api.PropertyValueComparison.SuperType.NUMBER;
import static org.neo4j.kernel.impl.api.PropertyValueComparison.SuperType.STRING;
/**
* This class contains transaction-local changes to the graph. These changes can then be used to augment reads from the
* committed state of the database (to make the local changes appear in local transaction read operations). At commit
* time a visitor is sent into this class to convert the end result of the tx changes into a physical changeset.
*
* See {@link org.neo4j.kernel.impl.api.KernelTransactionImplementation} for how this happens.
*
* This class is very large, as it has been used as a gathering point to consolidate all transaction state knowledge
* into one component. Now that that work is done, this class should be refactored to increase transparency in how it
* works.
*/
public final class TxState implements TransactionState, RelationshipVisitor.Home
{
private Map labelStatesMap;
private static final LabelState.Defaults LABEL_STATE = new LabelState.Defaults()
{
@Override
Map getMap( TxState state )
{
return state.labelStatesMap;
}
@Override
void setMap( TxState state, Map map )
{
state.labelStatesMap = map;
}
};
private Map nodeStatesMap;
private static final NodeState.Defaults NODE_STATE = new NodeState.Defaults()
{
@Override
Map getMap( TxState state )
{
return state.nodeStatesMap;
}
@Override
void setMap( TxState state, Map map )
{
state.nodeStatesMap = map;
}
};
private Map relationshipStatesMap;
private static final RelationshipState.Defaults RELATIONSHIP_STATE = new RelationshipState.Defaults()
{
@Override
Map getMap( TxState state )
{
return state.relationshipStatesMap;
}
@Override
void setMap( TxState state, Map map )
{
state.relationshipStatesMap = map;
}
};
private Map createdLabelTokens;
private Map createdPropertyKeyTokens;
private Map createdRelationshipTypeTokens;
private GraphState graphState;
private DiffSets indexChanges;
private DiffSets constraintIndexChanges;
private DiffSets constraintsChanges;
private DiffSets procedureChanges;
private Map addedProcedures;
private PropertyChanges propertyChangesForNodes;
// Tracks added and removed nodes, not modified nodes
private DiffSets nodes;
// Tracks added and removed relationships, not modified relationships
private RelationshipDiffSets relationships;
/**
* These two sets are needed because create-delete in same transaction is a no-op in {@link DiffSets}
* but we still need to provide correct answer in {@link #nodeIsDeletedInThisTx(long)} and
* {@link #relationshipIsDeletedInThisTx(long)} methods.
*/
private PrimitiveLongSet nodesDeletedInTx;
private PrimitiveLongSet relationshipsDeletedInTx;
private Map createdConstraintIndexesByConstraint;
private Map> createdNodeLegacyIndexes;
private Map> createdRelationshipLegacyIndexes;
private PrimitiveIntObjectMap>> indexUpdates;
private PrimitiveIntObjectMap> relationshipConstraintChanges;
private InstanceCache iteratorNodeCursor;
private InstanceCache singleNodeCursor;
private InstanceCache iteratorRelationshipCursor;
private InstanceCache singleRelationshipCursor;
private InstanceCache propertyCursor;
private InstanceCache singlePropertyCursor;
private InstanceCache labelCursor;
private InstanceCache singleLabelCursor;
private boolean hasChanges, hasDataChanges;
public TxState()
{
singleNodeCursor = new InstanceCache()
{
@Override
protected TxSingleNodeCursor create()
{
return new TxSingleNodeCursor( TxState.this, this );
}
};
iteratorNodeCursor = new InstanceCache()
{
@Override
protected TxIteratorNodeCursor create()
{
return new TxIteratorNodeCursor( TxState.this, this );
}
};
propertyCursor = new InstanceCache()
{
@Override
protected TxAllPropertyCursor create()
{
return new TxAllPropertyCursor( (Consumer) this );
}
};
singlePropertyCursor = new InstanceCache()
{
@Override
protected TxSinglePropertyCursor create()
{
return new TxSinglePropertyCursor( (Consumer) this );
}
};
labelCursor = new InstanceCache()
{
@Override
protected TxLabelCursor create()
{
return new TxLabelCursor( this );
}
};
singleLabelCursor = new InstanceCache()
{
@Override
protected TxSingleLabelCursor create()
{
return new TxSingleLabelCursor( this );
}
};
singleRelationshipCursor = new InstanceCache()
{
@Override
protected TxSingleRelationshipCursor create()
{
return new TxSingleRelationshipCursor( TxState.this, this );
}
};
iteratorRelationshipCursor = new InstanceCache()
{
@Override
protected TxIteratorRelationshipCursor create()
{
return new TxIteratorRelationshipCursor( TxState.this, this );
}
};
}
@Override
public void accept( final TxStateVisitor visitor )
throws ConstraintValidationKernelException, CreateConstraintFailureException
{
// Created nodes
if ( nodes != null )
{
nodes.accept( createdNodesVisitor( visitor ) );
}
if ( relationships != null )
{
// Created relationships
relationships.accept( createdRelationshipsVisitor( this, visitor ) );
// Deleted relationships
relationships.accept( deletedRelationshipsVisitor( visitor ) );
}
// Deleted nodes
if ( nodes != null )
{
nodes.accept( deletedNodesVisitor( visitor ) );
}
for ( NodeState node : modifiedNodes() )
{
node.accept( nodeVisitor( visitor ) );
}
for ( RelationshipState rel : modifiedRelationships() )
{
rel.accept( relVisitor( visitor ) );
}
if ( graphState != null )
{
graphState.accept( graphPropertyVisitor( visitor ) );
}
if ( indexChanges != null )
{
indexChanges.accept( indexVisitor( visitor, false ) );
}
if( procedureChanges != null )
{
procedureChanges.accept( procedureVisitor( visitor ) );
}
if ( constraintIndexChanges != null )
{
constraintIndexChanges.accept( indexVisitor( visitor, true ) );
}
if ( constraintsChanges != null )
{
constraintsChanges.accept( constraintsVisitor( visitor ) );
}
if ( createdLabelTokens != null )
{
for ( Map.Entry entry : createdLabelTokens.entrySet() )
{
visitor.visitCreatedLabelToken( entry.getValue(), entry.getKey() );
}
}
if ( createdPropertyKeyTokens != null )
{
for ( Map.Entry entry : createdPropertyKeyTokens.entrySet() )
{
visitor.visitCreatedPropertyKeyToken( entry.getValue(), entry.getKey() );
}
}
if ( createdRelationshipTypeTokens != null )
{
for ( Map.Entry entry : createdRelationshipTypeTokens.entrySet() )
{
visitor.visitCreatedRelationshipTypeToken( entry.getValue(), entry.getKey() );
}
}
if ( createdNodeLegacyIndexes != null )
{
for ( Map.Entry> entry : createdNodeLegacyIndexes.entrySet() )
{
visitor.visitCreatedNodeLegacyIndex( entry.getKey(), entry.getValue() );
}
}
if ( createdRelationshipLegacyIndexes != null )
{
for ( Map.Entry> entry : createdRelationshipLegacyIndexes.entrySet() )
{
visitor.visitCreatedRelationshipLegacyIndex( entry.getKey(), entry.getValue() );
}
}
}
private static DiffSetsVisitor deletedNodesVisitor( final TxStateVisitor visitor )
{
return new DiffSetsVisitor.Adapter()
{
@Override
public void visitRemoved( Long element )
{
visitor.visitDeletedNode( element );
}
};
}
private static DiffSetsVisitor createdNodesVisitor( final TxStateVisitor visitor )
{
return new DiffSetsVisitor.Adapter()
{
@Override
public void visitAdded( Long element )
{
visitor.visitCreatedNode( element );
}
};
}
private static DiffSetsVisitor deletedRelationshipsVisitor( final TxStateVisitor visitor )
{
return new DiffSetsVisitor.Adapter()
{
@Override
public void visitRemoved( Long id )
{
visitor.visitDeletedRelationship( id );
}
};
}
private static DiffSetsVisitor createdRelationshipsVisitor( ReadableTxState tx, final TxStateVisitor visitor )
{
return new RelationshipChangeVisitorAdapter( tx )
{
@Override
protected void visitAddedRelationship( long relationshipId, int type, long startNode, long endNode )
throws ConstraintValidationKernelException
{
visitor.visitCreatedRelationship( relationshipId, type, startNode, endNode );
}
};
}
private static DiffSetsVisitor constraintsVisitor( final TxStateVisitor visitor )
{
return new ConstraintDiffSetsVisitor( visitor );
}
static class ConstraintDiffSetsVisitor implements PropertyConstraint.ChangeVisitor,
DiffSetsVisitor
{
private final TxStateVisitor visitor;
ConstraintDiffSetsVisitor( TxStateVisitor visitor )
{
this.visitor = visitor;
}
@Override
public void visitAdded( PropertyConstraint element ) throws CreateConstraintFailureException
{
element.added( this );
}
@Override
public void visitRemoved( PropertyConstraint element )
{
element.removed( this );
}
@Override
public void visitAddedUniquePropertyConstraint( UniquenessConstraint constraint )
{
visitor.visitAddedUniquePropertyConstraint( constraint );
}
@Override
public void visitRemovedUniquePropertyConstraint( UniquenessConstraint constraint )
{
visitor.visitRemovedUniquePropertyConstraint( constraint );
}
@Override
public void visitAddedNodePropertyExistenceConstraint( NodePropertyExistenceConstraint constraint )
throws CreateConstraintFailureException
{
visitor.visitAddedNodePropertyExistenceConstraint( constraint );
}
@Override
public void visitRemovedNodePropertyExistenceConstraint( NodePropertyExistenceConstraint constraint )
{
visitor.visitRemovedNodePropertyExistenceConstraint( constraint );
}
@Override
public void visitAddedRelationshipPropertyExistenceConstraint(
RelationshipPropertyExistenceConstraint constraint ) throws CreateConstraintFailureException
{
visitor.visitAddedRelationshipPropertyExistenceConstraint( constraint );
}
@Override
public void visitRemovedRelationshipPropertyExistenceConstraint(
RelationshipPropertyExistenceConstraint constraint )
{
visitor.visitRemovedRelationshipPropertyExistenceConstraint( constraint );
}
}
private static DiffSetsVisitor procedureVisitor( final TxStateVisitor visitor )
{
return new DiffSetsVisitor()
{
@Override
public void visitAdded( ProcedureDescriptor element )
{
visitor.visitCreatedProcedure( element );
}
@Override
public void visitRemoved( ProcedureDescriptor element )
{
visitor.visitDroppedProcedure( element );
}
};
}
private static DiffSetsVisitor indexVisitor( final TxStateVisitor visitor,
final boolean forConstraint )
{
return new DiffSetsVisitor()
{
@Override
public void visitAdded( IndexDescriptor element )
{
visitor.visitAddedIndex( element, forConstraint );
}
@Override
public void visitRemoved( IndexDescriptor element )
{
visitor.visitRemovedIndex( element, forConstraint );
}
};
}
private static NodeState.Visitor nodeVisitor( final TxStateVisitor visitor )
{
return new NodeState.Visitor()
{
@Override
public void visitLabelChanges( long nodeId, Set added, Set removed )
throws ConstraintValidationKernelException
{
visitor.visitNodeLabelChanges( nodeId, added, removed );
}
@Override
public void visitPropertyChanges( long entityId, Iterator added,
Iterator changed, Iterator removed )
throws ConstraintValidationKernelException
{
visitor.visitNodePropertyChanges( entityId, added, changed, removed );
}
@Override
public void visitRelationshipChanges( long nodeId, RelationshipChangesForNode added,
RelationshipChangesForNode removed )
{
visitor.visitNodeRelationshipChanges( nodeId, added, removed );
}
};
}
private static PropertyContainerState.Visitor relVisitor( final TxStateVisitor visitor )
{
return new PropertyContainerState.Visitor()
{
@Override
public void visitPropertyChanges( long entityId, Iterator added,
Iterator changed, Iterator removed )
throws ConstraintValidationKernelException
{
visitor.visitRelPropertyChanges( entityId, added, changed, removed );
}
};
}
private static PropertyContainerState.Visitor graphPropertyVisitor( final TxStateVisitor visitor )
{
return new PropertyContainerState.Visitor()
{
@Override
public void visitPropertyChanges( long entityId, Iterator added,
Iterator changed, Iterator removed )
{
visitor.visitGraphPropertyChanges( added, changed, removed );
}
};
}
@Override
public boolean hasChanges()
{
return hasChanges;
}
@Override
public Iterable modifiedNodes()
{
return NODE_STATE.values( this );
}
private DiffSets getOrCreateLabelStateNodeDiffSets( int labelId )
{
return LABEL_STATE.getOrCreate( this, labelId ).getOrCreateNodeDiffSets();
}
@Override
public ReadableDiffSets nodeStateLabelDiffSets( long nodeId )
{
return NODE_STATE.get( this, nodeId ).labelDiffSets();
}
private DiffSets getOrCreateNodeStateLabelDiffSets( long nodeId )
{
return getOrCreateNodeState( nodeId ).getOrCreateLabelDiffSets();
}
@Override
public Iterator augmentGraphProperties( Iterator original )
{
if ( graphState != null )
{
return graphState.augmentProperties( original );
}
return original;
}
@Override
public boolean nodeIsAddedInThisTx( long nodeId )
{
return nodes != null && nodes.isAdded( nodeId );
}
@Override
public boolean relationshipIsAddedInThisTx( long relationshipId )
{
return relationships != null && relationships.isAdded( relationshipId );
}
private void changed()
{
hasChanges = true;
}
private void dataChanged()
{
changed();
hasDataChanges = true;
}
@Override
public void nodeDoCreate( long id )
{
nodes().add( id );
dataChanged();
}
@Override
public void nodeDoDelete( long nodeId )
{
if ( nodes().remove( nodeId ) )
{
recordNodeDeleted( nodeId );
}
if ( nodeStatesMap != null )
{
NodeState.Mutable nodeState = nodeStatesMap.remove( nodeId );
if ( nodeState != null )
{
ReadableDiffSets diff = nodeState.labelDiffSets();
for ( Integer label : diff.getAdded() )
{
getOrCreateLabelStateNodeDiffSets( label ).remove( nodeId );
}
nodeState.clearIndexDiffs( nodeId );
nodeState.clear();
}
}
dataChanged();
}
@Override
public void relationshipDoCreate( long id, int relationshipTypeId, long startNodeId, long endNodeId )
{
relationships().add( id );
if ( startNodeId == endNodeId )
{
getOrCreateNodeState( startNodeId ).addRelationship( id, relationshipTypeId, Direction.BOTH );
}
else
{
getOrCreateNodeState( startNodeId ).addRelationship( id, relationshipTypeId, Direction.OUTGOING );
getOrCreateNodeState( endNodeId ).addRelationship( id, relationshipTypeId, Direction.INCOMING );
}
getOrCreateRelationshipState( id ).setMetaData( startNodeId, endNodeId, relationshipTypeId );
dataChanged();
}
@Override
public boolean nodeIsDeletedInThisTx( long nodeId )
{
return nodesDeletedInTx != null && nodesDeletedInTx.contains( nodeId );
}
@Override
public boolean nodeModifiedInThisTx( long nodeId )
{
return nodeIsAddedInThisTx( nodeId ) || nodeIsDeletedInThisTx( nodeId ) || hasNodeState( nodeId );
}
@Override
public void relationshipDoDelete( long id, int type, long startNodeId, long endNodeId )
{
if ( relationships().remove( id ) )
{
recordRelationshipDeleted( id );
}
if ( startNodeId == endNodeId )
{
getOrCreateNodeState( startNodeId ).removeRelationship( id, type, Direction.BOTH );
}
else
{
getOrCreateNodeState( startNodeId ).removeRelationship( id, type, Direction.OUTGOING );
getOrCreateNodeState( endNodeId ).removeRelationship( id, type, Direction.INCOMING );
}
if ( relationshipStatesMap != null )
{
RelationshipState.Mutable removed = relationshipStatesMap.remove( id );
if ( removed != null )
{
removed.clear();
}
}
dataChanged();
}
@Override
public void relationshipDoDeleteAddedInThisTx( long relationshipId )
{
RELATIONSHIP_STATE.get( this, relationshipId ).accept( new RelationshipVisitor()
{
@Override
public void visit( long relId, int type, long startNode, long endNode )
{
relationshipDoDelete( relId, type, startNode, endNode );
}
} );
}
@Override
public boolean relationshipIsDeletedInThisTx( long relationshipId )
{
return relationshipsDeletedInTx != null && relationshipsDeletedInTx.contains( relationshipId );
}
@Override
public void nodeDoReplaceProperty( long nodeId, Property replacedProperty, DefinedProperty newProperty )
{
if ( replacedProperty.isDefined() )
{
getOrCreateNodeState( nodeId ).changeProperty( newProperty );
nodePropertyChanges().changeProperty( nodeId, replacedProperty.propertyKeyId(),
((DefinedProperty) replacedProperty).value(), newProperty.value() );
}
else
{
NodeState.Mutable nodeState = getOrCreateNodeState( nodeId );
nodeState.addProperty( newProperty );
nodePropertyChanges().addProperty( nodeId, newProperty.propertyKeyId(), newProperty.value() );
}
dataChanged();
}
@Override
public void relationshipDoReplaceProperty( long relationshipId,
Property replacedProperty,
DefinedProperty newProperty )
{
if ( replacedProperty.isDefined() )
{
getOrCreateRelationshipState( relationshipId ).changeProperty( newProperty );
}
else
{
getOrCreateRelationshipState( relationshipId ).addProperty( newProperty );
}
dataChanged();
}
@Override
public void graphDoReplaceProperty( Property replacedProperty, DefinedProperty newProperty )
{
if ( replacedProperty.isDefined() )
{
getOrCreateGraphState().changeProperty( newProperty );
}
else
{
getOrCreateGraphState().addProperty( newProperty );
}
dataChanged();
}
@Override
public void nodeDoRemoveProperty( long nodeId, DefinedProperty removedProperty )
{
getOrCreateNodeState( nodeId ).removeProperty( removedProperty );
nodePropertyChanges().removeProperty( nodeId, removedProperty.propertyKeyId(),
removedProperty.value() );
dataChanged();
}
@Override
public void relationshipDoRemoveProperty( long relationshipId, DefinedProperty removedProperty )
{
getOrCreateRelationshipState( relationshipId ).removeProperty( removedProperty );
dataChanged();
}
@Override
public void graphDoRemoveProperty( DefinedProperty removedProperty )
{
getOrCreateGraphState().removeProperty( removedProperty );
dataChanged();
}
@Override
public void nodeDoAddLabel( int labelId, long nodeId )
{
getOrCreateLabelStateNodeDiffSets( labelId ).add( nodeId );
getOrCreateNodeStateLabelDiffSets( nodeId ).add( labelId );
dataChanged();
}
@Override
public void nodeDoRemoveLabel( int labelId, long nodeId )
{
getOrCreateLabelStateNodeDiffSets( labelId ).remove( nodeId );
getOrCreateNodeStateLabelDiffSets( nodeId ).remove( labelId );
dataChanged();
}
@Override
public void labelDoCreateForName( String labelName, int id )
{
if ( createdLabelTokens == null )
{
createdLabelTokens = new HashMap<>();
}
createdLabelTokens.put( id, labelName );
changed();
}
@Override
public void propertyKeyDoCreateForName( String propertyKeyName, int id )
{
if ( createdPropertyKeyTokens == null )
{
createdPropertyKeyTokens = new HashMap<>();
}
createdPropertyKeyTokens.put( id, propertyKeyName );
changed();
}
@Override
public void relationshipTypeDoCreateForName( String labelName, int id )
{
if ( createdRelationshipTypeTokens == null )
{
createdRelationshipTypeTokens = new HashMap<>();
}
createdRelationshipTypeTokens.put( id, labelName );
changed();
}
@Override
public NodeState getNodeState( long id )
{
return NODE_STATE.get( this, id );
}
@Override
public RelationshipState getRelationshipState( long id )
{
return RELATIONSHIP_STATE.get( this, id );
}
public Cursor augmentSingleNodeCursor( Cursor cursor, long nodeId )
{
return hasChanges ? singleNodeCursor.get().init( cursor, nodeId ) : cursor;
}
public Cursor augmentPropertyCursor( Cursor cursor,
PropertyContainerState propertyContainerState )
{
return propertyContainerState.augmentPropertyCursor( propertyCursor, cursor );
}
public Cursor augmentSinglePropertyCursor( Cursor cursor,
PropertyContainerState propertyContainerState, int propertyKeyId )
{
return propertyContainerState.augmentSinglePropertyCursor( singlePropertyCursor, cursor, propertyKeyId );
}
public Cursor augmentLabelCursor( Cursor cursor, NodeState nodeState )
{
ReadableDiffSets labelDiffSets = nodeState.labelDiffSets();
if ( labelDiffSets.isEmpty() )
{
return cursor;
}
else
{
return labelCursor.get().init( cursor, labelDiffSets );
}
}
public Cursor augmentSingleLabelCursor( Cursor cursor, NodeState nodeState, int labelId )
{
ReadableDiffSets labelDiffSets = nodeState.labelDiffSets();
if ( labelDiffSets.isEmpty() )
{
return cursor;
}
else
{
return singleLabelCursor.get().init( cursor, labelDiffSets, labelId );
}
}
public Cursor augmentSingleRelationshipCursor( Cursor cursor,
long relationshipId )
{
return hasChanges ? singleRelationshipCursor.get().init( cursor, relationshipId ) : cursor;
}
@Override
public Cursor augmentIteratorRelationshipCursor( Cursor cursor,
RelationshipIterator iterator )
{
return hasChanges ? iteratorRelationshipCursor.get().init( cursor, iterator ) : cursor;
}
public Cursor augmentNodeRelationshipCursor( Cursor cursor,
NodeState nodeState,
Direction direction,
int[] relTypes )
{
return nodeState.augmentNodeRelationshipCursor( iteratorRelationshipCursor, cursor, direction, relTypes );
}
@Override
public Cursor augmentNodesGetAllCursor( Cursor cursor )
{
return hasChanges && nodes != null && !nodes.isEmpty()
? iteratorNodeCursor.get().init( cursor, nodes.getAdded().iterator() )
: cursor;
}
@Override
public Cursor augmentRelationshipsGetAllCursor( Cursor cursor )
{
return hasChanges && relationships != null && !relationships.isEmpty()
? iteratorRelationshipCursor.get().init( cursor, toPrimitiveIterator( relationships.getAdded().iterator() ) )
: cursor;
}
@Override
public ReadableDiffSets nodesWithLabelChanged( int labelId )
{
return LABEL_STATE.get( this, labelId ).nodeDiffSets();
}
private DiffSets procedureChanges()
{
return procedureChanges == null ? procedureChanges = new DiffSets<>() : procedureChanges;
}
private Map addedProcedures()
{
return addedProcedures == null ? addedProcedures = new HashMap<>() : addedProcedures;
}
@Override
public void procedureDoCreate( ProcedureSignature signature, String language, String body )
{
ProcedureDescriptor descriptor = new ProcedureDescriptor( signature, language, body );
procedureChanges().add( descriptor );
addedProcedures().put( signature.name(), descriptor );
hasChanges = true;
}
@Override
public void procedureDoDrop( ProcedureDescriptor descriptor )
{
procedureChanges().remove( descriptor );
if( addedProcedures != null )
{
addedProcedures.remove( descriptor.signature().name() );
}
hasChanges = true;
}
@Override
public Iterator augmentProcedures( Iterator procedures )
{
return procedureChanges != null ? procedureChanges.apply( procedures ) : procedures;
}
@Override
public ProcedureDescriptor getProcedure( ProcedureSignature.ProcedureName name )
{
return addedProcedures != null ? addedProcedures.get( name ) : null;
}
@Override
public void indexRuleDoAdd( IndexDescriptor descriptor )
{
DiffSets diff = indexChangesDiffSets();
if ( diff.unRemove( descriptor ) )
{
getOrCreateLabelState( descriptor.getLabelId() ).getOrCreateIndexChanges().unRemove( descriptor );
}
else
{
diff.add( descriptor );
getOrCreateLabelState( descriptor.getLabelId() ).getOrCreateIndexChanges().add( descriptor );
}
changed();
}
@Override
public void constraintIndexRuleDoAdd( IndexDescriptor descriptor )
{
constraintIndexChangesDiffSets().add( descriptor );
getOrCreateLabelState( descriptor.getLabelId() ).getOrCreateConstraintIndexChanges().add( descriptor );
changed();
}
@Override
public void indexDoDrop( IndexDescriptor descriptor )
{
indexChangesDiffSets().remove( descriptor );
getOrCreateLabelState( descriptor.getLabelId() ).getOrCreateIndexChanges().remove( descriptor );
changed();
}
@Override
public void constraintIndexDoDrop( IndexDescriptor descriptor )
{
constraintIndexChangesDiffSets().remove( descriptor );
getOrCreateLabelState( descriptor.getLabelId() ).getOrCreateConstraintIndexChanges().remove( descriptor );
changed();
}
@Override
public ReadableDiffSets indexDiffSetsByLabel( int labelId )
{
return LABEL_STATE.get( this, labelId ).indexChanges();
}
@Override
public ReadableDiffSets constraintIndexDiffSetsByLabel( int labelId )
{
return LABEL_STATE.get( this, labelId ).constraintIndexChanges();
}
@Override
public ReadableDiffSets indexChanges()
{
return ReadableDiffSets.Empty.ifNull( indexChanges );
}
private DiffSets indexChangesDiffSets()
{
if ( indexChanges == null )
{
indexChanges = new DiffSets<>();
}
return indexChanges;
}
@Override
public ReadableDiffSets constraintIndexChanges()
{
return ReadableDiffSets.Empty.ifNull( constraintIndexChanges );
}
private DiffSets constraintIndexChangesDiffSets()
{
if ( constraintIndexChanges == null )
{
constraintIndexChanges = new DiffSets<>();
}
return constraintIndexChanges;
}
@Override
public ReadableDiffSets addedAndRemovedNodes()
{
return ReadableDiffSets.Empty.ifNull( nodes );
}
private DiffSets nodes()
{
if ( nodes == null )
{
nodes = new DiffSets<>();
}
return nodes;
}
@Override
public int augmentNodeDegree( long nodeId, int degree, Direction direction )
{
return NODE_STATE.get( this, nodeId ).augmentDegree( direction, degree );
}
@Override
public int augmentNodeDegree( long nodeId, int degree, Direction direction, int typeId )
{
return NODE_STATE.get( this, nodeId ).augmentDegree( direction, degree, typeId );
}
@Override
public PrimitiveIntIterator nodeRelationshipTypes( long nodeId )
{
return NODE_STATE.get( this, nodeId ).relationshipTypes();
}
@Override
public ReadableRelationshipDiffSets addedAndRemovedRelationships()
{
return ReadableRelationshipDiffSets.Empty.ifNull( relationships );
}
private RelationshipDiffSets relationships()
{
if ( relationships == null )
{
relationships = new RelationshipDiffSets<>( this );
}
return relationships;
}
@Override
public Iterable modifiedRelationships()
{
return RELATIONSHIP_STATE.values( this );
}
private LabelState.Mutable getOrCreateLabelState( int labelId )
{
return LABEL_STATE.getOrCreate( this, labelId );
}
private NodeState.Mutable getOrCreateNodeState( long nodeId )
{
return NODE_STATE.getOrCreate( this, nodeId );
}
private RelationshipState.Mutable getOrCreateRelationshipState( long relationshipId )
{
return RELATIONSHIP_STATE.getOrCreate( this, relationshipId );
}
private GraphState getOrCreateGraphState()
{
if ( graphState == null )
{
graphState = new GraphState();
}
return graphState;
}
@Override
public void constraintDoAdd( UniquenessConstraint constraint, long indexId )
{
constraintsChangesDiffSets().add( constraint );
createdConstraintIndexesByConstraint().put( constraint, indexId );
getOrCreateLabelState( constraint.label() ).getOrCreateConstraintsChanges().add( constraint );
changed();
}
@Override
public void constraintDoAdd( NodePropertyExistenceConstraint constraint )
{
constraintsChangesDiffSets().add( constraint );
getOrCreateLabelState( constraint.label() ).getOrCreateConstraintsChanges().add( constraint );
hasChanges = true;
}
@Override
public void constraintDoAdd( RelationshipPropertyExistenceConstraint constraint )
{
constraintsChangesDiffSets().add( constraint );
relationshipConstraintChangesByType( constraint.relationshipType() ).add( constraint );
hasChanges = true;
}
@Override
public ReadableDiffSets constraintsChangesForLabelAndProperty( int labelId,
final int propertyKey )
{
return LABEL_STATE.get( this, labelId ).nodeConstraintsChanges().filterAdded(
new Predicate()
{
@Override
public boolean test( NodePropertyConstraint item )
{
return item.propertyKey() == propertyKey;
}
} );
}
@Override
public ReadableDiffSets constraintsChangesForLabel( int labelId )
{
return LABEL_STATE.get( this, labelId ).nodeConstraintsChanges();
}
@Override
public ReadableDiffSets constraintsChangesForRelationshipType( int relTypeId )
{
DiffSets changes = null;
if ( relationshipConstraintChanges != null )
{
changes = relationshipConstraintChanges.get( relTypeId );
}
return ReadableDiffSets.Empty.ifNull( changes );
}
@Override
public ReadableDiffSets constraintsChangesForRelationshipTypeAndProperty(
int relTypeId, final int propertyKey )
{
return constraintsChangesForRelationshipType( relTypeId ).filterAdded(
new Predicate()
{
@Override
public boolean test( RelationshipPropertyConstraint constraint )
{
return constraint.propertyKey() == propertyKey;
}
}
);
}
@Override
public ReadableDiffSets constraintsChanges()
{
return ReadableDiffSets.Empty.ifNull( constraintsChanges );
}
private DiffSets constraintsChangesDiffSets()
{
if ( constraintsChanges == null )
{
constraintsChanges = new DiffSets<>();
}
return constraintsChanges;
}
@Override
public void constraintDoDrop( NodePropertyConstraint constraint )
{
constraintsChangesDiffSets().remove( constraint );
if ( constraint instanceof UniquenessConstraint )
{
constraintIndexDoDrop( new IndexDescriptor( constraint.label(), constraint.propertyKey() ) );
}
getOrCreateLabelState( constraint.label() ).getOrCreateConstraintsChanges().remove( constraint );
changed();
}
@Override
public void constraintDoDrop( RelationshipPropertyConstraint constraint )
{
constraintsChangesDiffSets().remove( constraint );
relationshipConstraintChangesByType( constraint.relationshipType() ).remove( constraint );
hasChanges = true;
}
private DiffSets relationshipConstraintChangesByType( int relTypeId )
{
if ( relationshipConstraintChanges == null )
{
relationshipConstraintChanges = Primitive.intObjectMap();
}
DiffSets diffSets = relationshipConstraintChanges.get( relTypeId );
if ( diffSets == null )
{
relationshipConstraintChanges.put( relTypeId, diffSets = new DiffSets<>() );
}
return diffSets;
}
@Override
public boolean constraintDoUnRemove( NodePropertyConstraint constraint )
{
if ( constraintsChangesDiffSets().unRemove( constraint ) )
{
getOrCreateLabelState( constraint.label() ).getOrCreateConstraintsChanges().unRemove( constraint );
return true;
}
return false;
}
@Override
public boolean constraintIndexDoUnRemove( IndexDescriptor index )
{
if ( constraintIndexChangesDiffSets().unRemove( index ) )
{
LABEL_STATE.getOrCreate( this, index.getLabelId() ).getOrCreateConstraintIndexChanges().unRemove( index );
return true;
}
return false;
}
@Override
public Iterable constraintIndexesCreatedInTx()
{
if ( createdConstraintIndexesByConstraint != null && !createdConstraintIndexesByConstraint.isEmpty() )
{
return map( new Function()
{
@Override
public IndexDescriptor apply( UniquenessConstraint constraint )
{
return new IndexDescriptor( constraint.label(), constraint.propertyKey() );
}
}, createdConstraintIndexesByConstraint.keySet() );
}
return Iterables.empty();
}
@Override
public Long indexCreatedForConstraint( UniquenessConstraint constraint )
{
return createdConstraintIndexesByConstraint == null ? null :
createdConstraintIndexesByConstraint.get( constraint );
}
@Override
public ReadableDiffSets indexUpdatesForScanOrSeek( IndexDescriptor descriptor, Object value )
{
return ReadableDiffSets.Empty.ifNull( (value == null) ?
getIndexUpdatesForScanOrSeek(
descriptor.getLabelId(),
descriptor.getPropertyKeyId()
)
:
getIndexUpdatesForScanOrSeek(
descriptor.getLabelId(), /*create=*/false,
Property.property( descriptor.getPropertyKeyId(), value )
)
);
}
@Override
public ReadableDiffSets indexUpdatesForRangeSeekByNumber( IndexDescriptor descriptor,
Number lower, boolean includeLower,
Number upper, boolean includeUpper )
{
return ReadableDiffSets.Empty.ifNull(
getIndexUpdatesForRangeSeekByNumber( descriptor, lower, includeLower, upper, includeUpper )
);
}
private ReadableDiffSets getIndexUpdatesForRangeSeekByNumber( IndexDescriptor descriptor,
Number lower, boolean includeLower,
Number upper, boolean includeUpper )
{
TreeMap> sortedUpdates = getSortedIndexUpdates( descriptor );
if ( sortedUpdates == null )
{
return null;
}
DefinedProperty selectedLower;
boolean selectedIncludeLower;
DefinedProperty selectedUpper;
boolean selectedIncludeUpper;
int propertyKeyId = descriptor.getPropertyKeyId();
if ( lower == null )
{
selectedLower = DefinedProperty.numberProperty( propertyKeyId, NUMBER.lowLimit.castValue( Number.class ) );
selectedIncludeLower = NUMBER.lowLimit.isInclusive;
}
else
{
selectedLower = numberProperty( propertyKeyId, lower );
selectedIncludeLower = includeLower;
}
if ( upper == null )
{
selectedUpper = DefinedProperty.numberProperty( propertyKeyId, NUMBER.highLimit.castValue( Number.class ) );
selectedIncludeUpper = NUMBER.highLimit.isInclusive;
}
else
{
selectedUpper = numberProperty( propertyKeyId, upper );
selectedIncludeUpper = includeUpper;
}
DiffSets diffs = new DiffSets<>();
for ( Map.Entry> entry : sortedUpdates.subMap( selectedLower, selectedIncludeLower, selectedUpper, selectedIncludeUpper ).entrySet() )
{
DiffSets diffSets = entry.getValue();
diffs.addAll( diffSets.getAdded().iterator() );
diffs.removeAll( diffSets.getRemoved().iterator() );
}
return diffs;
}
@Override
public ReadableDiffSets indexUpdatesForRangeSeekByString( IndexDescriptor descriptor,
String lower, boolean includeLower,
String upper, boolean includeUpper )
{
return ReadableDiffSets.Empty.ifNull(
getIndexUpdatesForRangeSeekByString( descriptor, lower, includeLower, upper, includeUpper )
);
}
private ReadableDiffSets getIndexUpdatesForRangeSeekByString( IndexDescriptor descriptor,
String lower, boolean includeLower,
String upper, boolean includeUpper )
{
TreeMap> sortedUpdates = getSortedIndexUpdates( descriptor );
if ( sortedUpdates == null )
{
return null;
}
DefinedProperty selectedLower;
boolean selectedIncludeLower;
DefinedProperty selectedUpper;
boolean selectedIncludeUpper;
int propertyKeyId = descriptor.getPropertyKeyId();
if ( lower == null )
{
selectedLower = DefinedProperty.stringProperty( propertyKeyId, STRING.lowLimit.castValue( String.class ) );
selectedIncludeLower = STRING.lowLimit.isInclusive;
}
else
{
selectedLower = DefinedProperty.stringProperty( propertyKeyId, lower );
selectedIncludeLower = includeLower;
}
if ( upper == null )
{
selectedUpper = DefinedProperty.booleanProperty( propertyKeyId, STRING.highLimit.castValue( Boolean.class ).booleanValue() );
selectedIncludeUpper = STRING.highLimit.isInclusive;
}
else
{
selectedUpper = DefinedProperty.stringProperty( propertyKeyId, upper );
selectedIncludeUpper = includeUpper;
}
DiffSets diffs = new DiffSets<>();
for ( Map.Entry> entry : sortedUpdates.subMap( selectedLower, selectedIncludeLower, selectedUpper, selectedIncludeUpper ).entrySet() )
{
DiffSets diffSets = entry.getValue();
diffs.addAll( diffSets.getAdded().iterator() );
diffs.removeAll( diffSets.getRemoved().iterator() );
}
return diffs;
}
@Override
public ReadableDiffSets indexUpdatesForRangeSeekByPrefix( IndexDescriptor descriptor, String prefix )
{
return ReadableDiffSets.Empty.ifNull( getIndexUpdatesForRangeSeekByPrefix( descriptor, prefix ) );
}
private ReadableDiffSets getIndexUpdatesForRangeSeekByPrefix( IndexDescriptor descriptor, String prefix )
{
TreeMap> sortedUpdates = getSortedIndexUpdates( descriptor );
if ( sortedUpdates == null )
{
return null;
}
int propertyKeyId = descriptor.getPropertyKeyId();
DefinedProperty floor = Property.stringProperty( propertyKeyId, prefix );
DiffSets diffs = new DiffSets<>();
for ( Map.Entry> entry : sortedUpdates.tailMap( floor ).entrySet() )
{
DefinedProperty key = entry.getKey();
if ( key.propertyKeyId() == propertyKeyId && key.value().toString().startsWith( prefix ) )
{
DiffSets diffSets = entry.getValue();
diffs.addAll( diffSets.getAdded().iterator() );
diffs.removeAll( diffSets.getRemoved().iterator() );
}
else
{
break;
}
}
return diffs;
}
// Ensure sorted index updates for a given index. This is needed for range query support and
// may involve converting the existing hash map first
//
private TreeMap> getSortedIndexUpdates( IndexDescriptor descriptor )
{
if ( indexUpdates == null )
{
return null;
}
Map> updates = indexUpdates.get( descriptor.getLabelId() );
if ( updates == null )
{
return null;
}
TreeMap> sortedUpdates;
if ( updates instanceof TreeMap )
{
sortedUpdates = (TreeMap>) updates;
}
else
{
sortedUpdates = new TreeMap<>( DefinedProperty.COMPARATOR );
sortedUpdates.putAll( updates );
indexUpdates.put( descriptor.getLabelId(), sortedUpdates );
}
return sortedUpdates;
}
@Override
public void indexDoUpdateProperty( IndexDescriptor descriptor, long nodeId,
DefinedProperty propertyBefore, DefinedProperty propertyAfter )
{
DiffSets before = getIndexUpdatesForScanOrSeek( descriptor.getLabelId(), true, propertyBefore );
if ( before != null )
{
before.remove( nodeId );
if ( before.getRemoved().contains( nodeId ) )
{
getOrCreateNodeState( nodeId ).addIndexDiff( before );
}
else
{
getOrCreateNodeState( nodeId ).removeIndexDiff( before );
}
}
DiffSets after = getIndexUpdatesForScanOrSeek( descriptor.getLabelId(), true, propertyAfter );
if ( after != null )
{
after.add( nodeId );
if ( after.getAdded().contains( nodeId ) )
{
getOrCreateNodeState( nodeId ).addIndexDiff( after );
}
else
{
getOrCreateNodeState( nodeId ).removeIndexDiff( after );
}
}
}
private DiffSets getIndexUpdatesForScanOrSeek( int label, boolean create, DefinedProperty property )
{
if ( property == null )
{
return null;
}
if ( indexUpdates == null )
{
if ( !create )
{
return null;
}
indexUpdates = Primitive.intObjectMap();
}
Map> updates = indexUpdates.get( label );
if ( updates == null )
{
if ( !create )
{
return null;
}
indexUpdates.put( label, updates = new HashMap<>() );
}
DiffSets diffs = updates.get( property );
if ( diffs == null && create )
{
updates.put( property, diffs = new DiffSets<>() );
}
return diffs;
}
private DiffSets getIndexUpdatesForScanOrSeek( int label, int propertyKeyId )
{
if ( indexUpdates == null )
{
return null;
}
Map> updates = indexUpdates.get( label );
if ( updates == null )
{
return null;
}
DiffSets diffs = new DiffSets<>();
for ( Map.Entry> entry : updates.entrySet() )
{
if ( entry.getKey().propertyKeyId() == propertyKeyId )
{
diffs.addAll( entry.getValue().getAdded().iterator() );
diffs.removeAll( entry.getValue().getRemoved().iterator() );
}
}
return diffs;
}
private Map createdConstraintIndexesByConstraint()
{
if ( createdConstraintIndexesByConstraint == null )
{
createdConstraintIndexesByConstraint = new HashMap<>();
}
return createdConstraintIndexesByConstraint;
}
private boolean hasNodeState( long nodeId )
{
return nodeStatesMap != null && nodeStatesMap.containsKey( nodeId );
}
private PropertyChanges nodePropertyChanges()
{
return propertyChangesForNodes == null ?
propertyChangesForNodes = new PropertyChanges() : propertyChangesForNodes;
}
@Override
public PrimitiveLongIterator augmentNodesGetAll( PrimitiveLongIterator committed )
{
return addedAndRemovedNodes().augment( committed );
}
@Override
public RelationshipIterator augmentRelationshipsGetAll( RelationshipIterator committed )
{
return addedAndRemovedRelationships().augment( committed );
}
@Override
public boolean relationshipVisit( long relId, RelationshipVisitor visitor ) throws EX
{
return RELATIONSHIP_STATE.get( this, relId ).accept( visitor );
}
@Override
public void nodeLegacyIndexDoCreate( String indexName, Map customConfig )
{
assert customConfig != null;
if ( createdNodeLegacyIndexes == null )
{
createdNodeLegacyIndexes = new HashMap<>();
}
createdNodeLegacyIndexes.put( indexName, customConfig );
changed();
}
@Override
public void relationshipLegacyIndexDoCreate( String indexName, Map customConfig )
{
assert customConfig != null;
if ( createdRelationshipLegacyIndexes == null )
{
createdRelationshipLegacyIndexes = new HashMap<>();
}
createdRelationshipLegacyIndexes.put( indexName, customConfig );
changed();
}
@Override
public boolean hasDataChanges()
{
return hasDataChanges;
}
private void recordNodeDeleted( long id )
{
if ( nodesDeletedInTx == null )
{
nodesDeletedInTx = Primitive.longSet();
}
nodesDeletedInTx.add( id );
}
private void recordRelationshipDeleted( long id )
{
if ( relationshipsDeletedInTx == null )
{
relationshipsDeletedInTx = Primitive.longSet();
}
relationshipsDeletedInTx.add( id );
}
}