diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java index 8c1dab7a0303e..d0d5410e93bbb 100644 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java @@ -265,6 +265,48 @@ long lockingNodeUniqueIndexSeek( IndexReference index, IndexQuery.ExactPredicate Scan relationshipTypeScan( int type ); + /** + * @param nodeReference + * a reference from {@link NodeCursor#nodeReference()}. + * @param reference + * a reference from {@link NodeCursor#relationshipGroupReference()}. + * @param cursor + * the cursor to use for consuming the results. + */ + void relationshipGroups( long nodeReference, long reference, RelationshipGroupCursor cursor ); + + /** + * @param nodeReference + * a reference from {@link NodeCursor#nodeReference()}. + * @param reference + * a reference from {@link RelationshipGroupCursor#outgoingReference()}, + * {@link RelationshipGroupCursor#incomingReference()}, + * or {@link RelationshipGroupCursor#loopsReference()}. + * @param cursor + * the cursor to use for consuming the results. + */ + void relationships( long nodeReference, long reference, RelationshipTraversalCursor cursor ); + + /** + * @param nodeReference + * the owner of the properties. + * @param reference + * a reference from {@link NodeCursor#propertiesReference()}. + * @param cursor + * the cursor to use for consuming the results. + */ + void nodeProperties( long nodeReference, long reference, PropertyCursor cursor ); + + /** + * @param relationshipReference + * the owner of the properties. + * @param reference + * a reference from {@link RelationshipDataAccessor#propertiesReference()}. + * @param cursor + * the cursor to use for consuming the results. + */ + void relationshipProperties( long relationshipReference, long reference, PropertyCursor cursor ); + /** * Checks if a node was deleted in the current transaction * @param node the node to check diff --git a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/PropertyCursorTestBase.java b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/PropertyCursorTestBase.java index fef52f5fb579f..9db1ad8b2c97f 100644 --- a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/PropertyCursorTestBase.java +++ b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/PropertyCursorTestBase.java @@ -175,7 +175,7 @@ public void shouldNotAccessNonExistentProperties() node.properties( props ); assertFalse( "no properties by direct method", props.next() ); - node.properties( props ); + read.nodeProperties( node.nodeReference(), node.propertiesReference(), props ); assertFalse( "no properties via property ref", props.next() ); assertFalse( "only one node", node.next() ); @@ -268,7 +268,7 @@ private void assertAccessSingleProperty( long nodeId, Object expectedValue ) assertEquals( "correct value", expectedValue, props.propertyValue() ); assertFalse( "single property", props.next() ); - node.properties( props ); + read.nodeProperties( node.nodeReference(), node.propertiesReference(), props ); assertTrue( "has properties via property ref", props.next() ); assertEquals( "correct value", expectedValue, props.propertyValue() ); assertFalse( "single property", props.next() ); diff --git a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTransactionStateTestBase.java b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTransactionStateTestBase.java index e40c9deb5d885..cddcc0b3fe122 100644 --- a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTransactionStateTestBase.java +++ b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTransactionStateTestBase.java @@ -307,13 +307,25 @@ public void shouldTraverseDenseNodeWithoutGroupsWithDetachedReferences() throws @Test public void shouldTraverseSparseNodeViaGroups() throws Exception { - traverseViaGroups( sparse( graphDb ) ); + traverseViaGroups( sparse( graphDb ), false ); } @Test public void shouldTraverseDenseNodeViaGroups() throws Exception { - traverseViaGroups( RelationshipTestSupport.dense( graphDb ) ); + traverseViaGroups( RelationshipTestSupport.dense( graphDb ), false ); + } + + @Test + public void shouldTraverseSparseNodeViaGroupsWithDetachedReferences() throws Exception + { + traverseViaGroups( sparse( graphDb ), true ); + } + + @Test + public void shouldTraverseDenseNodeViaGroupsWithDetachedReferences() throws Exception + { + traverseViaGroups( RelationshipTestSupport.dense( graphDb ), true ); } @Test @@ -1130,7 +1142,7 @@ private void traverseWithoutGroups( RelationshipTestSupport.StartNode start, boo assertTrue( "access node", node.next() ); if ( detached ) { - node.allRelationships( relationship ); + tx.dataRead().relationships( start.id, node.allRelationshipsReference(), relationship ); } else { @@ -1147,7 +1159,7 @@ private void traverseWithoutGroups( RelationshipTestSupport.StartNode start, boo } } - private void traverseViaGroups( RelationshipTestSupport.StartNode start ) throws Exception + private void traverseViaGroups( RelationshipTestSupport.StartNode start, boolean detached ) throws Exception { try ( Transaction tx = beginTransaction() ) { @@ -1162,25 +1174,55 @@ private void traverseViaGroups( RelationshipTestSupport.StartNode start ) throws // when read.singleNode( start.id, node ); assertTrue( "access node", node.next() ); - node.relationships( group ); + if ( detached ) + { + read.relationshipGroups( start.id, node.relationshipGroupReference(), group ); + } + else + { + node.relationships( group ); + } while ( group.next() ) { - int type = group.type(); // outgoing - group.outgoing( relationship ); + if ( detached ) + { + read.relationships( start.id, group.outgoingReference(), relationship ); + } + else + { + group.outgoing( relationship ); + } // then - RelationshipTestSupport.assertCount( tx, relationship, expectedCounts, type, OUTGOING ); + RelationshipTestSupport + .assertCount( tx, relationship, expectedCounts, group.type(), OUTGOING ); // incoming - group.incoming( relationship ); + if ( detached ) + { + read.relationships( start.id, group.incomingReference(), relationship ); + } + else + { + group.incoming( relationship ); + } // then - RelationshipTestSupport.assertCount( tx, relationship, expectedCounts, type, INCOMING ); + RelationshipTestSupport + .assertCount( tx, relationship, expectedCounts, group.type(), INCOMING ); // loops - group.loops( relationship ); + if ( detached ) + { + read.relationships( start.id, group.loopsReference(), relationship ); + } + else + { + group.loops( relationship ); + } // then - RelationshipTestSupport.assertCount( tx, relationship, expectedCounts, type, BOTH ); + RelationshipTestSupport + .assertCount( tx, relationship, expectedCounts, group.type(), BOTH ); } } } @@ -1247,16 +1289,15 @@ public void hasPropertiesShouldSeeNewlyCreatedProperties() throws Exception // Then try ( Transaction tx = beginTransaction() ) { - try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor(); - PropertyCursor props = tx.cursors().allocatePropertyCursor() ) + try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor() ) { tx.dataRead().singleRelationship( relationship, cursor ); assertTrue( cursor.next() ); - assertFalse( hasProperties( cursor, props ) ); + assertFalse( hasProperties( cursor, tx ) ); tx.dataWrite().relationshipSetProperty( relationship, tx.tokenWrite().propertyKeyGetOrCreateForName( "prop" ), stringValue( "foo" ) ); - assertTrue( hasProperties( cursor, props ) ); + assertTrue( hasProperties( cursor, tx ) ); } } } @@ -1269,16 +1310,15 @@ public void hasPropertiesShouldSeeNewlyCreatedPropertiesOnNewlyCreatedRelationsh Write write = tx.dataWrite(); int token = tx.tokenWrite().relationshipTypeGetOrCreateForName( "R" ); long relationship = write.relationshipCreate( write.nodeCreate(), token, write.nodeCreate() ); - try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor(); - PropertyCursor props = tx.cursors().allocatePropertyCursor() ) + try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor() ) { tx.dataRead().singleRelationship( relationship, cursor ); assertTrue( cursor.next() ); - assertFalse( hasProperties( cursor, props ) ); + assertFalse( hasProperties( cursor, tx ) ); tx.dataWrite().relationshipSetProperty( relationship, tx.tokenWrite().propertyKeyGetOrCreateForName( "prop" ), stringValue( "foo" ) ); - assertTrue( hasProperties( cursor, props ) ); + assertTrue( hasProperties( cursor, tx ) ); } } } @@ -1306,19 +1346,18 @@ public void hasPropertiesShouldSeeNewlyRemovedProperties() throws Exception // Then try ( Transaction tx = beginTransaction() ) { - try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor(); - PropertyCursor props = tx.cursors().allocatePropertyCursor() ) + try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor() ) { tx.dataRead().singleRelationship( relationship, cursor ); assertTrue( cursor.next() ); - assertTrue( hasProperties( cursor, props ) ); + assertTrue( hasProperties( cursor, tx ) ); tx.dataWrite().relationshipRemoveProperty( relationship, prop1 ); - assertTrue( hasProperties( cursor, props ) ); + assertTrue( hasProperties( cursor, tx ) ); tx.dataWrite().relationshipRemoveProperty( relationship, prop2 ); - assertTrue( hasProperties( cursor, props ) ); + assertTrue( hasProperties( cursor, tx ) ); tx.dataWrite().relationshipRemoveProperty( relationship, prop3 ); - assertFalse( hasProperties( cursor, props ) ); + assertFalse( hasProperties( cursor, tx ) ); } } } @@ -1344,7 +1383,7 @@ public void propertyTypeShouldBeTxStateAware() throws Exception { tx.dataRead().singleRelationship( relationship, relationships ); assertTrue( relationships.next() ); - assertFalse( hasProperties( relationships, properties ) ); + assertFalse( hasProperties( relationships, tx ) ); int prop = tx.tokenWrite().propertyKeyGetOrCreateForName( "prop" ); tx.dataWrite().relationshipSetProperty( relationship, prop, stringValue( "foo" ) ); relationships.properties( properties ); @@ -1355,6 +1394,15 @@ public void propertyTypeShouldBeTxStateAware() throws Exception } } + private boolean hasProperties( RelationshipScanCursor cursor, Transaction tx ) + { + try ( PropertyCursor propertyCursor = tx.cursors().allocatePropertyCursor() ) + { + cursor.properties( propertyCursor ); + return propertyCursor.next(); + } + } + private void relateNTimes( int nRelationshipsInStore, int type, long n1, long n2, Transaction tx ) throws KernelException { @@ -1364,12 +1412,6 @@ private void relateNTimes( int nRelationshipsInStore, int type, long n1, long n2 } } - private boolean hasProperties( RelationshipScanCursor cursor, PropertyCursor props ) - { - cursor.properties( props ); - return props.next(); - } - private void assertCountRelationships( RelationshipScanCursor relationship, int expectedCount, long sourceNode, int type, long targetNode ) { diff --git a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java index 3c82e6378f507..872cb60c8cbeb 100644 --- a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java +++ b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/RelationshipTraversalCursorTestBase.java @@ -250,30 +250,56 @@ public void shouldHaveBeenAbleToCreateDenseAndSparseNodes() @Test public void shouldTraverseSparseNodeViaGroups() throws Exception { - traverseViaGroups( sparse ); + traverseViaGroups( sparse, false ); } @Test public void shouldTraverseDenseNodeViaGroups() throws Exception { - traverseViaGroups( dense ); + traverseViaGroups( dense, false ); + } + + @Test + public void shouldTraverseSparseNodeViaGroupsWithDetachedReferences() throws Exception + { + traverseViaGroups( sparse, true ); + } + + @Test + public void shouldTraverseDenseNodeViaGroupsWithDetachedReferences() throws Exception + { + traverseViaGroups( dense, true ); } @Test public void shouldTraverseSparseNodeWithoutGroups() throws Exception { Assume.assumeTrue( supportsSparseNodes() && supportsDirectTraversal() ); - traverseWithoutGroups( sparse ); + traverseWithoutGroups( sparse, false ); } @Test public void shouldTraverseDenseNodeWithoutGroups() throws Exception { Assume.assumeTrue( supportsDirectTraversal() ); - traverseWithoutGroups( dense ); + traverseWithoutGroups( dense, false ); + } + + @Test + public void shouldTraverseSparseNodeWithoutGroupsWithDetachedReferences() throws Exception + { + Assume.assumeTrue( supportsSparseNodes() ); + traverseWithoutGroups( sparse, true ); + } + + @Test + public void shouldTraverseDenseNodeWithoutGroupsWithDetachedReferences() throws Exception + { + Assume.assumeTrue( supportsDirectTraversal() ); + traverseWithoutGroups( dense, true ); } - private void traverseViaGroups( RelationshipTestSupport.StartNode start ) throws KernelException + private void traverseViaGroups( RelationshipTestSupport.StartNode start, boolean detached ) throws KernelException { // given Map expectedCounts = start.expectedCounts(); @@ -285,30 +311,57 @@ private void traverseViaGroups( RelationshipTestSupport.StartNode start ) throws // when read.singleNode( start.id, node ); assertTrue( "access node", node.next() ); - node.relationships( group ); + if ( detached ) + { + read.relationshipGroups( start.id, node.relationshipGroupReference(), group ); + } + else + { + node.relationships( group ); + } while ( group.next() ) { - int type = group.type(); // outgoing - group.outgoing( relationship ); + if ( detached ) + { + read.relationships( start.id, group.outgoingReference(), relationship ); + } + else + { + group.outgoing( relationship ); + } // then assertCount( tx, relationship, expectedCounts, group.type(), OUTGOING ); // incoming - group.incoming( relationship ); + if ( detached ) + { + read.relationships( start.id, group.incomingReference(), relationship ); + } + else + { + group.incoming( relationship ); + } // then assertCount( tx, relationship, expectedCounts, group.type(), INCOMING ); // loops - group.loops( relationship ); + if ( detached ) + { + read.relationships( start.id, group.loopsReference(), relationship ); + } + else + { + group.loops( relationship ); + } // then assertCount( tx, relationship, expectedCounts, group.type(), BOTH ); } } } - private void traverseWithoutGroups( RelationshipTestSupport.StartNode start ) + private void traverseWithoutGroups( RelationshipTestSupport.StartNode start, boolean detached ) throws KernelException { // given @@ -318,7 +371,15 @@ private void traverseWithoutGroups( RelationshipTestSupport.StartNode start ) // when read.singleNode( start.id, node ); assertTrue( "access node", node.next() ); - node.allRelationships( relationship ); + + if ( detached ) + { + read.relationships( start.id, node.allRelationshipsReference(), relationship ); + } + else + { + node.allRelationships( relationship ); + } Map counts = count( tx, relationship ); diff --git a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java index c641aa4ed41a6..ff25e2d82e0dc 100644 --- a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java +++ b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java @@ -27,6 +27,7 @@ import org.neo4j.internal.kernel.api.NodeValueIndexCursor; import org.neo4j.internal.kernel.api.PropertyCursor; import org.neo4j.internal.kernel.api.Read; +import org.neo4j.internal.kernel.api.RelationshipGroupCursor; import org.neo4j.internal.kernel.api.RelationshipScanCursor; import org.neo4j.internal.kernel.api.RelationshipTraversalCursor; import org.neo4j.internal.kernel.api.Scan; @@ -168,12 +169,36 @@ public void relationshipTypeScan( int type, RelationshipScanCursor cursor ) throw new UnsupportedOperationException(); } + @Override + public void relationships( long nodeReference, long reference, RelationshipTraversalCursor cursor ) + { + throw new UnsupportedOperationException(); + } + @Override public Scan relationshipTypeScan( int type ) { throw new UnsupportedOperationException(); } + @Override + public void nodeProperties( long nodeReference, long reference, PropertyCursor cursor ) + { + throw new UnsupportedOperationException(); + } + + @Override + public void relationshipProperties( long relationshipReference, long reference, PropertyCursor cursor ) + { + throw new UnsupportedOperationException(); + } + + @Override + public void relationshipGroups( long nodeReference, long reference, RelationshipGroupCursor cursor ) + { + throw new UnsupportedOperationException(); + } + @Override public boolean nodeDeletedInTransaction( long node ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/NodeStateImpl.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/NodeStateImpl.java index ef1e54eba4a1a..3ae94b681908f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/NodeStateImpl.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/NodeStateImpl.java @@ -68,6 +68,12 @@ public Iterator addedAndChangedProperties() return emptyIterator(); } + @Override + public boolean hasPropertyChanges() + { + return false; + } + @Override public LongDiffSets labelDiffSets() { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/PropertyContainerStateImpl.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/PropertyContainerStateImpl.java index c3669515037e0..d097e62989d23 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/PropertyContainerStateImpl.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/PropertyContainerStateImpl.java @@ -156,6 +156,12 @@ public Iterator addedAndChangedProperties() return Iterators.concat( toPropertyIterator( addedProperties ), toPropertyIterator( changedProperties ) ); } + @Override + public boolean hasPropertyChanges() + { + return addedProperties != null || removedProperties != null || changedProperties != null; + } + @Override public boolean isPropertyChangedOrRemoved( int propertyKey ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/RelationshipStateImpl.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/RelationshipStateImpl.java index 830710bdc5ca0..1b820d62ee9ab 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/RelationshipStateImpl.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/RelationshipStateImpl.java @@ -70,6 +70,12 @@ public Iterator addedAndChangedProperties() return emptyIterator(); } + @Override + public boolean hasPropertyChanges() + { + return false; + } + @Override public boolean isPropertyChangedOrRemoved( int propertyKey ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeCursor.java index fb2a12450eb21..779a8554cb0ef 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeCursor.java @@ -35,9 +35,7 @@ import org.neo4j.kernel.api.txstate.TransactionState; import org.neo4j.storageengine.api.StorageNodeCursor; import org.neo4j.storageengine.api.txstate.LongDiffSets; -import org.neo4j.storageengine.api.txstate.NodeState; -import static org.neo4j.internal.kernel.api.Read.ANY_RELATIONSHIP_TYPE; import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; class DefaultNodeCursor implements NodeCursor @@ -142,7 +140,7 @@ public void relationships( RelationshipGroupCursor cursor ) @Override public void allRelationships( RelationshipTraversalCursor cursor ) { - ((DefaultRelationshipTraversalCursor) cursor).init( nodeReference(), allRelationshipsReference(), read, null, ANY_RELATIONSHIP_TYPE ); + ((DefaultRelationshipTraversalCursor) cursor).init( nodeReference(), allRelationshipsReference(), read ); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipGroupCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipGroupCursor.java index e6faca04da834..f74db874a49f8 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipGroupCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipGroupCursor.java @@ -31,6 +31,9 @@ import org.neo4j.storageengine.api.txstate.NodeState; import org.neo4j.storageengine.api.txstate.RelationshipState; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.encodeNoIncomingRels; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.encodeNoLoopRels; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.encodeNoOutgoingRels; import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; class DefaultRelationshipGroupCursor implements RelationshipGroupCursor @@ -188,37 +191,40 @@ public int loopCount() @Override public void outgoing( RelationshipTraversalCursor cursor ) { - ((DefaultRelationshipTraversalCursor) cursor).init( storeCursor.getOwningNode(), outgoingReference(), read, RelationshipDirection.OUTGOING, type() ); + ((DefaultRelationshipTraversalCursor) cursor).init( storeCursor.getOwningNode(), outgoingReference(), read ); } @Override public void incoming( RelationshipTraversalCursor cursor ) { - ((DefaultRelationshipTraversalCursor) cursor).init( storeCursor.getOwningNode(), incomingReference(), read, RelationshipDirection.INCOMING, type() ); + ((DefaultRelationshipTraversalCursor) cursor).init( storeCursor.getOwningNode(), incomingReference(), read ); } @Override public void loops( RelationshipTraversalCursor cursor ) { - ((DefaultRelationshipTraversalCursor) cursor).init( storeCursor.getOwningNode(), loopsReference(), read, RelationshipDirection.LOOP, type() ); + ((DefaultRelationshipTraversalCursor) cursor).init( storeCursor.getOwningNode(), loopsReference(), read ); } @Override public long outgoingReference() { - return storeCursor.outgoingReference(); + long reference = storeCursor.outgoingReference(); + return reference == NO_ID ? encodeNoOutgoingRels( storeCursor.type() ) : reference; } @Override public long incomingReference() { - return storeCursor.incomingReference(); + long reference = storeCursor.incomingReference(); + return reference == NO_ID ? encodeNoIncomingRels( storeCursor.type() ) : reference; } @Override public long loopsReference() { - return storeCursor.loopsReference(); + long reference = storeCursor.loopsReference(); + return reference == NO_ID ? encodeNoLoopRels( storeCursor.type() ) : reference; } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipTraversalCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipTraversalCursor.java index 0a6791c0008f8..2fd8dd6662795 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipTraversalCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipTraversalCursor.java @@ -32,6 +32,10 @@ import static java.lang.String.format; import static org.neo4j.internal.kernel.api.Read.ANY_RELATIONSHIP_TYPE; +import static org.neo4j.kernel.impl.newapi.References.clearEncoding; +import static org.neo4j.kernel.impl.newapi.RelationshipDirection.INCOMING; +import static org.neo4j.kernel.impl.newapi.RelationshipDirection.LOOP; +import static org.neo4j.kernel.impl.newapi.RelationshipDirection.OUTGOING; import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; class DefaultRelationshipTraversalCursor extends DefaultRelationshipCursor @@ -123,14 +127,89 @@ private static FilterState fromRelationshipDirection( RelationshipDirection dire super( pool, storeCursor ); } - void init( long nodeReference, long reference, Read read, RelationshipDirection direction, int type ) + void init( long nodeReference, long reference, Read read ) { - storeCursor.init( nodeReference, reference, direction, type ); - this.filterState = direction == null ? FilterState.NONE : FilterState.fromRelationshipDirection( direction ); - this.filterType = type; + /* There are 5 different ways a relationship traversal cursor can be initialized: + * + * 1. From a batched group in a detached way. This happens when the user manually retrieves the relationships + * references from the group cursor and passes it to this method and if the group cursor was based on having + * batched all the different types in the single (mixed) chain of relationships. + * In this case we should pass a reference marked with some flag to the first relationship in the chain that + * has the type of the current group in the group cursor. The traversal cursor then needs to read the type + * from that first record and use that type as a filter for when reading the rest of the chain. + * - NOTE: we probably have to do the same sort of filtering for direction - so we need a flag for that too. + * + * 2. From a batched group in a DIRECT way. This happens when the traversal cursor is initialized directly from + * the group cursor, in this case we can simply initialize the traversal cursor with the buffered state from + * the group cursor, so this method here does not have to be involved, and things become pretty simple. + * + * 3. Traversing all relationships - regardless of type - of a node that has grouped relationships. In this case + * the traversal cursor needs to traverse through the group records in order to get to the actual + * relationships. The initialization of the cursor (through this here method) should be with a FLAGGED + * reference to the (first) group record. + * + * 4. Traversing a single chain - this is what happens in the cases when + * a) Traversing all relationships of a node without grouped relationships. + * b) Traversing the relationships of a particular group of a node with grouped relationships. + * + * 5. There are no relationships - i.e. passing in NO_ID to this method. + * + * This means that we need reference encodings (flags) for cases: 1, 3, 4, 5 + */ + + RelationshipReferenceEncoding encoding = RelationshipReferenceEncoding.parseEncoding( reference ); + + switch ( encoding ) + { + case NONE: + case GROUP: + storeCursor.init( nodeReference, reference ); + initFiltering( FilterState.NONE, false ); + break; + + case FILTER_TX_STATE: + // The relationships in tx-state needs to be filtered according to the first relationship we discover, + // but let's have the store cursor bother with this detail. + storeCursor.init( nodeReference, clearEncoding( reference ) ); + initFiltering( FilterState.NOT_INITIALIZED, false ); + break; + + case FILTER: + // The relationships needs to be filtered according to the first relationship we discover + storeCursor.init( nodeReference, clearEncoding( reference ) ); + initFiltering( FilterState.NOT_INITIALIZED, true ); + break; + + case NO_OUTGOING_OF_TYPE: // nothing in store, but proceed to check tx-state changes + storeCursor.init( nodeReference, NO_ID ); + initFiltering( FilterState.fromRelationshipDirection( OUTGOING ), false ); + this.filterType = (int) clearEncoding( reference ); + break; + + case NO_INCOMING_OF_TYPE: // nothing in store, but proceed to check tx-state changes + storeCursor.init( nodeReference, NO_ID ); + initFiltering( FilterState.fromRelationshipDirection( INCOMING ), false ); + this.filterType = (int) clearEncoding( reference ); + break; + + case NO_LOOP_OF_TYPE: // nothing in store, but proceed to check tx-state changes + storeCursor.init( nodeReference, NO_ID ); + initFiltering( FilterState.fromRelationshipDirection( LOOP ), false ); + this.filterType = (int) clearEncoding( reference ); + break; + + default: + throw new IllegalStateException( "Unknown encoding " + encoding ); + } + init( read ); this.addedRelationships = ImmutableEmptyLongIterator.INSTANCE; - this.filterStore = true; + } + + private void initFiltering( FilterState filterState, boolean filterStore ) + { + this.filterState = filterState; + this.filterStore = filterStore; } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java index 8228c15041f28..68e697ee64f2a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java @@ -30,7 +30,9 @@ import org.neo4j.internal.kernel.api.NodeValueIndexCursor; import org.neo4j.internal.kernel.api.PropertyCursor; import org.neo4j.internal.kernel.api.RelationshipExplicitIndexCursor; +import org.neo4j.internal.kernel.api.RelationshipGroupCursor; import org.neo4j.internal.kernel.api.RelationshipScanCursor; +import org.neo4j.internal.kernel.api.RelationshipTraversalCursor; import org.neo4j.internal.kernel.api.Scan; import org.neo4j.internal.kernel.api.exceptions.KernelException; import org.neo4j.internal.kernel.api.exceptions.explicitindex.ExplicitIndexNotFoundKernelException; @@ -318,6 +320,30 @@ public final Scan relationshipTypeScan( int type ) throw new UnsupportedOperationException( "not implemented" ); } + @Override + public void relationshipGroups( long nodeReference, long reference, RelationshipGroupCursor cursor ) + { + ((DefaultRelationshipGroupCursor) cursor).init( nodeReference, reference, this ); + } + + @Override + public void relationships( long nodeReference, long reference, RelationshipTraversalCursor cursor ) + { + ((DefaultRelationshipTraversalCursor) cursor).init( nodeReference, reference, this ); + } + + @Override + public void nodeProperties( long nodeReference, long reference, PropertyCursor cursor ) + { + ((DefaultPropertyCursor) cursor).initNode( nodeReference, reference, this, ktx ); + } + + @Override + public void relationshipProperties( long relationshipReference, long reference, PropertyCursor cursor ) + { + ((DefaultPropertyCursor) cursor).initRelationship( relationshipReference, reference, this, ktx ); + } + @Override public final void graphProperties( PropertyCursor cursor ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/References.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/References.java similarity index 88% rename from community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/References.java rename to community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/References.java index 85d7238112f24..7f72d90958e12 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/References.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/References.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.kernel.impl.storageengine.impl.recordstorage; +package org.neo4j.kernel.impl.newapi; import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; @@ -41,11 +41,11 @@ * the reference, the encoding must be cleared with {@link References#clearEncoding(long)}. To guard against using an * encoded reference, all encoded references are marked so they appear negative. */ -class References +public class References { - static final long FLAG_MARKER = 0x8000_0000_0000_0000L; - static final long FLAG_MASK = 0x7000_0000_0000_0000L; - static final long FLAGS = 0xF000_0000_0000_0000L; + public static final long FLAG_MARKER = 0x8000_0000_0000_0000L; + public static final long FLAG_MASK = 0x7000_0000_0000_0000L; + public static final long FLAGS = 0xF000_0000_0000_0000L; /** * Clear all encoding from a reference. @@ -53,7 +53,7 @@ class References * @param reference The reference to clear. * @return The cleared reference. */ - static long clearEncoding( long reference ) + public static long clearEncoding( long reference ) { assert reference != NO_ID; return reference & ~FLAGS; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipReferenceEncoding.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipReferenceEncoding.java new file mode 100644 index 0000000000000..48637af7740dd --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipReferenceEncoding.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.newapi; + +import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; + +public enum RelationshipReferenceEncoding +{ + /** No encoding */ + NONE( 0 ), + + /** @see #encodeForFiltering(long) */ + FILTER( 1 ), + + /** @see #encodeForTxStateFiltering(long) */ + FILTER_TX_STATE( 2 ), + + /** @see #encodeGroup(long) */ + GROUP( 3 ), + + /** @see #encodeNoOutgoingRels(int) */ + NO_OUTGOING_OF_TYPE( 4 ), + + /** @see #encodeNoIncomingRels(int) */ + NO_INCOMING_OF_TYPE( 5 ), + + /** @see #encodeNoLoopRels(int) */ + NO_LOOP_OF_TYPE( 6 ); + + private static final RelationshipReferenceEncoding[] ENCODINGS = RelationshipReferenceEncoding.values(); + final long id; + final long bits; + + RelationshipReferenceEncoding( long id ) + { + this.id = id; + this.bits = id << 60; + } + + public static RelationshipReferenceEncoding parseEncoding( long reference ) + { + if ( reference == NO_ID ) + { + return NONE; + } + return ENCODINGS[encodingId( reference )]; + } + + private static int encodingId( long reference ) + { + return (int)((reference & References.FLAG_MASK) >> 60); + } + + /** + * Encode a group id as a relationship reference. + */ + public static long encodeGroup( long groupId ) + { + return groupId | GROUP.bits | References.FLAG_MARKER; + } + + /** + * Encode that the relationship id needs filtering by it's first element. + */ + public static long encodeForFiltering( long relationshipId ) + { + return relationshipId | FILTER.bits | References.FLAG_MARKER; + } + + /** + * Encode that the relationship id needs filtering by it's first element. + */ + public static long encodeForTxStateFiltering( long relationshipId ) + { + return relationshipId | FILTER_TX_STATE.bits | References.FLAG_MARKER; + } + + /** + * Encode that no outgoing relationships of the encoded type exist. + */ + public static long encodeNoOutgoingRels( int type ) + { + return type | NO_OUTGOING_OF_TYPE.bits | References.FLAG_MARKER; + } + + /** + * Encode that no incoming relationships of the encoded type exist. + */ + public static long encodeNoIncomingRels( int type ) + { + return type | NO_INCOMING_OF_TYPE.bits | References.FLAG_MARKER; + } + + /** + * Encode that no loop relationships of the encoded type exist. + */ + public static long encodeNoLoopRels( int type ) + { + return type | NO_LOOP_OF_TYPE.bits | References.FLAG_MARKER; + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncoding.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncoding.java index 10fd5cff13b8c..2d84acc3d4275 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncoding.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncoding.java @@ -19,6 +19,8 @@ */ package org.neo4j.kernel.impl.storageengine.impl.recordstorage; +import org.neo4j.kernel.impl.newapi.References; + import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; class GroupReferenceEncoding diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordNodeCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordNodeCursor.java index 88852d591e620..a5dec2f78d3e9 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordNodeCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordNodeCursor.java @@ -20,6 +20,7 @@ package org.neo4j.kernel.impl.storageengine.impl.recordstorage; import org.neo4j.io.pagecache.PageCursor; +import org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding; import org.neo4j.kernel.impl.store.NodeLabelsField; import org.neo4j.kernel.impl.store.NodeStore; import org.neo4j.kernel.impl.store.RecordCursor; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipGroupCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipGroupCursor.java index 0820ca8e278b2..830c4d4e83b80 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipGroupCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipGroupCursor.java @@ -31,8 +31,10 @@ import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.storageengine.api.StorageRelationshipGroupCursor; +import static org.neo4j.kernel.impl.newapi.References.clearEncoding; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.encodeForFiltering; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.encodeForTxStateFiltering; import static org.neo4j.kernel.impl.storageengine.impl.recordstorage.GroupReferenceEncoding.isRelationship; -import static org.neo4j.kernel.impl.storageengine.impl.recordstorage.References.clearEncoding; class RecordRelationshipGroupCursor extends RelationshipGroupRecord implements StorageRelationshipGroupCursor { @@ -235,19 +237,22 @@ private int count( long reference ) @Override public long outgoingReference() { - return getFirstOut(); + long outgoing = getFirstOut(); + return outgoing == NO_ID ? NO_ID : encodeRelationshipReference( outgoing ); } @Override public long incomingReference() { - return getFirstIn(); + long incoming = getFirstIn(); + return incoming == NO_ID ? NO_ID : encodeRelationshipReference( incoming ); } @Override public long loopsReference() { - return getFirstLoop(); + long loops = getFirstLoop(); + return loops == NO_ID ? NO_ID : encodeRelationshipReference( loops ); } @Override @@ -296,6 +301,12 @@ long loopsRawId() return getFirstLoop(); } + private long encodeRelationshipReference( long relationshipId ) + { + assert relationshipId != NO_ID; + return isBuffered() ? encodeForFiltering( relationshipId ) : encodeForTxStateFiltering( relationshipId ); + } + private boolean isBuffered() { return bufferedGroup != null; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursor.java index 8cd8708e66ef8..ecbac33493ff9 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursor.java @@ -20,13 +20,13 @@ package org.neo4j.kernel.impl.storageengine.impl.recordstorage; import org.neo4j.io.pagecache.PageCursor; -import org.neo4j.kernel.impl.newapi.RelationshipDirection; +import org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding; import org.neo4j.kernel.impl.store.RelationshipGroupStore; import org.neo4j.kernel.impl.store.RelationshipStore; import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor; -import static org.neo4j.kernel.impl.storageengine.impl.recordstorage.References.clearEncoding; +import static org.neo4j.kernel.impl.newapi.References.clearEncoding; class RecordRelationshipTraversalCursor extends RecordRelationshipCursor implements StorageRelationshipTraversalCursor { @@ -53,7 +53,7 @@ private enum GroupState } @Override - public void init( long nodeReference, long reference, RelationshipDirection filterDirection, int filterType ) + public void init( long nodeReference, long reference ) { /* There are basically two ways a relationship traversal cursor can be initialized: * diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RelationshipReferenceEncoding.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RelationshipReferenceEncoding.java deleted file mode 100644 index 1e5c9c29112a2..0000000000000 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RelationshipReferenceEncoding.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2002-2018 "Neo4j," - * Neo4j Sweden AB [http://neo4j.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.storageengine.impl.recordstorage; - -import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; - -enum RelationshipReferenceEncoding -{ - /** No encoding */ - NONE( 0 ), - - /** @see #encodeGroup(long) */ - GROUP( 1 ); - - private static final RelationshipReferenceEncoding[] ENCODINGS = RelationshipReferenceEncoding.values(); - - final long id; - final long bits; - - RelationshipReferenceEncoding( long id ) - { - this.id = id; - this.bits = id << 60; - } - - static RelationshipReferenceEncoding parseEncoding( long reference ) - { - if ( reference == NO_ID ) - { - return NONE; - } - return ENCODINGS[encodingId( reference )]; - } - - private static int encodingId( long reference ) - { - return (int)((reference & References.FLAG_MASK) >> 60); - } - - /** - * Encode a group id as a relationship reference. - */ - static long encodeGroup( long groupId ) - { - return groupId | GROUP.bits | References.FLAG_MARKER; - } -} diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageRelationshipTraversalCursor.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageRelationshipTraversalCursor.java index 4d15edc7a1770..69853469f6387 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageRelationshipTraversalCursor.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageRelationshipTraversalCursor.java @@ -19,13 +19,11 @@ */ package org.neo4j.storageengine.api; -import org.neo4j.kernel.impl.newapi.RelationshipDirection; - public interface StorageRelationshipTraversalCursor extends StorageRelationshipCursor { long neighbourNodeReference(); long originNodeReference(); - void init( long nodeReference, long reference, RelationshipDirection filterDirection, int filterType ); + void init( long nodeReference, long reference ); } diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PropertyContainerState.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PropertyContainerState.java index 470753e921199..0d15b524f13b5 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PropertyContainerState.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/txstate/PropertyContainerState.java @@ -46,6 +46,8 @@ public interface PropertyContainerState Iterator addedAndChangedProperties(); + boolean hasPropertyChanges(); + boolean isPropertyChangedOrRemoved( int propertyKey ); PropertyContainerState EMPTY = new EmptyPropertyContainerState(); @@ -76,6 +78,12 @@ public Iterator addedAndChangedProperties() return emptyIterator(); } + @Override + public boolean hasPropertyChanges() + { + return false; + } + @Override public boolean isPropertyChangedOrRemoved( int propertyKey ) { diff --git a/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchRelationshipIterable.java b/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchRelationshipIterable.java index e7698ae9a6ddc..073b0692de9a6 100644 --- a/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchRelationshipIterable.java +++ b/community/kernel/src/main/java/org/neo4j/unsafe/batchinsert/internal/BatchRelationshipIterable.java @@ -28,8 +28,6 @@ import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor; -import static org.neo4j.internal.kernel.api.Read.ANY_RELATIONSHIP_TYPE; - abstract class BatchRelationshipIterable implements Iterable { private final StorageRelationshipTraversalCursor relationshipCursor; @@ -43,7 +41,7 @@ abstract class BatchRelationshipIterable implements Iterable { throw new NotFoundException( "Node " + nodeId + " not found" ); } - relationshipCursor.init( nodeId, nodeCursor.allRelationshipsReference(), null, ANY_RELATIONSHIP_TYPE ); + relationshipCursor.init( nodeId, nodeCursor.allRelationshipsReference() ); } @Override diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncodingTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncodingTest.java new file mode 100644 index 0000000000000..fdf57894e465a --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncodingTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.storageengine.impl.recordstorage; + +import org.junit.Test; + +import java.util.concurrent.ThreadLocalRandom; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GroupReferenceEncodingTest +{ + // This value the largest possible high limit id +1 (see HighLimitV3_1_0) + private static long MAX_ID_LIMIT = 1L << 50; + + @Test + public void encodeRelationship() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + long reference = random.nextLong( MAX_ID_LIMIT ); + assertFalse( GroupReferenceEncoding.isRelationship( reference ) ); + assertTrue( GroupReferenceEncoding.isRelationship( GroupReferenceEncoding.encodeRelationship( reference ) ) ); + assertTrue( "encoded reference is negative", GroupReferenceEncoding.encodeRelationship( reference ) < 0 ); + } + } +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursorTest.java index 61d9408e6cc6e..130f23f702f29 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RecordRelationshipTraversalCursorTest.java @@ -50,7 +50,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.pagecache_memory; -import static org.neo4j.internal.kernel.api.Read.ANY_RELATIONSHIP_TYPE; import static org.neo4j.io.pagecache.tracing.cursor.context.EmptyVersionContextSupplier.EMPTY; import static org.neo4j.kernel.impl.newapi.RelationshipDirection.INCOMING; import static org.neo4j.kernel.impl.newapi.RelationshipDirection.LOOP; @@ -113,13 +112,13 @@ public void retrieveNodeRelationships() try ( RecordRelationshipTraversalCursor cursor = getNodeRelationshipCursor() ) { - cursor.init( FIRST_OWNING_NODE, 1L, direction, ANY_RELATIONSHIP_TYPE ); + cursor.init( FIRST_OWNING_NODE, 1L ); assertTrue( cursor.next() ); - cursor.init( FIRST_OWNING_NODE, 2, direction, ANY_RELATIONSHIP_TYPE ); + cursor.init( FIRST_OWNING_NODE, 2 ); assertTrue( cursor.next() ); - cursor.init( FIRST_OWNING_NODE, 3, direction, ANY_RELATIONSHIP_TYPE ); + cursor.init( FIRST_OWNING_NODE, 3 ); assertTrue( cursor.next() ); } } @@ -131,7 +130,7 @@ public void retrieveUsedRelationshipChain() long expectedNodeId = 1; try ( RecordRelationshipTraversalCursor cursor = getNodeRelationshipCursor() ) { - cursor.init( FIRST_OWNING_NODE, 1, direction, ANY_RELATIONSHIP_TYPE ); + cursor.init( FIRST_OWNING_NODE, 1 ); while ( cursor.next() ) { assertEquals( "Should load next relationship in a sequence", expectedNodeId++, cursor.relationshipReference() ); @@ -149,7 +148,7 @@ public void retrieveRelationshipChainWithUnusedLink() int relationshipIndex = 0; try ( RecordRelationshipTraversalCursor cursor = getNodeRelationshipCursor() ) { - cursor.init( FIRST_OWNING_NODE, 1, direction, ANY_RELATIONSHIP_TYPE ); + cursor.init( FIRST_OWNING_NODE, 1 ); while ( cursor.next() ) { assertEquals( "Should load next relationship in a sequence", @@ -168,7 +167,7 @@ public void shouldHandleDenseNodeWithNoRelationships() try ( RecordRelationshipTraversalCursor cursor = getNodeRelationshipCursor() ) { // WHEN - cursor.init( FIRST_OWNING_NODE, NO_NEXT_RELATIONSHIP.intValue(), direction, ANY_RELATIONSHIP_TYPE ); + cursor.init( FIRST_OWNING_NODE, NO_NEXT_RELATIONSHIP.intValue() ); // THEN assertFalse( cursor.next() ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/ReferencesTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/ReferencesTest.java index cef1c1eedc363..bd8151c566114 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/ReferencesTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/ReferencesTest.java @@ -23,15 +23,21 @@ import java.util.concurrent.ThreadLocalRandom; +import org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import static org.neo4j.kernel.impl.storageengine.impl.recordstorage.References.clearEncoding; -import static org.neo4j.kernel.impl.storageengine.impl.recordstorage.RelationshipReferenceEncoding.GROUP; -import static org.neo4j.kernel.impl.storageengine.impl.recordstorage.RelationshipReferenceEncoding.parseEncoding; +import static org.neo4j.kernel.impl.newapi.References.clearEncoding; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.FILTER; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.FILTER_TX_STATE; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.GROUP; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.NO_INCOMING_OF_TYPE; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.NO_LOOP_OF_TYPE; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.NO_OUTGOING_OF_TYPE; +import static org.neo4j.kernel.impl.newapi.RelationshipReferenceEncoding.parseEncoding; import static org.neo4j.kernel.impl.store.record.AbstractBaseRecord.NO_ID; public class ReferencesTest @@ -42,7 +48,12 @@ public class ReferencesTest @Test public void shouldPreserveNoId() { + assertThat( RelationshipReferenceEncoding.encodeForFiltering( NO_ID ), equalTo( (long) NO_ID ) ); + assertThat( RelationshipReferenceEncoding.encodeForTxStateFiltering( NO_ID ), equalTo( (long) NO_ID ) ); assertThat( RelationshipReferenceEncoding.encodeGroup( NO_ID ), equalTo( (long) NO_ID ) ); + assertThat( RelationshipReferenceEncoding.encodeNoIncomingRels( NO_ID ), equalTo( (long) NO_ID ) ); + assertThat( RelationshipReferenceEncoding.encodeNoOutgoingRels( NO_ID ), equalTo( (long) NO_ID ) ); + assertThat( RelationshipReferenceEncoding.encodeNoLoopRels( NO_ID ), equalTo( (long) NO_ID ) ); assertThat( GroupReferenceEncoding.encodeRelationship( NO_ID ), equalTo( (long) NO_ID ) ); } @@ -57,12 +68,41 @@ public void shouldClearFlags() int token = random.nextInt(Integer.MAX_VALUE); assertThat( clearEncoding( RelationshipReferenceEncoding.encodeGroup( reference ) ), equalTo( reference ) ); + assertThat( clearEncoding( RelationshipReferenceEncoding.encodeForFiltering( reference ) ), equalTo( reference ) ); + assertThat( clearEncoding( RelationshipReferenceEncoding.encodeForTxStateFiltering( reference ) ), equalTo( reference ) ); + assertThat( clearEncoding( RelationshipReferenceEncoding.encodeNoIncomingRels( token ) ), equalTo( (long) token ) ); + assertThat( clearEncoding( RelationshipReferenceEncoding.encodeNoOutgoingRels( token ) ), equalTo( (long) token ) ); + assertThat( clearEncoding( RelationshipReferenceEncoding.encodeNoLoopRels( token ) ), equalTo( (long) token ) ); assertThat( clearEncoding( GroupReferenceEncoding.encodeRelationship( reference ) ), equalTo( reference ) ); } } - // Relationship + @Test + public void encodeForFiltering() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + long reference = random.nextLong( MAX_ID_LIMIT ); + assertNotEquals( FILTER, parseEncoding( reference ) ); + assertEquals( FILTER, parseEncoding( RelationshipReferenceEncoding.encodeForFiltering( reference ) ) ); + assertTrue( "encoded reference is negative", RelationshipReferenceEncoding.encodeForFiltering( reference ) < 0 ); + } + } + + @Test + public void encodeForTxStateFiltering() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + long reference = random.nextLong( MAX_ID_LIMIT ); + assertNotEquals( FILTER_TX_STATE, parseEncoding( reference ) ); + assertEquals( FILTER_TX_STATE, parseEncoding( RelationshipReferenceEncoding.encodeForTxStateFiltering( reference ) ) ); + assertTrue( "encoded reference is negative", RelationshipReferenceEncoding.encodeForTxStateFiltering( reference ) < 0 ); + } + } @Test public void encodeFromGroup() @@ -77,18 +117,42 @@ public void encodeFromGroup() } } - // Group + @Test + public void encodeNoIncomingRels() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + int token = random.nextInt(Integer.MAX_VALUE); + assertNotEquals( NO_INCOMING_OF_TYPE, parseEncoding( token ) ); + assertEquals( NO_INCOMING_OF_TYPE, parseEncoding( RelationshipReferenceEncoding.encodeNoIncomingRels( token ) ) ); + assertTrue( "encoded reference is negative", RelationshipReferenceEncoding.encodeNoIncomingRels( token ) < 0 ); + } + } @Test - public void encodeRelationship() + public void encodeNoOutgoingRels() { ThreadLocalRandom random = ThreadLocalRandom.current(); for ( int i = 0; i < 1000; i++ ) { - long reference = random.nextLong( MAX_ID_LIMIT ); - assertFalse( GroupReferenceEncoding.isRelationship( reference ) ); - assertTrue( GroupReferenceEncoding.isRelationship( GroupReferenceEncoding.encodeRelationship( reference ) ) ); - assertTrue( "encoded reference is negative", GroupReferenceEncoding.encodeRelationship( reference ) < 0 ); + int token = random.nextInt(Integer.MAX_VALUE); + assertNotEquals( NO_OUTGOING_OF_TYPE, parseEncoding( token ) ); + assertEquals( NO_OUTGOING_OF_TYPE, parseEncoding( RelationshipReferenceEncoding.encodeNoOutgoingRels( token ) ) ); + assertTrue( "encoded reference is negative", RelationshipReferenceEncoding.encodeNoOutgoingRels( token ) < 0 ); + } + } + + @Test + public void encodeNoLoopRels() + { + ThreadLocalRandom random = ThreadLocalRandom.current(); + for ( int i = 0; i < 1000; i++ ) + { + int token = random.nextInt(Integer.MAX_VALUE); + assertNotEquals( NO_LOOP_OF_TYPE, parseEncoding( token ) ); + assertEquals( NO_LOOP_OF_TYPE, parseEncoding( RelationshipReferenceEncoding.encodeNoLoopRels( token ) ) ); + assertTrue( "encoded reference is negative", RelationshipReferenceEncoding.encodeNoLoopRels( token ) < 0 ); } } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/NeoStoresTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/NeoStoresTest.java index 4b9da23091a05..513de8bcf59a5 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/NeoStoresTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/NeoStoresTest.java @@ -108,7 +108,6 @@ import static org.junit.Assert.fail; import static org.neo4j.graphdb.factory.GraphDatabaseSettings.counts_store_rotation_timeout; import static org.neo4j.helpers.collection.MapUtil.stringMap; -import static org.neo4j.internal.kernel.api.Read.ANY_RELATIONSHIP_TYPE; import static org.neo4j.internal.kernel.api.security.LoginContext.AUTH_DISABLED; import static org.neo4j.kernel.impl.store.RecordStore.getRecord; import static org.neo4j.kernel.impl.store.format.standard.MetaDataRecordFormat.FIELD_NOT_PRESENT; @@ -1062,7 +1061,7 @@ else if ( rel == rel2 ) private StorageRelationshipTraversalCursor allocateRelationshipTraversalCursor( StorageNodeCursor node ) { StorageRelationshipTraversalCursor relationships = storageReader.allocateRelationshipTraversalCursor(); - relationships.init( node.nodeReference(), node.allRelationshipsReference(), null, ANY_RELATIONSHIP_TYPE ); + relationships.init( node.nodeReference(), node.allRelationshipsReference() ); return relationships; } diff --git a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/ReferenceTest.java b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipReferenceEncodingTest.java similarity index 98% rename from enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/ReferenceTest.java rename to enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipReferenceEncodingTest.java index 0453a9603165b..2399565ca6bc5 100644 --- a/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/ReferenceTest.java +++ b/enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/RelationshipReferenceEncodingTest.java @@ -30,7 +30,7 @@ import static org.junit.Assert.assertEquals; -public class ReferenceTest +public class RelationshipReferenceEncodingTest { /** * The current scheme only allows us to use 58 bits for a reference. Adhere to that limit here.