org.neo4j.kernel.impl.newapi.DefaultRelationshipTraversalCursor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-kernel Show documentation
Show all versions of neo4j-kernel Show documentation
Neo4j kernel is a lightweight, embedded Java database designed to
store data structured as graphs rather than tables. For more
information, see http://neo4j.org.
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://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 java.lang.String.format;
import static org.neo4j.kernel.impl.newapi.Read.NO_ID;
import org.eclipse.collections.api.iterator.LongIterator;
import org.eclipse.collections.impl.iterator.ImmutableEmptyLongIterator;
import org.neo4j.collection.PrimitiveLongCollections;
import org.neo4j.internal.kernel.api.KernelReadTracer;
import org.neo4j.internal.kernel.api.NodeCursor;
import org.neo4j.internal.kernel.api.RelationshipTraversalCursor;
import org.neo4j.internal.kernel.api.security.AccessMode;
import org.neo4j.storageengine.api.RelationshipSelection;
import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor;
class DefaultRelationshipTraversalCursor extends DefaultRelationshipCursor
implements RelationshipTraversalCursor {
private final StorageRelationshipTraversalCursor storeCursor;
private final InternalCursorFactory internalCursors;
private final boolean applyAccessModeToTxState;
private DefaultNodeCursor securityNodeCursor;
private LongIterator addedRelationships;
private long originNodeReference;
private RelationshipSelection selection;
private long neighbourNodeReference;
DefaultRelationshipTraversalCursor(
CursorPool pool,
StorageRelationshipTraversalCursor storeCursor,
InternalCursorFactory internalCursors,
boolean applyAccessModeToTxState) {
super(storeCursor, pool);
this.storeCursor = storeCursor;
this.internalCursors = internalCursors;
this.applyAccessModeToTxState = applyAccessModeToTxState;
}
/**
* Initializes this cursor to traverse over relationships, with a reference that was earlier retrieved from {@link NodeCursor#relationshipsReference()}.
*
* @param nodeReference reference to the origin node.
* @param reference reference to the place to start traversing these relationships.
* @param selection the relationship selector
* @param read reference to {@link Read}.
*/
void init(long nodeReference, long reference, RelationshipSelection selection, Read read) {
this.originNodeReference = nodeReference;
this.selection = selection;
this.neighbourNodeReference = -1;
this.storeCursor.init(nodeReference, reference, selection);
init(read);
this.addedRelationships = ImmutableEmptyLongIterator.INSTANCE;
}
/**
* Initializes this cursor to traverse over relationships, directly from the {@link NodeCursor}.
*
* @param nodeCursor {@link NodeCursor} at the origin node.
* @param selection the relationship selector
* @param read reference to {@link Read}.
*/
void init(DefaultNodeCursor nodeCursor, RelationshipSelection selection, Read read) {
this.originNodeReference = nodeCursor.nodeReference();
this.selection = selection;
this.neighbourNodeReference = NO_ID;
if (!nodeCursor.currentNodeIsAddedInTx()) {
nodeCursor.storeCursor.relationships(storeCursor, selection);
} else {
storeCursor.reset();
}
init(read);
this.addedRelationships = ImmutableEmptyLongIterator.INSTANCE;
}
/**
* Initializes this cursor to access a details of a relationship from the transaction state using the ID provided.
*
* @param addedRelationship the relationship to access
* @param read reference to {@link Read}.
*/
void init(long addedRelationship, Read read) {
assert addedRelationship != NO_ID;
this.originNodeReference = NO_ID;
this.neighbourNodeReference = NO_ID;
this.selection = null;
storeCursor.reset();
init(read);
this.checkHasChanges = false;
this.hasChanges = true;
this.addedRelationships = PrimitiveLongCollections.single(addedRelationship);
}
void init(DefaultNodeCursor nodeCursor, RelationshipSelection selection, long neighbourNodeReference, Read read) {
this.originNodeReference = nodeCursor.nodeReference();
this.selection = selection;
this.neighbourNodeReference = neighbourNodeReference;
if (!nodeCursor.currentNodeIsAddedInTx()) {
nodeCursor.storeCursor.relationshipsTo(storeCursor, selection, neighbourNodeReference);
}
init(read);
this.addedRelationships = ImmutableEmptyLongIterator.INSTANCE;
}
@Override
public void otherNode(NodeCursor cursor) {
read.singleNode(otherNodeReference(), cursor);
}
@Override
public long otherNodeReference() {
if (currentAddedInTx != NO_ID) {
// Here we compare the source/target nodes from tx-state to the origin node and decide the neighbour node
// from it
long originNodeReference = originNodeReference();
if (txStateSourceNodeReference == originNodeReference) {
return txStateTargetNodeReference;
} else if (txStateTargetNodeReference == originNodeReference) {
return txStateSourceNodeReference;
} else {
throw new IllegalStateException(format(
"Relationship[%d] which was added in tx has an origin node [%d] which is neither source [%d] nor target [%d]",
currentAddedInTx, originNodeReference, txStateSourceNodeReference, txStateTargetNodeReference));
}
}
return storeCursor.neighbourNodeReference();
}
@Override
public long originNodeReference() {
return originNodeReference;
}
@Override
public boolean next() {
boolean hasChanges = hasChanges();
// tx-state relationships
if (hasChanges) {
while (addedRelationships.hasNext()) {
read.txState().relationshipVisit(addedRelationships.next(), relationshipTxStateDataVisitor);
if (neighbourNodeReference != NO_ID && otherNodeReference() != neighbourNodeReference) {
continue;
}
if (tracer != null) {
tracer.onRelationship(relationshipReference());
}
return true;
}
currentAddedInTx = NO_ID;
}
while (storeCursor.next()) {
boolean skip = hasChanges && read.txState().relationshipIsDeletedInThisBatch(storeCursor.entityReference());
if (!skip && allowed()) {
return true;
}
}
return false;
}
@Override
public void setTracer(KernelReadTracer tracer) {
super.setTracer(tracer);
storeCursor.setTracer(tracer);
}
@Override
public void removeTracer() {
storeCursor.removeTracer();
super.removeTracer();
}
protected boolean allowed() {
AccessMode accessMode = read.getAccessMode();
if (accessMode.allowsTraverseRelType(storeCursor.type())) {
if (accessMode.allowsTraverseAllLabels()) {
return true;
}
if (securityNodeCursor == null) {
securityNodeCursor = internalCursors.allocateNodeCursor();
}
read.singleNode(storeCursor.neighbourNodeReference(), securityNodeCursor);
return securityNodeCursor.next();
}
return false;
}
@Override
public void closeInternal() {
if (!isClosed()) {
read = null;
selection = null;
storeCursor.close();
}
super.closeInternal();
}
@Override
protected void collectAddedTxStateSnapshot() {
if (selection != null) {
addedRelationships = selection.addedRelationships(read.txState().getNodeState(originNodeReference));
}
}
@Override
public boolean isClosed() {
return read == null;
}
@Override
public void release() {
if (storeCursor != null) {
storeCursor.close();
}
if (securityNodeCursor != null) {
securityNodeCursor.close();
securityNodeCursor.release();
securityNodeCursor = null;
}
}
@Override
public String toString() {
if (isClosed()) {
return "RelationshipTraversalCursor[closed state]";
} else {
return "RelationshipTraversalCursor[id=" + storeCursor.entityReference() + ", " + storeCursor + "]";
}
}
}