From 5400f6ca96ecf382f1bbc3730cdf54439db1b36a Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Fri, 18 May 2018 13:16:40 +0200 Subject: [PATCH] Make `RelationshipCursor::hasProperties` tx-state aware --- .../RelationshipTransactionStateTestBase.java | 93 +++++++++++++++++++ .../newapi/DefaultRelationshipScanCursor.java | 5 +- .../DefaultRelationshipTraversalCursor.java | 4 +- .../impl/newapi/RelationshipCursor.java | 38 +++++++- 4 files changed, 131 insertions(+), 9 deletions(-) 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 291087790d74..0bfe02d74972 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 @@ -49,6 +49,7 @@ import static org.neo4j.internal.kernel.api.RelationshipTransactionStateTestBase.RelationshipDirection.LOOP; import static org.neo4j.internal.kernel.api.RelationshipTransactionStateTestBase.RelationshipDirection.OUT; import static org.neo4j.values.storable.Values.NO_VALUE; +import static org.neo4j.values.storable.Values.longValue; import static org.neo4j.values.storable.Values.stringValue; @SuppressWarnings( "Duplicates" ) @@ -1269,6 +1270,98 @@ private Map modifyStartNodeRelationships( RelationshipTestSuppor return expectedCounts; } + @Test + public void hasPropertiesShouldSeeNewlyCreatedProperties() throws Exception + { + // Given + long relationship; + try ( Transaction tx = session.beginTransaction() ) + { + Write write = tx.dataWrite(); + int token = tx.tokenWrite().relationshipTypeGetOrCreateForName( "R" ); + relationship = write.relationshipCreate( write.nodeCreate(), + token, + write.nodeCreate() ); + tx.success(); + } + + // Then + try ( Transaction tx = session.beginTransaction() ) + { + try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor() ) + { + tx.dataRead().singleRelationship( relationship, cursor ); + assertTrue( cursor.next() ); + assertFalse( cursor.hasProperties() ); + tx.dataWrite().relationshipSetProperty( relationship, + tx.tokenWrite().propertyKeyGetOrCreateForName( "prop" ), + stringValue( "foo" ) ); + assertTrue( cursor.hasProperties() ); + } + } + } + + @Test + public void hasPropertiesShouldSeeNewlyCreatedPropertiesOnNewlyCreatedRelationship() throws Exception + { + try ( Transaction tx = session.beginTransaction() ) + { + 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() ) + { + tx.dataRead().singleRelationship( relationship, cursor ); + assertTrue( cursor.next() ); + assertFalse( cursor.hasProperties() ); + tx.dataWrite().relationshipSetProperty( relationship, + tx.tokenWrite().propertyKeyGetOrCreateForName( "prop" ), + stringValue( "foo" ) ); + assertTrue( cursor.hasProperties() ); + } + } + } + + @Test + public void hasPropertiesShouldSeeNewlyRemovedProperties() throws Exception + { + // Given + long relationship; + int prop1, prop2, prop3; + try ( Transaction tx = session.beginTransaction() ) + { + Write write = tx.dataWrite(); + int token = tx.tokenWrite().relationshipTypeGetOrCreateForName( "R" ); + relationship = write.relationshipCreate( write.nodeCreate(), token, write.nodeCreate() ); + prop1 = tx.tokenWrite().propertyKeyGetOrCreateForName( "prop1" ); + prop2 = tx.tokenWrite().propertyKeyGetOrCreateForName( "prop2" ); + prop3 = tx.tokenWrite().propertyKeyGetOrCreateForName( "prop3" ); + tx.dataWrite().relationshipSetProperty( relationship, prop1, longValue( 1 ) ); + tx.dataWrite().relationshipSetProperty( relationship, prop2, longValue( 2 ) ); + tx.dataWrite().relationshipSetProperty( relationship, prop3, longValue( 3 ) ); + tx.success(); + } + + // Then + try ( Transaction tx = session.beginTransaction() ) + { + try ( RelationshipScanCursor cursor = tx.cursors().allocateRelationshipScanCursor() ) + { + tx.dataRead().singleRelationship( relationship, cursor ); + assertTrue( cursor.next() ); + + assertTrue( cursor.hasProperties() ); + tx.dataWrite().relationshipRemoveProperty( relationship, prop1 ); + assertTrue( cursor.hasProperties() ); + tx.dataWrite().relationshipRemoveProperty( relationship, prop2 ); + assertTrue( cursor.hasProperties() ); + tx.dataWrite().relationshipRemoveProperty( relationship, prop3 ); + assertFalse( cursor.hasProperties() ); + } + } + } + + private void relateNTimes( int nRelationshipsInStore, int type, long n1, long n2, Transaction tx ) throws KernelException { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipScanCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipScanCursor.java index ca0b8ad91770..a3c39a0b2638 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipScanCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultRelationshipScanCursor.java @@ -35,11 +35,9 @@ class DefaultRelationshipScanCursor extends RelationshipCursor implements Relati private PageCursor pageCursor; private Set addedRelationships; - private final DefaultCursors pool; - DefaultRelationshipScanCursor( DefaultCursors pool ) { - this.pool = pool; + super(pool); } void scan( int type, Read read ) @@ -142,6 +140,7 @@ private boolean containsRelationship( TransactionState txs ) @Override public void close() { + super.close(); if ( !isClosed() ) { read = null; 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 345418d6462b..dae433d6a1b1 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 @@ -122,7 +122,6 @@ private static FilterState fromRelationshipDirection( RelationshipDirection dire private Record buffer; private PageCursor pageCursor; private final DefaultRelationshipGroupCursor group; - private final DefaultCursors pool; private GroupState groupState; private FilterState filterState; private boolean filterStore; @@ -132,8 +131,8 @@ private static FilterState fromRelationshipDirection( RelationshipDirection dire DefaultRelationshipTraversalCursor( DefaultRelationshipGroupCursor group, DefaultCursors pool ) { + super(pool); this.group = group; - this.pool = pool; } /* @@ -482,6 +481,7 @@ private boolean traversingDenseNode() @Override public void close() { + super.close(); if ( !isClosed() ) { read = null; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipCursor.java index 3a77a651b5f7..8fc68d31b384 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/RelationshipCursor.java @@ -27,13 +27,16 @@ abstract class RelationshipCursor extends RelationshipRecord implements RelationshipDataAccessor, RelationshipVisitor { - Read read; private boolean hasChanges; private boolean checkHasChanges; + private PropertyCursor propertyCursor; + final DefaultCursors pool; + Read read; - RelationshipCursor() + RelationshipCursor( DefaultCursors pool ) { super( NO_ID ); + this.pool = pool; } protected void init( Read read ) @@ -57,7 +60,16 @@ public int type() @Override public boolean hasProperties() { - return nextProp != DefaultPropertyCursor.NO_ID; + if (read.hasTxStateWithChanges()) + { + PropertyCursor cursor = propertyCursor(); + properties( cursor ); + return cursor.next(); + } + else + { + return nextProp != DefaultPropertyCursor.NO_ID; + } } @Override @@ -118,7 +130,7 @@ protected boolean hasChanges() } // Load transaction state using RelationshipVisitor - protected void loadFromTxState( long reference ) + void loadFromTxState( long reference ) { read.txState().relationshipVisit( reference, this ); } @@ -130,4 +142,22 @@ public void visit( long relationshipId, int typeId, long startNodeId, long endNo setId( relationshipId ); initialize( true, NO_ID, startNodeId, endNodeId, typeId, NO_ID, NO_ID, NO_ID, NO_ID, false, false ); } + + private PropertyCursor propertyCursor() + { + if ( propertyCursor == null ) + { + propertyCursor = pool.allocatePropertyCursor(); + } + return propertyCursor; + } + + void close() + { + if ( propertyCursor != null ) + { + propertyCursor.close(); + propertyCursor = null; + } + } }