From c798096d5689545f087f4cb2056ac519302902c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Finn=C3=A9?= Date: Sun, 27 May 2018 10:10:29 +0200 Subject: [PATCH] Brings back removed kernel API dereferenced accessor methods Some of the reference encoding logic is split, so that most of it is in the kernel code and only select parts remain as store cursor responsibilities. --- .../org/neo4j/internal/kernel/api/Read.java | 42 +++++++ .../kernel/api/PropertyCursorTestBase.java | 4 +- .../RelationshipTransactionStateTestBase.java | 108 +++++++++++----- .../RelationshipTraversalCursorTestBase.java | 85 +++++++++++-- .../internal/kernel/api/helpers/StubRead.java | 25 ++++ .../kernel/impl/api/state/NodeStateImpl.java | 6 + .../api/state/PropertyContainerStateImpl.java | 6 + .../impl/api/state/RelationshipStateImpl.java | 6 + .../kernel/impl/newapi/DefaultNodeCursor.java | 4 +- .../DefaultRelationshipGroupCursor.java | 18 ++- .../DefaultRelationshipTraversalCursor.java | 89 ++++++++++++- .../org/neo4j/kernel/impl/newapi/Read.java | 26 ++++ .../recordstorage => newapi}/References.java | 12 +- .../newapi/RelationshipReferenceEncoding.java | 118 ++++++++++++++++++ .../recordstorage/GroupReferenceEncoding.java | 2 + .../impl/recordstorage/RecordNodeCursor.java | 1 + .../RecordRelationshipGroupCursor.java | 19 ++- .../RecordRelationshipTraversalCursor.java | 6 +- .../RelationshipReferenceEncoding.java | 64 ---------- .../StorageRelationshipTraversalCursor.java | 4 +- .../api/txstate/PropertyContainerState.java | 8 ++ .../internal/BatchRelationshipIterable.java | 4 +- .../GroupReferenceEncodingTest.java | 46 +++++++ ...RecordRelationshipTraversalCursorTest.java | 13 +- .../impl/recordstorage/ReferencesTest.java | 86 +++++++++++-- .../kernel/impl/store/NeoStoresTest.java | 3 +- ...=> RelationshipReferenceEncodingTest.java} | 2 +- 27 files changed, 642 insertions(+), 165 deletions(-) rename community/kernel/src/main/java/org/neo4j/kernel/impl/{storageengine/impl/recordstorage => newapi}/References.java (88%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipReferenceEncoding.java delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/RelationshipReferenceEncoding.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/storageengine/impl/recordstorage/GroupReferenceEncodingTest.java rename enterprise/kernel/src/test/java/org/neo4j/kernel/impl/store/format/highlimit/{ReferenceTest.java => RelationshipReferenceEncodingTest.java} (98%) 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.