use of org.neo4j.kernel.impl.index.schema.EntityTokenRange in project neo4j by neo4j.
the class RelationshipChecker method check.
private void check(LongRange nodeIdRange, boolean firstRound, long fromRelationshipId, long toRelationshipId, boolean checkToEndOfIndex) throws Exception {
RelationshipCounter counter = observedCounts.instantiateRelationshipCounter();
long[] typeHolder = new long[1];
try (var cursorContext = new CursorContext(context.pageCacheTracer.createPageCursorTracer(RELATIONSHIP_RANGE_CHECKER_TAG));
RecordReader<RelationshipRecord> relationshipReader = new RecordReader<>(context.neoStores.getRelationshipStore(), true, cursorContext);
BoundedIterable<EntityTokenRange> relationshipTypeReader = getRelationshipTypeIndexReader(fromRelationshipId, toRelationshipId, checkToEndOfIndex, cursorContext);
SafePropertyChainReader property = new SafePropertyChainReader(context, cursorContext);
SchemaComplianceChecker schemaComplianceChecker = new SchemaComplianceChecker(context, mandatoryProperties, indexes, cursorContext, context.memoryTracker)) {
ProgressListener localProgress = progress.threadLocalReporter();
CacheAccess.Client client = cacheAccess.client();
MutableIntObjectMap<Value> propertyValues = new IntObjectHashMap<>();
Iterator<EntityTokenRange> relationshipTypeRangeIterator = relationshipTypeReader.iterator();
EntityTokenIndexCheckState typeIndexState = new EntityTokenIndexCheckState(null, fromRelationshipId - 1);
for (long relationshipId = fromRelationshipId; relationshipId < toRelationshipId && !context.isCancelled(); relationshipId++) {
localProgress.add(1);
RelationshipRecord relationshipRecord = relationshipReader.read(relationshipId);
if (!relationshipRecord.inUse()) {
continue;
}
// Start/end nodes
long startNode = relationshipRecord.getFirstNode();
boolean startNodeIsWithinRange = nodeIdRange.isWithinRangeExclusiveTo(startNode);
boolean startNodeIsNegativeOnFirstRound = startNode < 0 && firstRound;
if (startNodeIsWithinRange || startNodeIsNegativeOnFirstRound) {
checkRelationshipVsNode(client, relationshipRecord, startNode, relationshipRecord.isFirstInFirstChain(), (relationship, node) -> reporter.forRelationship(relationship).sourceNodeNotInUse(node), (relationship, node) -> reporter.forRelationship(relationship).sourceNodeDoesNotReferenceBack(node), (relationship, node) -> reporter.forNode(node).relationshipNotFirstInSourceChain(relationship), (relationship, node) -> reporter.forRelationship(relationship).sourceNodeHasNoRelationships(node), relationship -> reporter.forRelationship(relationship).illegalSourceNode(), cursorContext);
}
long endNode = relationshipRecord.getSecondNode();
boolean endNodeIsWithinRange = nodeIdRange.isWithinRangeExclusiveTo(endNode);
boolean endNodeIsNegativeOnFirstRound = endNode < 0 && firstRound;
if (endNodeIsWithinRange || endNodeIsNegativeOnFirstRound) {
checkRelationshipVsNode(client, relationshipRecord, endNode, relationshipRecord.isFirstInSecondChain(), (relationship, node) -> reporter.forRelationship(relationship).targetNodeNotInUse(node), (relationship, node) -> reporter.forRelationship(relationship).targetNodeDoesNotReferenceBack(node), (relationship, node) -> reporter.forNode(node).relationshipNotFirstInTargetChain(relationship), (relationship, node) -> reporter.forRelationship(relationship).targetNodeHasNoRelationships(node), relationship -> reporter.forRelationship(relationship).illegalTargetNode(), cursorContext);
}
if (firstRound) {
if (startNode >= context.highNodeId) {
reporter.forRelationship(relationshipRecord).sourceNodeNotInUse(context.recordLoader.node(startNode, cursorContext));
}
if (endNode >= context.highNodeId) {
reporter.forRelationship(relationshipRecord).targetNodeNotInUse(context.recordLoader.node(endNode, cursorContext));
}
// Properties
typeHolder[0] = relationshipRecord.getType();
lightClear(propertyValues);
boolean propertyChainIsOk = property.read(propertyValues, relationshipRecord, reporter::forRelationship, cursorContext);
if (propertyChainIsOk) {
schemaComplianceChecker.checkContainsMandatoryProperties(relationshipRecord, typeHolder, propertyValues, reporter::forRelationship);
// gets checked this way, larger indexes will be checked in IndexChecker
if (context.consistencyFlags.isCheckIndexes()) {
schemaComplianceChecker.checkCorrectlyIndexed((RelationshipRecord) relationshipRecord, typeHolder, propertyValues, reporter::forRelationship);
}
}
// Type and count
checkValidToken(relationshipRecord, relationshipRecord.getType(), tokenHolders.relationshipTypeTokens(), neoStores.getRelationshipTypeTokenStore(), (rel, token) -> reporter.forRelationship(rel).illegalRelationshipType(), (rel, token) -> reporter.forRelationship(rel).relationshipTypeNotInUse(token), cursorContext);
observedCounts.incrementRelationshipTypeCounts(counter, relationshipRecord);
// Relationship type index
if (relationshipTypeReader.maxCount() != 0) {
checkRelationshipVsRelationshipTypeIndex(relationshipRecord, relationshipTypeRangeIterator, typeIndexState, relationshipId, relationshipRecord.getType(), fromRelationshipId, cursorContext);
}
}
observedCounts.incrementRelationshipNodeCounts(counter, relationshipRecord, startNodeIsWithinRange, endNodeIsWithinRange);
}
if (firstRound && !context.isCancelled() && relationshipTypeReader.maxCount() != 0) {
reportRemainingRelationshipTypeIndexEntries(relationshipTypeRangeIterator, typeIndexState, checkToEndOfIndex ? Long.MAX_VALUE : toRelationshipId, cursorContext);
}
localProgress.done();
}
}
use of org.neo4j.kernel.impl.index.schema.EntityTokenRange in project neo4j by neo4j.
the class EntityTokenRangeTest method shouldAdaptToStringToEntityTypeRelationship.
@Test
void shouldAdaptToStringToEntityTypeRelationship() {
EntityTokenRange relationshipTypeRange = new EntityTokenRangeImpl(0, new long[0][], RELATIONSHIP);
assertThat(relationshipTypeRange.toString()).contains("RelationshipTypeRange");
}
use of org.neo4j.kernel.impl.index.schema.EntityTokenRange in project neo4j by neo4j.
the class EntityTokenRangeTest method shouldAdaptToStringToEntityTypeNode.
@Test
void shouldAdaptToStringToEntityTypeNode() {
EntityTokenRange nodeLabelRange = new EntityTokenRangeImpl(0, new long[0][], NODE);
assertThat(nodeLabelRange.toString()).contains("NodeLabelRange");
}
use of org.neo4j.kernel.impl.index.schema.EntityTokenRange in project neo4j by neo4j.
the class NodeChecker method check.
private void check(long fromNodeId, long toNodeId, boolean last) throws Exception {
long usedNodes = 0;
try (var cursorContext = new CursorContext(context.pageCacheTracer.createPageCursorTracer(NODE_RANGE_CHECKER_TAG));
RecordReader<NodeRecord> nodeReader = new RecordReader<>(context.neoStores.getNodeStore(), true, cursorContext);
RecordReader<DynamicRecord> labelReader = new RecordReader<>(context.neoStores.getNodeStore().getDynamicLabelStore(), false, cursorContext);
BoundedIterable<EntityTokenRange> labelIndexReader = getLabelIndexReader(fromNodeId, toNodeId, last, cursorContext);
SafePropertyChainReader property = new SafePropertyChainReader(context, cursorContext);
SchemaComplianceChecker schemaComplianceChecker = new SchemaComplianceChecker(context, mandatoryProperties, smallIndexes, cursorContext, context.memoryTracker)) {
ProgressListener localProgress = nodeProgress.threadLocalReporter();
MutableIntObjectMap<Value> propertyValues = new IntObjectHashMap<>();
CacheAccess.Client client = context.cacheAccess.client();
long[] nextRelCacheFields = new long[] { -1, -1, 1, /*inUse*/
0, 0, 1, /*note that this needs to be checked*/
0, 0 };
Iterator<EntityTokenRange> nodeLabelRangeIterator = labelIndexReader.iterator();
EntityTokenIndexCheckState labelIndexState = new EntityTokenIndexCheckState(null, fromNodeId - 1);
for (long nodeId = fromNodeId; nodeId < toNodeId && !context.isCancelled(); nodeId++) {
localProgress.add(1);
NodeRecord nodeRecord = nodeReader.read(nodeId);
if (!nodeRecord.inUse()) {
continue;
}
// Cache nextRel
long nextRel = nodeRecord.getNextRel();
if (nextRel < NULL_REFERENCE.longValue()) {
reporter.forNode(nodeRecord).relationshipNotInUse(new RelationshipRecord(nextRel));
nextRel = NULL_REFERENCE.longValue();
}
nextRelCacheFields[CacheSlots.NodeLink.SLOT_RELATIONSHIP_ID] = nextRel;
nextRelCacheFields[CacheSlots.NodeLink.SLOT_IS_DENSE] = longOf(nodeRecord.isDense());
usedNodes++;
// Labels
long[] unverifiedLabels = RecordLoading.safeGetNodeLabels(context, nodeRecord.getId(), nodeRecord.getLabelField(), labelReader, cursorContext);
long[] labels = checkNodeLabels(nodeRecord, unverifiedLabels, cursorContext);
// Cache the label field, so that if it contains inlined labels then it's free.
// Otherwise cache the dynamic labels in another data structure and point into it.
long labelField = nodeRecord.getLabelField();
boolean hasInlinedLabels = !NodeLabelsField.fieldPointsToDynamicRecordOfLabels(nodeRecord.getLabelField());
if (labels == null) {
// There was some inconsistency in the label field or dynamic label chain. Let's continue but w/o labels for this node
hasInlinedLabels = true;
labelField = NO_LABELS_FIELD.longValue();
}
boolean hasSingleLabel = labels != null && labels.length == 1;
nextRelCacheFields[CacheSlots.NodeLink.SLOT_HAS_INLINED_LABELS] = longOf(hasInlinedLabels);
nextRelCacheFields[CacheSlots.NodeLink.SLOT_LABELS] = hasSingleLabel ? // this makes RelationshipChecker "parse" the cached node labels more efficiently for single-label nodes
labels[0] : // Otherwise put the encoded label field if inlined, otherwise a ref to the cached dynamic labels
hasInlinedLabels ? labelField : observedCounts.cacheDynamicNodeLabels(labels);
nextRelCacheFields[CacheSlots.NodeLink.SLOT_HAS_SINGLE_LABEL] = longOf(hasSingleLabel);
// Properties
lightClear(propertyValues);
boolean propertyChainIsOk = property.read(propertyValues, nodeRecord, reporter::forNode, cursorContext);
// Label index
if (labelIndexReader.maxCount() != 0) {
checkNodeVsLabelIndex(nodeRecord, nodeLabelRangeIterator, labelIndexState, nodeId, labels, fromNodeId, cursorContext);
}
client.putToCache(nodeId, nextRelCacheFields);
// Mandatory properties and (some) indexing
if (labels != null && propertyChainIsOk) {
schemaComplianceChecker.checkContainsMandatoryProperties(nodeRecord, labels, propertyValues, reporter::forNode);
// gets checked this way, larger indexes will be checked in IndexChecker
if (context.consistencyFlags.isCheckIndexes()) {
schemaComplianceChecker.checkCorrectlyIndexed(nodeRecord, labels, propertyValues, reporter::forNode);
}
}
// Large indexes are checked elsewhere, more efficiently than per-entity
}
if (!context.isCancelled() && labelIndexReader.maxCount() != 0) {
reportRemainingLabelIndexEntries(nodeLabelRangeIterator, labelIndexState, last ? Long.MAX_VALUE : toNodeId, cursorContext);
}
localProgress.done();
}
observedCounts.incrementNodeLabel(ANY_LABEL, usedNodes);
}
use of org.neo4j.kernel.impl.index.schema.EntityTokenRange in project neo4j by neo4j.
the class EntityTokenRangeTest method shouldRebaseOnRangeId.
@Test
void shouldRebaseOnRangeId() {
// given
long[][] labelsPerNode = new long[][] { { 1 }, { 1, 3 }, { 3, 5, 7 }, {}, { 1, 5, 7 }, {}, {}, { 1, 2, 3, 4 } };
// when
EntityTokenRange range = new EntityTokenRangeImpl(10, labelsPerNode, NODE);
// then
long baseNodeId = range.id() * labelsPerNode.length;
long[] expectedNodeIds = new long[labelsPerNode.length];
for (int i = 0; i < expectedNodeIds.length; i++) {
expectedNodeIds[i] = baseNodeId + i;
}
assertArrayEquals(expectedNodeIds, range.entities());
}
Aggregations