All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 ); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy