relationshipTypes();
/**
* Returns degree, e.g. number of relationships for this node.
@@ -109,27 +99,4 @@ public interface NodeItem
* @return whether or not this node has the given label.
*/
boolean hasLabel( int labelId );
-
- /**
- * @return label ids attached to this node.
- */
- PrimitiveIntIterator getLabels();
-
- /**
- * @param direction {@link Direction} to filter on.
- * @param typeIds relationship type ids to filter on.
- * @return relationship ids for the given direction and relationship types.
- */
- RelationshipIterator getRelationships( Direction direction, int[] typeIds );
-
- /**
- * @param direction {@link Direction} to filter on.
- * @return relationship ids for the given direction.
- */
- RelationshipIterator getRelationships( Direction direction );
-
- /**
- * @return relationship type ids for all relationships attached to this node.
- */
- PrimitiveIntIterator getRelationshipTypes();
}
diff --git a/community/primitive-collections/src/main/java/org/neo4j/cursor/GenericCursor.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipTypeItem.java
similarity index 55%
rename from community/primitive-collections/src/main/java/org/neo4j/cursor/GenericCursor.java
rename to community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipTypeItem.java
index ec6134648f967..cc76d6e5177c2 100644
--- a/community/primitive-collections/src/main/java/org/neo4j/cursor/GenericCursor.java
+++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipTypeItem.java
@@ -17,36 +17,10 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.neo4j.cursor;
+package org.neo4j.storageengine.api;
-/**
- * Generic base class for cursor where clients
- * can access the current state through the get method.
- *
- * Subclasses must implement the {@link #next()} method and
- * set the current field to the next item.
- *
- * @param the type of instances being iterated
- */
-public abstract class GenericCursor
- implements Cursor
-{
- protected T current;
+import java.util.function.IntSupplier;
- @Override
- public T get()
- {
- if ( current == null )
- {
- throw new IllegalStateException();
- }
-
- return current;
- }
-
- @Override
- public void close()
- {
- current = null;
- }
+public interface RelationshipTypeItem extends IntSupplier
+{
}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java
index c136f333c9e84..6dc3472209862 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java
@@ -40,14 +40,13 @@
import org.neo4j.kernel.api.txstate.LegacyIndexTransactionState;
import org.neo4j.kernel.api.txstate.TransactionState;
import org.neo4j.kernel.api.txstate.TxStateHolder;
+import org.neo4j.kernel.impl.api.TwoPhaseNodeForRelationshipLockingTest.RelationshipData;
import org.neo4j.kernel.impl.api.operations.EntityReadOperations;
import org.neo4j.kernel.impl.api.operations.EntityWriteOperations;
import org.neo4j.kernel.impl.api.operations.SchemaReadOperations;
import org.neo4j.kernel.impl.api.operations.SchemaStateOperations;
import org.neo4j.kernel.impl.api.operations.SchemaWriteOperations;
-import org.neo4j.kernel.impl.api.state.StubCursors;
import org.neo4j.kernel.impl.api.state.TxState;
-import org.neo4j.kernel.impl.api.store.CursorRelationshipIterator;
import org.neo4j.kernel.impl.api.store.RelationshipIterator;
import org.neo4j.kernel.impl.api.store.StoreSingleNodeCursor;
import org.neo4j.kernel.impl.factory.CanWrite;
@@ -56,16 +55,13 @@
import org.neo4j.kernel.impl.locking.ResourceTypes;
import org.neo4j.kernel.impl.locking.SimpleStatementLocks;
import org.neo4j.kernel.impl.proc.Procedures;
-import org.neo4j.kernel.impl.util.Cursors;
import org.neo4j.storageengine.api.Direction;
import org.neo4j.storageengine.api.NodeItem;
-import org.neo4j.storageengine.api.RelationshipItem;
import org.neo4j.storageengine.api.StorageStatement;
import static org.junit.Assert.assertSame;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@@ -73,6 +69,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import static org.neo4j.kernel.impl.api.TwoPhaseNodeForRelationshipLockingTest.returnRelationships;
import static org.neo4j.kernel.impl.locking.ResourceTypes.schemaResource;
public class LockingStatementOperationsTest
@@ -496,12 +493,7 @@ public void shouldNotAcquireEntityWriteLockBeforeSettingPropertyOnJustCreatedRel
public void detachDeleteNodeWithoutRelationshipsExclusivelyLockNode() throws KernelException
{
long nodeId = 1L;
-
- NodeItem nodeItem = mock( NodeItem.class );
- when( nodeItem.getRelationships( Direction.BOTH ) ).thenReturn( RelationshipIterator.EMPTY );
- StoreSingleNodeCursor nodeCursor = mock( StoreSingleNodeCursor.class );
- when( nodeCursor.get() ).thenReturn( nodeItem );
- when( entityReadOps.nodeCursorById( state, nodeId ) ).thenReturn( nodeCursor );
+ returnRelationships( entityReadOps, state, nodeId, false );
lockingOps.nodeDetachDelete( state, nodeId );
@@ -513,33 +505,17 @@ public void detachDeleteNodeWithoutRelationshipsExclusivelyLockNode() throws Ker
@Test
public void detachDeleteNodeExclusivelyLockNodes() throws KernelException
{
- long startNodeId = 1L;
- long endNodeId = 2L;
-
- RelationshipItem relationshipItem = StubCursors.asRelationship( 1L, 0, startNodeId, endNodeId, null );
- CursorRelationshipIterator relationshipIterator =
- new CursorRelationshipIterator( Cursors.cursor( relationshipItem ) );
-
- NodeItem nodeItem = mock( NodeItem.class );
- when( nodeItem.getRelationships( Direction.BOTH ) ).thenReturn( relationshipIterator );
- StoreSingleNodeCursor nodeCursor = mock( StoreSingleNodeCursor.class );
- when( nodeCursor.get() ).thenReturn( nodeItem );
- when( entityReadOps.nodeCursorById( state, startNodeId ) ).thenReturn( nodeCursor );
- doAnswer( invocation ->
- {
- RelationshipVisitor visitor = invocation.getArgumentAt( 2, RelationshipVisitor.class );
- visitor.visit( relationshipItem.id(), relationshipItem.type(), relationshipItem.startNode(),
- relationshipItem.endNode() );
- return null;
- } ).when( entityReadOps ).relationshipVisit( eq(state), anyLong(), any() );
+ long nodeId = 1L;
+ RelationshipData relationship = new RelationshipData( 1, nodeId, 2L );
+ returnRelationships( entityReadOps, state, nodeId, false, relationship );
- lockingOps.nodeDetachDelete( state, startNodeId );
+ lockingOps.nodeDetachDelete( state, nodeId );
- order.verify( locks ).acquireExclusive( LockTracer.NONE, ResourceTypes.NODE, startNodeId );
- order.verify( locks ).acquireExclusive( LockTracer.NONE, ResourceTypes.NODE, endNodeId );
- order.verify( locks, times( 0 ) ).releaseExclusive( ResourceTypes.NODE, startNodeId );
- order.verify( locks, times( 0 ) ).releaseExclusive( ResourceTypes.NODE, endNodeId );
- order.verify( entityWriteOps ).nodeDetachDelete( state, startNodeId );
+ order.verify( locks ).acquireExclusive( LockTracer.NONE, ResourceTypes.NODE, relationship.startNodeId );
+ order.verify( locks ).acquireExclusive( LockTracer.NONE, ResourceTypes.NODE, relationship.endNodeId );
+ order.verify( locks, times( 0 ) ).releaseExclusive( ResourceTypes.NODE, relationship.startNodeId );
+ order.verify( locks, times( 0 ) ).releaseExclusive( ResourceTypes.NODE, relationship.endNodeId );
+ order.verify( entityWriteOps ).nodeDetachDelete( state, nodeId );
}
private static class SimpleTxStateHolder implements TxStateHolder
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/TwoPhaseNodeForRelationshipLockingTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/TwoPhaseNodeForRelationshipLockingTest.java
index 67711cb7f8c4b..140655ca85058 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/TwoPhaseNodeForRelationshipLockingTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/TwoPhaseNodeForRelationshipLockingTest.java
@@ -19,34 +19,31 @@
*/
package org.neo4j.kernel.impl.api;
-import java.util.HashSet;
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-import org.apache.commons.lang3.NotImplementedException;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.HashSet;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
import org.neo4j.cursor.Cursor;
import org.neo4j.function.ThrowingConsumer;
import org.neo4j.kernel.api.exceptions.EntityNotFoundException;
import org.neo4j.kernel.api.exceptions.KernelException;
import org.neo4j.kernel.impl.api.operations.EntityReadOperations;
-import org.neo4j.kernel.impl.api.store.RelationshipIterator;
import org.neo4j.kernel.impl.locking.LockTracer;
import org.neo4j.kernel.impl.locking.Locks;
import org.neo4j.kernel.impl.locking.ResourceTypes;
import org.neo4j.kernel.impl.locking.SimpleStatementLocks;
import org.neo4j.storageengine.api.Direction;
+import org.neo4j.storageengine.api.EntityType;
import org.neo4j.storageengine.api.NodeItem;
+import org.neo4j.storageengine.api.RelationshipItem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -73,10 +70,10 @@ public void shouldLockNodesInOrderAndConsumeTheRelationships() throws Throwable
Collector collector = new Collector();
TwoPhaseNodeForRelationshipLocking locking = new TwoPhaseNodeForRelationshipLocking( ops, collector );
- returnRelationships( nodeId, false, 21L, 22L, 23L );
- returnNodesForRelationship( 21L, nodeId, 43L );
- returnNodesForRelationship( 22L, 40L, nodeId );
- returnNodesForRelationship( 23L, nodeId, 41L );
+ RelationshipData relationship1 = new RelationshipData( 21L, nodeId, 43L );
+ RelationshipData relationship2 = new RelationshipData( 22L, 40L, nodeId );
+ RelationshipData relationship3 = new RelationshipData( 23L, nodeId, 41L );
+ returnRelationships( ops, state, nodeId, false, relationship1, relationship2, relationship3 );
InOrder inOrder = inOrder( locks );
@@ -92,16 +89,17 @@ public void shouldLockNodesInOrderAndConsumeTheRelationships() throws Throwable
}
@Test
- public void shouldLockNodesInOrderAndConsumeTheRelationshipsAndRetryIfTheNewRelationshipsAreCreated() throws Throwable
+ public void shouldLockNodesInOrderAndConsumeTheRelationshipsAndRetryIfTheNewRelationshipsAreCreated()
+ throws Throwable
{
// given
Collector collector = new Collector();
TwoPhaseNodeForRelationshipLocking locking = new TwoPhaseNodeForRelationshipLocking( ops, collector );
- returnRelationships( nodeId, true, 21L, 22L, 23L );
- returnNodesForRelationship( 21L, nodeId, 43L );
- returnNodesForRelationship( 22L, 40L, nodeId );
- returnNodesForRelationship( 23L, nodeId, 41L );
+ RelationshipData relationship1 = new RelationshipData( 21L, nodeId, 43L );
+ RelationshipData relationship2 = new RelationshipData( 22L, 40L, nodeId );
+ RelationshipData relationship3 = new RelationshipData( 23L, nodeId, 41L );
+ returnRelationships( ops, state, nodeId, true, relationship1, relationship2, relationship3 );
InOrder inOrder = inOrder( locks );
@@ -129,7 +127,7 @@ public void lockNodeWithoutRelationships() throws Exception
{
Collector collector = new Collector();
TwoPhaseNodeForRelationshipLocking locking = new TwoPhaseNodeForRelationshipLocking( ops, collector );
- returnRelationships( nodeId, false );
+ returnRelationships( ops, state, nodeId, false );
locking.lockAllNodesAndConsumeRelationships( nodeId, state );
@@ -137,65 +135,69 @@ public void lockNodeWithoutRelationships() throws Exception
verifyNoMoreInteractions( locks );
}
- private void returnNodesForRelationship( final long relId, final long startNodeId, final long endNodeId )
- throws Exception
+ public static class RelationshipData
{
- doAnswer( new Answer()
+ public final long relId;
+ public final long startNodeId;
+ public final long endNodeId;
+
+ RelationshipData( long relId, long startNodeId, long endNodeId )
{
- @Override
- public Void answer( InvocationOnMock invocation ) throws Throwable
- {
- @SuppressWarnings( "unchecked" )
- RelationshipVisitor visitor =
- (RelationshipVisitor) invocation.getArguments()[2];
- visitor.visit( relId, 6, startNodeId, endNodeId );
- return null;
- }
- } ).when( ops ).relationshipVisit( eq( state ), eq( relId ), any( RelationshipVisitor.class ) );
+ this.relId = relId;
+ this.startNodeId = startNodeId;
+ this.endNodeId = endNodeId;
+ }
+
+ RelationshipItem asRelationshipItem()
+ {
+ RelationshipItem rel = mock( RelationshipItem.class );
+ when( rel.id() ).thenReturn( relId );
+ when( rel.startNode() ).thenReturn( startNodeId );
+ when( rel.endNode() ).thenReturn( endNodeId );
+ return rel;
+ }
}
- private void returnRelationships( long nodeId, final boolean skipFirst, final long... relIds )
- throws EntityNotFoundException
+ static void returnRelationships( EntityReadOperations ops, KernelStatement state, long nodeId,
+ final boolean skipFirst, final RelationshipData... relIds ) throws EntityNotFoundException
{
- //noinspection unchecked
- Cursor cursor = mock( Cursor.class );
- when( ops.nodeCursorById( state, nodeId ) ).thenReturn( cursor );
NodeItem nodeItem = mock( NodeItem.class );
- when( cursor.get() ).thenReturn( nodeItem );
- when( nodeItem.getRelationships( Direction.BOTH ) ).thenAnswer( new Answer()
+ when( nodeItem.relationships( Direction.BOTH ) ).thenAnswer( new Answer>()
{
private boolean first = skipFirst;
@Override
- public RelationshipIterator answer( InvocationOnMock invocation ) throws Throwable
+ public Cursor answer( InvocationOnMock invocation ) throws Throwable
{
try
{
- return new RelationshipIterator()
+ return new Cursor()
{
private int i = first ? 1 : 0;
+ private RelationshipData relationshipData = null;
@Override
- public boolean relationshipVisit( long relationshipId,
- RelationshipVisitor visitor )
+ public boolean next()
{
- throw new NotImplementedException( "don't call this!" );
+ boolean next = i < relIds.length;
+ relationshipData = next ? relIds[i++] : null;
+ return next;
}
@Override
- public boolean hasNext()
+ public RelationshipItem get()
{
- return i < relIds.length;
+ if ( relationshipData == null )
+ {
+ throw new NoSuchElementException();
+ }
+
+ return relationshipData.asRelationshipItem();
}
@Override
- public long next()
+ public void close()
{
- if ( !hasNext() )
- {
- throw new NoSuchElementException();
- }
- return relIds[i++];
}
};
}
@@ -204,7 +206,42 @@ public long next()
first = false;
}
}
- });
+ } );
+
+ when( ops.nodeCursorById( state, nodeId ) ).thenAnswer( invocationOnMock ->
+ {
+ Cursor cursor = new Cursor()
+ {
+ private int i = 0;
+
+ @Override
+ public boolean next()
+ {
+ return i++ == 0;
+ }
+
+ @Override
+ public NodeItem get()
+ {
+ if ( i != 1 )
+ {
+ throw new NoSuchElementException();
+ }
+ return nodeItem;
+ }
+
+ @Override
+ public void close()
+ {
+
+ }
+ };
+ if ( !cursor.next() )
+ {
+ throw new EntityNotFoundException( EntityType.NODE, nodeId );
+ }
+ return cursor;
+ } );
}
private static class Collector implements ThrowingConsumer
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursorTest.java
new file mode 100644
index 0000000000000..8a93e17957f5e
--- /dev/null
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/cursor/TxSingleNodeCursorTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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 .
+ */
+package org.neo4j.kernel.impl.api.cursor;
+
+import org.junit.Test;
+
+import org.neo4j.kernel.api.txstate.TransactionState;
+import org.neo4j.kernel.impl.util.Cursors;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TxSingleNodeCursorTest
+{
+
+ private final TransactionState state = mock( TransactionState.class );
+ private TxSingleNodeCursor cursor = new TxSingleNodeCursor( state, ( l ) ->
+ {
+ } );
+
+ @Test
+ public void shouldNotLoopForeverWhenNodesAreAddedToTheTxState() throws Exception
+ {
+ // given
+ int nodeId = 42;
+ when( state.nodeIsDeletedInThisTx( nodeId ) ).thenReturn( false );
+ when( state.nodeIsAddedInThisTx( nodeId ) ).thenReturn( true );
+
+ // when
+ cursor.init( Cursors.empty(), nodeId );
+
+ // then
+ assertTrue( cursor.next() );
+ assertFalse( cursor.next() );
+ }
+}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java
index face7d8b6b964..a810cfa573e4e 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java
@@ -26,11 +26,14 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.function.IntSupplier;
+import org.neo4j.collection.primitive.Primitive;
import org.neo4j.collection.primitive.PrimitiveIntCollections;
+import org.neo4j.collection.primitive.PrimitiveIntSet;
import org.neo4j.collection.primitive.PrimitiveLongCollections;
-import org.neo4j.cursor.Cursor;
import org.neo4j.kernel.api.exceptions.EntityNotFoundException;
import org.neo4j.kernel.api.index.IndexDescriptor;
import org.neo4j.kernel.api.txstate.TransactionState;
@@ -40,7 +43,6 @@
import org.neo4j.kernel.impl.api.legacyindex.InternalAutoIndexing;
import org.neo4j.kernel.impl.api.store.StoreStatement;
import org.neo4j.kernel.impl.index.LegacyIndexStore;
-import org.neo4j.storageengine.api.NodeItem;
import org.neo4j.storageengine.api.StoreReadLayer;
import static org.junit.Assert.assertEquals;
@@ -333,12 +335,7 @@ private void commitLabels( Labels... labels ) throws Exception
for ( int label : nodeLabels.labelIds )
{
- Collection nodes = allLabels.get( label );
- if ( nodes == null )
- {
- nodes = new ArrayList<>();
- allLabels.put( label, nodes );
- }
+ Collection nodes = allLabels.computeIfAbsent( label, k -> new ArrayList<>() );
nodes.add( nodeLabels.nodeId );
}
}
@@ -360,25 +357,20 @@ private void commitLabels( Integer... labels ) throws Exception
commitLabels( labels( nodeId, labels ) );
}
- private void assertLabels( Integer... labels ) throws EntityNotFoundException
+ private void assertLabels( int... labels ) throws EntityNotFoundException
{
- try ( Cursor cursor = txContext.nodeCursorById( state, nodeId ) )
+ txContext.nodeCursorById( state, nodeId ).forAll( node ->
{
- if ( cursor.next() )
- {
- assertEquals( asSet( labels ), PrimitiveIntCollections.toSet( cursor.get().getLabels() ) );
- }
- }
+ PrimitiveIntSet collect = node.labels().collect( Primitive.intSet(), IntSupplier::getAsInt );
+ assertEquals( PrimitiveIntCollections.asSet( labels ), collect );
+ } );
- for ( int label : labels )
+ txContext.nodeCursorById( state, nodeId ).forAll( node ->
{
- try ( Cursor cursor = txContext.nodeCursorById( state, nodeId ) )
+ for ( int label : labels )
{
- if ( cursor.next() )
- {
- assertTrue( "Expected labels not found on node", cursor.get().hasLabel( label ) );
- }
+ assertTrue( "Expected labels not found on node", node.hasLabel( label ) );
}
- }
+ } );
}
}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java
index 19406a8e62104..fd05a1d13fe03 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StubCursors.java
@@ -26,7 +26,7 @@
import org.neo4j.cursor.Cursor;
import org.neo4j.helpers.collection.Iterables;
-import org.neo4j.kernel.api.cursor.NodeItemHelper;
+import org.neo4j.kernel.api.cursor.EntityItemHelper;
import org.neo4j.kernel.api.cursor.RelationshipItemHelper;
import org.neo4j.kernel.api.properties.DefinedProperty;
import org.neo4j.kernel.impl.util.Cursors;
@@ -36,6 +36,9 @@
import org.neo4j.storageengine.api.NodeItem;
import org.neo4j.storageengine.api.PropertyItem;
import org.neo4j.storageengine.api.RelationshipItem;
+import org.neo4j.storageengine.api.RelationshipTypeItem;
+
+import static org.neo4j.kernel.impl.util.Cursors.empty;
/**
* Stub cursors to be used for testing.
@@ -44,167 +47,174 @@ public class StubCursors
{
public static Cursor asNodeCursor( final long nodeId )
{
- return asNodeCursor( nodeId, Cursors.empty(), Cursors.empty() );
+ return asNodeCursor( nodeId, empty(), empty() );
}
- public static Cursor asNodeCursor( final long... nodeIds)
+ public static Cursor asNodeCursor( final long... nodeIds )
{
NodeItem[] nodeItems = new NodeItem[nodeIds.length];
for (int i = 0; i < nodeIds.length; i++)
{
- nodeItems[i] = asNode( nodeIds[i] );
+ nodeItems[i] = new StubNodeItem( nodeIds[i], empty(), empty() );
}
- return Cursors.cursor( nodeItems);
+ return Cursors.cursor( nodeItems );
}
public static Cursor asNodeCursor( final long nodeId,
final Cursor propertyCursor,
final Cursor labelCursor )
{
- return Cursors.cursor( asNode( nodeId, propertyCursor, labelCursor ) );
+ return Cursors.cursor( new StubNodeItem( nodeId, propertyCursor, labelCursor ) );
}
- public static NodeItem asNode( final long nodeId )
+ private static class StubNodeItem extends EntityItemHelper implements NodeItem
{
- return asNode( nodeId, Cursors.empty(), Cursors.empty() );
- }
+ private final long nodeId;
+ private final Cursor propertyCursor;
+ private final Cursor labelCursor;
- public static NodeItem asNode( final long nodeId,
- final Cursor propertyCursor,
- final Cursor labelCursor )
- {
- return new NodeItemHelper()
+ private StubNodeItem( long nodeId, Cursor propertyCursor, Cursor labelCursor )
{
- @Override
- public long id()
- {
- return nodeId;
- }
+ this.nodeId = nodeId;
+ this.propertyCursor = propertyCursor;
+ this.labelCursor = labelCursor;
+ }
- @Override
- public Cursor label( final int labelId )
+ @Override
+ public long id()
+ {
+ return nodeId;
+ }
+
+ @Override
+ public Cursor label( final int labelId )
+ {
+ return new Cursor()
{
- return new Cursor()
- {
- Cursor cursor = labels();
+ Cursor cursor = labels();
- @Override
- public boolean next()
+ @Override
+ public boolean next()
+ {
+ while ( cursor.next() )
{
- while ( cursor.next() )
+ if ( cursor.get().getAsInt() == labelId )
{
- if ( cursor.get().getAsInt() == labelId )
- {
- return true;
- }
+ return true;
}
-
- return false;
}
- @Override
- public void close()
- {
- cursor.close();
- }
+ return false;
+ }
- @Override
- public LabelItem get()
- {
- return cursor.get();
- }
- };
- }
+ @Override
+ public void close()
+ {
+ cursor.close();
+ }
- @Override
- public Cursor labels()
- {
- return labelCursor;
- }
+ @Override
+ public LabelItem get()
+ {
+ return cursor.get();
+ }
+ };
+ }
- @Override
- public Cursor property( final int propertyKeyId )
+ @Override
+ public boolean hasLabel( int labelId )
+ {
+ return label( labelId ).exists();
+ }
+
+ @Override
+ public Cursor labels()
+ {
+ return labelCursor;
+ }
+
+ @Override
+ public Cursor property( final int propertyKeyId )
+ {
+ return new Cursor()
{
- return new Cursor()
- {
- Cursor cursor = properties();
+ Cursor cursor = properties();
- @Override
- public boolean next()
+ @Override
+ public boolean next()
+ {
+ while ( cursor.next() )
{
- while ( cursor.next() )
+ if ( cursor.get().propertyKeyId() == propertyKeyId )
{
- if ( cursor.get().propertyKeyId() == propertyKeyId )
- {
- return true;
- }
+ return true;
}
-
- return false;
}
- @Override
- public void close()
- {
- cursor.close();
- }
+ return false;
+ }
- @Override
- public PropertyItem get()
- {
- return cursor.get();
- }
- };
- }
+ @Override
+ public void close()
+ {
+ cursor.close();
+ }
- @Override
- public Cursor properties()
- {
- return propertyCursor;
- }
+ @Override
+ public PropertyItem get()
+ {
+ return cursor.get();
+ }
+ };
+ }
- @Override
- public Cursor relationships( Direction direction, int... relTypes )
- {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public Cursor properties()
+ {
+ return propertyCursor;
+ }
- @Override
- public Cursor relationships( Direction direction )
- {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public Cursor relationships( Direction direction, int... relTypes )
+ {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public Cursor relationshipTypes()
- {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public Cursor relationships( Direction direction )
+ {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public int degree( Direction direction )
- {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public Cursor relationshipTypes()
+ {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public int degree( Direction direction, int relType )
- {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public int degree( Direction direction )
+ {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public boolean isDense()
- {
- throw new UnsupportedOperationException( );
- }
+ @Override
+ public int degree( Direction direction, int relType )
+ {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public Cursor degrees()
- {
- throw new UnsupportedOperationException();
- }
- };
+ @Override
+ public boolean isDense()
+ {
+ throw new UnsupportedOperationException( );
+ }
+
+ @Override
+ public Cursor degrees()
+ {
+ throw new UnsupportedOperationException();
+ }
}
public static RelationshipItem asRelationship( final long relId, final int type,
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/DiskLayerLabelTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/DiskLayerLabelTest.java
index 6ebcc66934518..18073ffc8d8fe 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/DiskLayerLabelTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/DiskLayerLabelTest.java
@@ -21,10 +21,14 @@
import org.junit.Test;
+import java.util.Collection;
import java.util.HashSet;
+import java.util.Set;
+import java.util.function.IntSupplier;
+import org.neo4j.collection.primitive.Primitive;
import org.neo4j.collection.primitive.PrimitiveIntCollections;
-import org.neo4j.collection.primitive.PrimitiveIntIterator;
+import org.neo4j.collection.primitive.PrimitiveIntSet;
import org.neo4j.collection.primitive.PrimitiveLongCollections;
import org.neo4j.collection.primitive.PrimitiveLongIterator;
import org.neo4j.cursor.Cursor;
@@ -63,11 +67,11 @@ public void should_be_able_to_list_labels_for_node() throws Exception
}
// THEN
- Cursor node = disk.newStatement().acquireSingleNodeCursor( nodeId );
- node.next();
- PrimitiveIntIterator readLabels = node.get().getLabels();
- assertEquals( new HashSet<>( asList( labelId1, labelId2 ) ),
- PrimitiveIntCollections.addToCollection( readLabels, new HashSet() ) );
+ disk.newStatement().acquireSingleNodeCursor( nodeId ).forAll( node ->
+ {
+ PrimitiveIntSet actual = node.labels().collect( Primitive.intSet(), IntSupplier::getAsInt );
+ assertEquals( PrimitiveIntCollections.asSet( new int[]{labelId1, labelId2} ), actual );
+ } );
}
@Test
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java
index 115d55c13197e..9d41e87272578 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/store/StoreSingleNodeCursorTest.java
@@ -49,6 +49,7 @@
import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord;
import org.neo4j.storageengine.api.DegreeItem;
+import org.neo4j.storageengine.api.RelationshipTypeItem;
import org.neo4j.test.rule.DatabaseRule;
import org.neo4j.test.rule.ImpermanentDatabaseRule;
import org.neo4j.test.rule.RandomRule;
@@ -463,7 +464,7 @@ private Set relTypes( StoreSingleNodeCursor cursor )
{
Set types = new HashSet<>();
- Cursor relTypesCursor = cursor.relationshipTypes();
+ Cursor relTypesCursor = cursor.relationshipTypes();
while ( relTypesCursor.next() )
{
int typeId = relTypesCursor.get().getAsInt();
@@ -619,9 +620,9 @@ private void noNodeChange( long nodeId )
@SuppressWarnings( "unchecked" )
private StoreSingleNodeCursor newCursor( long nodeId )
{
- StoreSingleNodeCursor cursor = new StoreSingleNodeCursor( new NodeRecord( -1 ), resolveNeoStores(),
- mock( StoreStatement.class ), mock( Consumer.class ), new RecordCursors( resolveNeoStores() ),
- NO_LOCK_SERVICE );
+ StoreSingleNodeCursor cursor =
+ new StoreSingleNodeCursor( new NodeRecord( -1 ), resolveNeoStores(), mock( Consumer.class ),
+ new RecordCursors( resolveNeoStores() ), NO_LOCK_SERVICE );
cursor.init( nodeId );
assertTrue( cursor.next() );
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java
index ed878b43a210c..a775f721b4074 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java
@@ -47,6 +47,7 @@
import org.neo4j.test.rule.ConfigurablePageCacheRule;
import org.neo4j.test.rule.fs.EphemeralFileSystemRule;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.helpers.collection.MapUtil.stringMap;
@@ -258,6 +259,60 @@ public void pageCursorErrorsMustNotLingerInRecordCursor() throws Exception
assertTrue( cursor.next( 2, new IntRecord( 2 ), NORMAL ) );
}
+ @Test
+ public void shouldReadTheCorrectRecordWhenGivenAnExplicitIdAndNotUseTheCurrentIdPointer() throws Exception
+ {
+ createStore();
+ IntRecord record42 = new IntRecord( 42 );
+ record42.value = 0x42;
+ store.updateRecord( record42 );
+ IntRecord record43 = new IntRecord( 43 );
+ record43.value = 0x43;
+ store.updateRecord( record43 );
+
+ RecordCursor