All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.kernel.impl.newapi.AllStoreHolder Maven / Gradle / Ivy

/*
 * Copyright (c) "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.function.Predicates.alwaysTrue;
import static org.neo4j.internal.helpers.collection.Iterators.singleOrNull;
import static org.neo4j.internal.kernel.api.IndexQueryConstraints.unconstrained;
import static org.neo4j.storageengine.api.txstate.TxStateVisitor.EMPTY;

import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.neo4j.collection.Dependencies;
import org.neo4j.collection.RawIterator;
import org.neo4j.collection.diffset.DiffSets;
import org.neo4j.common.EntityType;
import org.neo4j.exceptions.KernelException;
import org.neo4j.internal.helpers.collection.Iterators;
import org.neo4j.internal.kernel.api.IndexMonitor;
import org.neo4j.internal.kernel.api.IndexReadSession;
import org.neo4j.internal.kernel.api.InternalIndexState;
import org.neo4j.internal.kernel.api.NodeValueIndexCursor;
import org.neo4j.internal.kernel.api.PopulationProgress;
import org.neo4j.internal.kernel.api.PropertyIndexQuery;
import org.neo4j.internal.kernel.api.RelationshipDataAccessor;
import org.neo4j.internal.kernel.api.RelationshipIndexCursor;
import org.neo4j.internal.kernel.api.RelationshipScanCursor;
import org.neo4j.internal.kernel.api.RelationshipValueIndexCursor;
import org.neo4j.internal.kernel.api.SchemaReadCore;
import org.neo4j.internal.kernel.api.TokenPredicate;
import org.neo4j.internal.kernel.api.TokenRead;
import org.neo4j.internal.kernel.api.TokenReadSession;
import org.neo4j.internal.kernel.api.exceptions.ProcedureException;
import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException;
import org.neo4j.internal.kernel.api.procs.ProcedureCallContext;
import org.neo4j.internal.kernel.api.procs.ProcedureHandle;
import org.neo4j.internal.kernel.api.procs.ProcedureSignature;
import org.neo4j.internal.kernel.api.procs.QualifiedName;
import org.neo4j.internal.kernel.api.procs.UserAggregationReducer;
import org.neo4j.internal.kernel.api.procs.UserFunctionHandle;
import org.neo4j.internal.kernel.api.procs.UserFunctionSignature;
import org.neo4j.internal.kernel.api.security.AccessMode;
import org.neo4j.internal.kernel.api.security.SecurityAuthorizationHandler;
import org.neo4j.internal.schema.ConstraintDescriptor;
import org.neo4j.internal.schema.IndexDescriptor;
import org.neo4j.internal.schema.IndexType;
import org.neo4j.internal.schema.SchemaDescriptor;
import org.neo4j.internal.schema.SchemaDescriptors;
import org.neo4j.internal.schema.SchemaState;
import org.neo4j.io.pagecache.context.CursorContext;
import org.neo4j.kernel.api.AssertOpen;
import org.neo4j.kernel.api.index.IndexSample;
import org.neo4j.kernel.api.index.IndexUsageStats;
import org.neo4j.kernel.api.index.TokenIndexReader;
import org.neo4j.kernel.api.index.ValueIndexReader;
import org.neo4j.kernel.api.procedure.GlobalProcedures;
import org.neo4j.kernel.api.txstate.TransactionState;
import org.neo4j.kernel.impl.api.ClockContext;
import org.neo4j.kernel.impl.api.IndexReaderCache;
import org.neo4j.kernel.impl.api.KernelTransactionImplementation;
import org.neo4j.kernel.impl.api.OverridableSecurityContext;
import org.neo4j.kernel.impl.api.index.IndexingService;
import org.neo4j.kernel.impl.api.index.stats.IndexStatisticsStore;
import org.neo4j.kernel.impl.api.parallel.ParallelAccessCheck;
import org.neo4j.kernel.impl.api.parallel.ThreadExecutionContext;
import org.neo4j.kernel.impl.locking.Locks;
import org.neo4j.kernel.impl.locking.Locks.Client;
import org.neo4j.lock.LockTracer;
import org.neo4j.lock.ResourceTypes;
import org.neo4j.memory.MemoryTracker;
import org.neo4j.storageengine.api.CountsDelta;
import org.neo4j.storageengine.api.StorageLocks;
import org.neo4j.storageengine.api.StorageReader;
import org.neo4j.storageengine.api.StorageSchemaReader;
import org.neo4j.storageengine.api.cursor.StoreCursors;
import org.neo4j.storageengine.api.txstate.TransactionCountingStateVisitor;
import org.neo4j.values.AnyValue;
import org.neo4j.values.storable.Value;

/**
 * Implementation of read operation of the Kernel API.
 * 

* The relation between a transaction and All Store Holder can be one to many * and the instances of All Store Holder related to the same transaction can be used concurrently by multiple threads. * The thread safety aspect of the operation performed by All Store Holder must reflect it! * This means that All Store Holder must not use objects shared by multiple All Store Holders that are * not safe for multithreaded use!!! * This class has two implementations. * {@link ForTransactionScope} has one-to-one relation with a transaction and therefore it can safely * use {@link KernelTransactionImplementation}, its resources and generally any transaction-scoped resource * {@link ForThreadExecutionContextScope} has one-to-many relation with a transaction and therefore it CANNOT safely * use {@link KernelTransactionImplementation}, its resources and generally any transaction-scoped * resource which is not designed for concurrent use. * It should use resources in {@link org.neo4j.kernel.impl.api.parallel.ThreadExecutionContext} scope instead. */ public abstract class AllStoreHolder extends Read { private final SchemaState schemaState; private final IndexingService indexingService; private final IndexStatisticsStore indexStatisticsStore; private final GlobalProcedures globalProcedures; private final boolean enableIndexUsageStatistics; private final MemoryTracker memoryTracker; private final IndexReaderCache valueIndexReaderCache; private final IndexReaderCache tokenIndexReaderCache; private AllStoreHolder( StorageReader storageReader, TokenRead tokenRead, SchemaState schemaState, IndexingService indexingService, IndexStatisticsStore indexStatisticsStore, GlobalProcedures globalProcedures, MemoryTracker memoryTracker, DefaultPooledCursors cursors, StoreCursors storageCursors, StorageLocks storageLocks, LockTracer lockTracer, boolean enableIndexUsageStatistics) { super(storageReader, tokenRead, cursors, storageCursors, storageLocks, lockTracer); this.schemaState = schemaState; this.valueIndexReaderCache = new IndexReaderCache<>( index -> indexingService.getIndexProxy(index).newValueReader()); this.tokenIndexReaderCache = new IndexReaderCache<>( index -> indexingService.getIndexProxy(index).newTokenReader()); this.indexingService = indexingService; this.indexStatisticsStore = indexStatisticsStore; this.memoryTracker = memoryTracker; this.globalProcedures = globalProcedures; this.enableIndexUsageStatistics = enableIndexUsageStatistics; } @Override public boolean nodeExists(long reference) { performCheckBeforeOperation(); if (hasTxStateWithChanges()) { TransactionState txState = txState(); if (txState.nodeIsDeletedInThisTx(reference)) { return false; } else if (txState.nodeIsAddedInThisTx(reference)) { return true; } } boolean existsInNodeStore = storageReader.nodeExists(reference, storageCursors); if (getAccessMode().allowsTraverseAllLabels()) { return existsInNodeStore; } else if (!existsInNodeStore) { return false; } else { try (DefaultNodeCursor node = cursors.allocateNodeCursor(cursorContext())) { singleNode(reference, node); return node.next(); } } } @Override public boolean nodeDeletedInTransaction(long node) { performCheckBeforeOperation(); return hasTxStateWithChanges() && txState().nodeIsDeletedInThisTx(node); } @Override public boolean relationshipDeletedInTransaction(long relationship) { performCheckBeforeOperation(); return hasTxStateWithChanges() && txState().relationshipIsDeletedInThisTx(relationship); } @Override public Value nodePropertyChangeInTransactionOrNull(long node, int propertyKeyId) { performCheckBeforeOperation(); return hasTxStateWithChanges() ? txState().getNodeState(node).propertyValue(propertyKeyId) : null; } @Override public Value relationshipPropertyChangeInTransactionOrNull(long relationship, int propertyKeyId) { performCheckBeforeOperation(); return hasTxStateWithChanges() ? txState().getRelationshipState(relationship).propertyValue(propertyKeyId) : null; } @Override public long countsForNode(int labelId) { return countsForNodeWithoutTxState(labelId) + countsForNodeInTxState(labelId); } @Override public long countsForNodeWithoutTxState(int labelId) { if (getAccessMode().allowsTraverseAllNodesWithLabel(labelId)) { // All nodes with the specified label can be traversed, so the count store can be used. return storageReader.countsForNode(labelId, cursorContext()); } else if (getAccessMode().disallowsTraverseLabel(labelId)) { // No nodes with the specified label can be traversed, so the count will be 0. return 0; } else { // We have a restriction on what part of the graph can be traversed, that can affect nodes with the // specified label. // This disables the count store entirely. // We need to calculate the counts through expensive operations. // We cannot use a NodeLabelScan without an expensive post-filtering, since it is not guaranteed that all // nodes with the label can be traversed. long count = 0; // DefaultNodeCursor already contains traversal checks within next() try (DefaultNodeCursor nodes = cursors.allocateNodeCursor(cursorContext())) { this.allNodesScan(nodes); while (nodes.next()) { if (labelId == TokenRead.ANY_LABEL || nodes.hasLabel(labelId)) { count++; } } } return count - countsForNodeInTxState(labelId); } } private long countsForNodeInTxState(int labelId) { long count = 0; if (hasTxStateWithChanges()) { CountsDelta counts = new CountsDelta(); try { TransactionState txState = txState(); try (var countingVisitor = new TransactionCountingStateVisitor( EMPTY, storageReader, txState, counts, cursorContext(), storageCursors)) { txState.accept(countingVisitor); } if (counts.hasChanges()) { count += counts.nodeCount(labelId, cursorContext()); } } catch (KernelException e) { throw new IllegalArgumentException("Unexpected error: " + e.getMessage()); } } return count; } @Override public long countsForRelationship(int startLabelId, int typeId, int endLabelId) { return countsForRelationshipWithoutTxState(startLabelId, typeId, endLabelId) + countsForRelationshipInTxState(startLabelId, typeId, endLabelId); } @Override public long countsForRelationshipWithoutTxState(int startLabelId, int typeId, int endLabelId) { if (getAccessMode().allowsTraverseRelType(typeId) && getAccessMode().allowsTraverseNode(startLabelId) && getAccessMode().allowsTraverseNode(endLabelId)) { return storageReader.countsForRelationship(startLabelId, typeId, endLabelId, cursorContext()); } if (getAccessMode().disallowsTraverseRelType(typeId) || getAccessMode().disallowsTraverseLabel(startLabelId) || getAccessMode().disallowsTraverseLabel(endLabelId)) { // Not allowed to traverse any relationship with the specified relationship type, start node label and end // node label, // so the count will be 0. return 0; } // token index scan can only scan for single relationship type if (typeId != TokenRead.ANY_RELATIONSHIP_TYPE) { try { var index = findUsableTokenIndex(EntityType.RELATIONSHIP); if (index != IndexDescriptor.NO_INDEX) { long count = 0; try (var relationshipsWithType = cursors.allocateRelationshipTypeIndexCursor(cursorContext()); DefaultNodeCursor sourceNode = cursors.allocateNodeCursor(cursorContext()); DefaultNodeCursor targetNode = cursors.allocateNodeCursor(cursorContext())) { var session = tokenReadSession(index); this.relationshipTypeScan( session, relationshipsWithType, unconstrained(), new TokenPredicate(typeId), cursorContext()); count += countRelationshipsWithEndLabels( relationshipsWithType, sourceNode, targetNode, startLabelId, endLabelId); } return count - countsForRelationshipInTxState(startLabelId, typeId, endLabelId); } } catch (KernelException ignored) { // ignore, fallback to allRelationshipsScan } } long count; try (DefaultRelationshipScanCursor rels = cursors.allocateRelationshipScanCursor(cursorContext()); DefaultNodeCursor sourceNode = cursors.allocateFullAccessNodeCursor(cursorContext()); DefaultNodeCursor targetNode = cursors.allocateFullAccessNodeCursor(cursorContext())) { this.allRelationshipsScan(rels); Predicate predicate = typeId == TokenRead.ANY_RELATIONSHIP_TYPE ? alwaysTrue() : CursorPredicates.hasType(typeId); var filteredCursor = new FilteringRelationshipScanCursorWrapper(rels, predicate); count = countRelationshipsWithEndLabels(filteredCursor, sourceNode, targetNode, startLabelId, endLabelId); } return count - countsForRelationshipInTxState(startLabelId, typeId, endLabelId); } private static long countRelationshipsWithEndLabels( RelationshipIndexCursor relationship, DefaultNodeCursor sourceNode, DefaultNodeCursor targetNode, int startLabelId, int endLabelId) { long internalCount = 0; while (relationship.next()) { if (relationship.readFromStore() && matchesLabels(relationship, sourceNode, targetNode, startLabelId, endLabelId)) { internalCount++; } } return internalCount; } private static long countRelationshipsWithEndLabels( RelationshipScanCursor relationship, DefaultNodeCursor sourceNode, DefaultNodeCursor targetNode, int startLabelId, int endLabelId) { long internalCount = 0; while (relationship.next()) { if (matchesLabels(relationship, sourceNode, targetNode, startLabelId, endLabelId)) { internalCount++; } } return internalCount; } private static boolean matchesLabels( RelationshipDataAccessor relationship, DefaultNodeCursor sourceNode, DefaultNodeCursor targetNode, int startLabelId, int endLabelId) { relationship.source(sourceNode); relationship.target(targetNode); return sourceNode.next() && (startLabelId == TokenRead.ANY_LABEL || sourceNode.hasLabel(startLabelId)) && targetNode.next() && (endLabelId == TokenRead.ANY_LABEL || targetNode.hasLabel(endLabelId)); } private long countsForRelationshipInTxState(int startLabelId, int typeId, int endLabelId) { long count = 0; if (hasTxStateWithChanges()) { CountsDelta counts = new CountsDelta(); try { TransactionState txState = txState(); try (var countingVisitor = new TransactionCountingStateVisitor( EMPTY, storageReader, txState, counts, cursorContext(), storageCursors)) { txState.accept(countingVisitor); } if (counts.hasChanges()) { count += counts.relationshipCount(startLabelId, typeId, endLabelId, cursorContext()); } } catch (KernelException e) { throw new IllegalArgumentException("Unexpected error: " + e.getMessage()); } } return count; } IndexDescriptor findUsableTokenIndex(EntityType entityType) throws IndexNotFoundKernelException { var descriptor = SchemaDescriptors.forAnyEntityTokens(entityType); var index = index(descriptor, IndexType.LOOKUP); if (index != IndexDescriptor.NO_INDEX && indexGetState(index) == InternalIndexState.ONLINE) { return index; } return IndexDescriptor.NO_INDEX; } @Override public boolean relationshipExists(long reference) { performCheckBeforeOperation(); if (hasTxStateWithChanges()) { TransactionState txState = txState(); if (txState.relationshipIsDeletedInThisTx(reference)) { return false; } else if (txState.relationshipIsAddedInThisTx(reference)) { return true; } } boolean existsInRelStore = storageReader.relationshipExists(reference, storageCursors); if (getAccessMode().allowsTraverseAllRelTypes()) { return existsInRelStore; } else if (!existsInRelStore) { return false; } else { try (DefaultRelationshipScanCursor rels = cursors.allocateRelationshipScanCursor(cursorContext())) { singleRelationship(reference, rels); return rels.next(); } } } @Override public ValueIndexReader newValueIndexReader(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); return indexingService.getIndexProxy(index).newValueReader(); } public TokenIndexReader newTokenIndexReader(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); return indexingService.getIndexProxy(index).newTokenReader(); } @Override public IndexReadSession indexReadSession(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); return new DefaultIndexReadSession(valueIndexReaderCache.getOrCreate(index), index); } @Override public TokenReadSession tokenReadSession(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); return new DefaultTokenReadSession(tokenIndexReaderCache.getOrCreate(index), index); } @Override public Iterator indexForSchemaNonTransactional(SchemaDescriptor schema) { return storageReader.indexGetForSchema(schema); } @Override public IndexDescriptor indexForSchemaAndIndexTypeNonTransactional(SchemaDescriptor schema, IndexType indexType) { var index = storageReader.indexGetForSchemaAndType(schema, indexType); return index == null ? IndexDescriptor.NO_INDEX : index; } @Override public Iterator indexForSchemaNonLocking(SchemaDescriptor schema) { return indexGetForSchema(storageReader, schema); } @Override public Iterator getLabelIndexesNonLocking(int labelId) { return indexesGetForLabel(storageReader, labelId); } @Override public Iterator getRelTypeIndexesNonLocking(int relTypeId) { return indexesGetForRelationshipType(storageReader, relTypeId); } @Override public Iterator indexesGetAllNonLocking() { return indexesGetAll(storageReader); } /** * Lock the given index if it is valid and exists. * If the given index descriptor does not reference an index that exists, then {@link IndexDescriptor#NO_INDEX} is returned. * * @param index committed, transaction-added or even null. * @return The validated index descriptor, which is not necessarily the same as the one given as an argument. */ private IndexDescriptor lockIndex(IndexDescriptor index) { if (index == null) { return IndexDescriptor.NO_INDEX; } index = acquireSharedSchemaLock(index); // Since the schema cache gives us snapshots views of the schema, the indexes could be dropped in-between us // getting the snapshot, and taking the shared schema locks. // Thus, after we take the lock, we need to filter out indexes that no longer exists. if (!indexExists(index)) { releaseSharedSchemaLock(index); index = IndexDescriptor.NO_INDEX; } return index; } /** * Maps all index descriptors according to {@link #lockIndex(IndexDescriptor)}. */ private Iterator lockIndexes(Iterator indexes) { Predicate exists = index -> index != IndexDescriptor.NO_INDEX; return Iterators.filter(exists, Iterators.map(this::lockIndex, indexes)); } private boolean indexExists(IndexDescriptor index) { if (hasTxStateWithChanges()) { DiffSets changes = txState().indexChanges(); return changes.isAdded(index) || (storageReader.indexExists(index) && !changes.isRemoved(index)); } return storageReader.indexExists(index); } public void assertIndexExists(IndexDescriptor index) throws IndexNotFoundKernelException { if (!indexExists(index)) { throw new IndexNotFoundKernelException("Index does not exist: ", index); } } private ConstraintDescriptor lockConstraint(ConstraintDescriptor constraint) { if (constraint == null) { return null; } constraint = acquireSharedSchemaLock(constraint); if (!constraintExists(constraint)) { releaseSharedSchemaLock(constraint); constraint = null; } return constraint; } private Iterator lockConstraints(Iterator constraints) { return Iterators.filter(Objects::nonNull, Iterators.map(this::lockConstraint, constraints)); } @Override public boolean constraintExists(ConstraintDescriptor constraint) { acquireSharedSchemaLock(constraint); performCheckBeforeOperation(); if (hasTxStateWithChanges()) { DiffSets changes = txState().constraintsChanges(); return changes.isAdded(constraint) || (storageReader.constraintExists(constraint) && !changes.isRemoved(constraint)); } return storageReader.constraintExists(constraint); } @Override public Iterator index(SchemaDescriptor schema) { performCheckBeforeOperation(); return lockIndexes(indexGetForSchema(storageReader, schema)); } Iterator indexGetForSchema(StorageSchemaReader reader, SchemaDescriptor schema) { Iterator indexes = reader.indexGetForSchema(schema); if (hasTxStateWithChanges()) { DiffSets diffSets = txState().indexDiffSetsBySchema(schema); indexes = diffSets.apply(indexes); } return indexes; } @Override public IndexDescriptor index(SchemaDescriptor schema, IndexType type) { performCheckBeforeOperation(); return lockIndex(indexGetForSchemaAndType(storageReader, schema, type)); } IndexDescriptor indexGetForSchemaAndType(StorageSchemaReader reader, SchemaDescriptor schema, IndexType type) { var index = reader.indexGetForSchemaAndType(schema, type); if (hasTxStateWithChanges()) { var indexChanges = txState().indexChanges(); if (index == null) { // check if such index was added in this tx var added = indexChanges .filterAdded( id -> id.getIndexType() == type && id.schema().equals(schema)) .getAdded(); index = singleOrNull(added.iterator()); } if (indexChanges.isRemoved(index)) { // this index was removed in this tx return null; } } return index; } @Override public Iterator indexesGetForLabel(int labelId) { acquireSharedLock(ResourceTypes.LABEL, labelId); performCheckBeforeOperation(); return lockIndexes(indexesGetForLabel(storageReader, labelId)); } Iterator indexesGetForLabel(StorageSchemaReader reader, int labelId) { if (getAccessMode().allowsTraverseNode(labelId)) { Iterator iterator = reader.indexesGetForLabel(labelId); if (hasTxStateWithChanges()) { iterator = txState().indexDiffSetsByLabel(labelId).apply(iterator); } return iterator; } else { return Collections.emptyIterator(); } } @Override public Iterator indexesGetForRelationshipType(int relationshipType) { acquireSharedLock(ResourceTypes.RELATIONSHIP_TYPE, relationshipType); performCheckBeforeOperation(); return lockIndexes(indexesGetForRelationshipType(storageReader, relationshipType)); } Iterator indexesGetForRelationshipType(StorageSchemaReader reader, int relationshipType) { Iterator iterator = reader.indexesGetForRelationshipType(relationshipType); if (hasTxStateWithChanges()) { iterator = txState().indexDiffSetsByRelationshipType(relationshipType).apply(iterator); } return iterator; } @Override public IndexDescriptor indexGetForName(String name) { return indexGetForName(storageReader, name); } IndexDescriptor indexGetForName(StorageSchemaReader reader, String name) { performCheckBeforeOperation(); IndexDescriptor index = reader.indexGetForName(name); if (hasTxStateWithChanges()) { Predicate namePredicate = indexDescriptor -> indexDescriptor.getName().equals(name); Iterator indexes = txState().indexChanges().filterAdded(namePredicate).apply(Iterators.iterator(index)); index = singleOrNull(indexes); } return lockIndex(index); } @Override public ConstraintDescriptor constraintGetForName(String name) { return constraintGetForName(storageReader, name); } ConstraintDescriptor constraintGetForName(StorageSchemaReader reader, String name) { performCheckBeforeOperation(); ConstraintDescriptor constraint = reader.constraintGetForName(name); if (hasTxStateWithChanges()) { Predicate namePredicate = constraintDescriptor -> constraintDescriptor.getName().equals(name); Iterator constraints = txState().constraintsChanges().filterAdded(namePredicate).apply(Iterators.iterator(constraint)); constraint = singleOrNull(constraints); } return lockConstraint(constraint); } @Override public Iterator indexesGetAll() { performCheckBeforeOperation(); Iterator iterator = indexesGetAll(storageReader); return lockIndexes(iterator); } Iterator indexesGetAll(StorageSchemaReader reader) { Iterator iterator = reader.indexesGetAll(); if (hasTxStateWithChanges()) { iterator = txState().indexChanges().apply(iterator); } return iterator; } @Override public InternalIndexState indexGetState(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); acquireSharedSchemaLock(index); performCheckBeforeOperation(); return indexGetStateLocked(index); } @Override public InternalIndexState indexGetStateNonLocking(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); performCheckBeforeOperation(); return indexGetStateLocked( index); // TODO: Can we call this method without locking(since we assert valid index)? } InternalIndexState indexGetStateLocked(IndexDescriptor index) throws IndexNotFoundKernelException { SchemaDescriptor schema = index.schema(); // If index is in our state, then return populating if (hasTxStateWithChanges()) { if (checkIndexState(index, txState().indexDiffSetsBySchema(schema))) { return InternalIndexState.POPULATING; } } return indexingService.getIndexProxy(index).getState(); } @Override public PopulationProgress indexGetPopulationProgress(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); acquireSharedSchemaLock(index); performCheckBeforeOperation(); return indexGetPopulationProgressLocked(index); } PopulationProgress indexGetPopulationProgressLocked(IndexDescriptor index) throws IndexNotFoundKernelException { if (hasTxStateWithChanges()) { if (checkIndexState(index, txState().indexDiffSetsBySchema(index.schema()))) { return PopulationProgress.NONE; } } return indexingService.getIndexProxy(index).getIndexPopulationProgress(); } @Override public Long indexGetOwningUniquenessConstraintId(IndexDescriptor index) { acquireSharedSchemaLock(index); performCheckBeforeOperation(); return storageReader.indexGetOwningUniquenessConstraintId(storageReader.indexGetForName(index.getName())); } @Override public String indexGetFailure(IndexDescriptor index) throws IndexNotFoundKernelException { assertValidIndex(index); return indexingService.getIndexProxy(index).getPopulationFailure().asString(); } @Override public double indexUniqueValuesSelectivity(IndexDescriptor index) throws IndexNotFoundKernelException { performCheckBeforeOperation(); assertValidIndex(index); acquireSharedSchemaLock(index); assertIndexExists(index); // Throws if the index has been dropped. final IndexSample indexSample = indexStatisticsStore.indexSample(index.getId()); long unique = indexSample.uniqueValues(); long size = indexSample.sampleSize(); return size == 0 ? 1.0d : ((double) unique) / ((double) size); } @Override public long indexSize(IndexDescriptor index) throws IndexNotFoundKernelException { performCheckBeforeOperation(); assertValidIndex(index); acquireSharedSchemaLock(index); return indexStatisticsStore.indexSample(index.getId()).indexSize(); } @Override public IndexUsageStats indexUsageStats(IndexDescriptor index) throws IndexNotFoundKernelException { performCheckBeforeOperation(); assertValidIndex(index); if (!enableIndexUsageStatistics) { throw new UnsupportedOperationException("Index usage statistics are not supported yet"); } acquireSharedSchemaLock(index); return indexStatisticsStore.usageStats(index.getId()); } @Override public long nodesGetCount() { return countsForNode(TokenRead.ANY_LABEL); } @Override public long relationshipsGetCount() { return countsForRelationship(TokenRead.ANY_LABEL, TokenRead.ANY_RELATIONSHIP_TYPE, TokenRead.ANY_LABEL); } @Override public IndexSample indexSample(IndexDescriptor index) throws IndexNotFoundKernelException { performCheckBeforeOperation(); assertValidIndex(index); return indexStatisticsStore.indexSample(index.getId()); } private static boolean checkIndexState(IndexDescriptor index, DiffSets diffSet) throws IndexNotFoundKernelException { if (diffSet.isAdded(index)) { return true; } if (diffSet.isRemoved(index)) { throw new IndexNotFoundKernelException("Index has been dropped in this transaction: ", index); } return false; } @Override public Iterator constraintsGetForSchema(SchemaDescriptor schema) { acquireSharedSchemaLock(() -> schema); return getConstraintsForSchema(schema); } @Override public Iterator constraintsGetForSchemaNonLocking(SchemaDescriptor schema) { return getConstraintsForSchema(schema); } private Iterator getConstraintsForSchema(SchemaDescriptor schema) { performCheckBeforeOperation(); Iterator constraints = storageReader.constraintsGetForSchema(schema); if (hasTxStateWithChanges()) { return txState().constraintsChangesForSchema(schema).apply(constraints); } return constraints; } @Override public Iterator constraintsGetForLabel(int labelId) { performCheckBeforeOperation(); acquireSharedLock(ResourceTypes.LABEL, labelId); return constraintsGetForLabel(storageReader, labelId); } @Override public Iterator constraintsGetForLabelNonLocking(int labelId) { performCheckBeforeOperation(); return constraintsGetForLabel(storageReader, labelId); } Iterator constraintsGetForLabel(StorageSchemaReader reader, int labelId) { Iterator constraints = reader.constraintsGetForLabel(labelId); if (hasTxStateWithChanges()) { return txState().constraintsChangesForLabel(labelId).apply(constraints); } return constraints; } @Override public Iterator constraintsGetAll() { performCheckBeforeOperation(); Iterator constraints = constraintsGetAll(storageReader); return lockConstraints(constraints); } @Override public Iterator constraintsGetAllNonLocking() { performCheckBeforeOperation(); return constraintsGetAll(storageReader); } Iterator constraintsGetAll(StorageSchemaReader reader) { Iterator constraints = reader.constraintsGetAll(); if (hasTxStateWithChanges()) { constraints = txState().constraintsChanges().apply(constraints); } return constraints; } @Override public Iterator constraintsGetForRelationshipType(int typeId) { performCheckBeforeOperation(); acquireSharedLock(ResourceTypes.RELATIONSHIP_TYPE, typeId); return constraintsGetForRelationshipType(storageReader, typeId); } @Override public Iterator constraintsGetForRelationshipTypeNonLocking(int typeId) { performCheckBeforeOperation(); return constraintsGetForRelationshipType(storageReader, typeId); } Iterator constraintsGetForRelationshipType(StorageSchemaReader reader, int typeId) { Iterator constraints = reader.constraintsGetForRelationshipType(typeId); if (hasTxStateWithChanges()) { return txState().constraintsChangesForRelationshipType(typeId).apply(constraints); } return constraints; } @Override public SchemaReadCore snapshot() { performCheckBeforeOperation(); StorageSchemaReader snapshot = storageReader.schemaSnapshot(); return new SchemaReadCoreSnapshot(snapshot, this); } @Override public V schemaStateGetOrCreate(K key, Function creator) { return schemaState.getOrCreate(key, creator); } @Override public void schemaStateFlush() { schemaState.clear(); } @Override public boolean transactionStateHasChanges() { return hasTxStateWithChanges(); } static void assertValidIndex(IndexDescriptor index) throws IndexNotFoundKernelException { if (index == IndexDescriptor.NO_INDEX) { throw new IndexNotFoundKernelException("No index was found"); } } public void release() { valueIndexReaderCache.close(); tokenIndexReaderCache.close(); } @Override public MemoryTracker memoryTracker() { return memoryTracker; } @Override public IndexMonitor monitor() { return indexingService.getMonitor(); } @Override public RawIterator procedureCallRead( int id, AnyValue[] arguments, ProcedureCallContext context) throws ProcedureException { return procedureCaller().callProcedure(id, arguments, AccessMode.Static.READ, context); } @Override public RawIterator procedureCallWrite( int id, AnyValue[] arguments, ProcedureCallContext context) throws ProcedureException { return procedureCaller().callProcedure(id, arguments, AccessMode.Static.TOKEN_WRITE, context); } @Override public RawIterator procedureCallSchema( int id, AnyValue[] arguments, ProcedureCallContext context) throws ProcedureException { return procedureCaller().callProcedure(id, arguments, AccessMode.Static.SCHEMA, context); } @Override public RawIterator procedureCallDbms( int id, AnyValue[] arguments, ProcedureCallContext context) throws ProcedureException { return procedureCaller().callProcedure(id, arguments, AccessMode.Static.ACCESS, context); } @Override public AnyValue functionCall(int id, AnyValue[] arguments) throws ProcedureException { return procedureCaller().callFunction(id, arguments); } @Override public AnyValue builtInFunctionCall(int id, AnyValue[] arguments) throws ProcedureException { return procedureCaller().callBuiltInFunction(id, arguments); } @Override public UserAggregationReducer aggregationFunction(int id) throws ProcedureException { return procedureCaller().createAggregationFunction(id); } @Override public UserAggregationReducer builtInAggregationFunction(int id) throws ProcedureException { return procedureCaller().createBuiltInAggregationFunction(id); } @Override public UserFunctionHandle functionGet(QualifiedName name) { performCheckBeforeOperation(); return globalProcedures.function(name); } @Override public Stream functionGetAll() { performCheckBeforeOperation(); return globalProcedures.getAllNonAggregatingFunctions(); } @Override public ProcedureHandle procedureGet(QualifiedName name) throws ProcedureException { performCheckBeforeOperation(); return globalProcedures.procedure(name); } @Override public Set proceduresGetAll() { performCheckBeforeOperation(); return globalProcedures.getAllProcedures(); } @Override public UserFunctionHandle aggregationFunctionGet(QualifiedName name) { performCheckBeforeOperation(); return globalProcedures.aggregationFunction(name); } @Override public Stream aggregationFunctionGetAll() { performCheckBeforeOperation(); return globalProcedures.getAllAggregatingFunctions(); } abstract ProcedureCaller procedureCaller(); public static class ForTransactionScope extends AllStoreHolder { private final KernelTransactionImplementation ktx; private final ProcedureCaller procedureCaller; public ForTransactionScope( StorageReader storageReader, TokenRead tokenRead, KernelTransactionImplementation ktx, StorageLocks storageLocks, DefaultPooledCursors cursors, GlobalProcedures globalProcedures, SchemaState schemaState, IndexingService indexingService, IndexStatisticsStore indexStatisticsStore, Dependencies databaseDependencies, MemoryTracker memoryTracker, boolean enableIndexUsageStatistics) { super( storageReader, tokenRead, schemaState, indexingService, indexStatisticsStore, globalProcedures, memoryTracker, cursors, ktx.storeCursors(), storageLocks, ktx.lockTracer(), enableIndexUsageStatistics); this.ktx = ktx; this.procedureCaller = new ProcedureCaller.ForTransactionScope(ktx, globalProcedures, databaseDependencies); } @Override public TransactionState txState() { return ktx.txState(); } @Override public boolean hasTxStateWithChanges() { return ktx.hasTxStateWithChanges(); } @Override void performCheckBeforeOperation() { if (ParallelAccessCheck.shouldPerformCheck()) { ParallelAccessCheck.checkNotCypherWorkerThread(); } ktx.assertOpen(); } @Override AccessMode getAccessMode() { return ktx.securityContext().mode(); } @Override Locks.Client getLockClient() { // lock client has to be accessed like this, because of KernelTransaction#freezeLocks return ktx.lockClient(); } @Override public CursorContext cursorContext() { return ktx.cursorContext(); } @Override ProcedureCaller procedureCaller() { return procedureCaller; } } public static class ForThreadExecutionContextScope extends AllStoreHolder { private final OverridableSecurityContext overridableSecurityContext; private final CursorContext cursorContext; private final Locks.Client lockClient; private final AssertOpen assertOpen; private final ProcedureCaller procedureCaller; public ForThreadExecutionContextScope( ThreadExecutionContext executionContext, StorageReader storageReader, SchemaState schemaState, IndexingService indexingService, IndexStatisticsStore indexStatisticsStore, GlobalProcedures globalProcedures, Dependencies databaseDependencies, DefaultPooledCursors cursors, StoreCursors storageCursors, CursorContext cursorContext, StorageLocks storageLocks, Client lockClient, LockTracer lockTracer, OverridableSecurityContext overridableSecurityContext, AssertOpen assertOpen, SecurityAuthorizationHandler securityAuthorizationHandler, Supplier clockContextSupplier, boolean enableIndexUsageStatistics) { super( storageReader, executionContext.tokenRead(), schemaState, indexingService, indexStatisticsStore, globalProcedures, executionContext.memoryTracker(), cursors, storageCursors, storageLocks, lockTracer, enableIndexUsageStatistics); this.overridableSecurityContext = overridableSecurityContext; this.cursorContext = cursorContext; this.lockClient = lockClient; this.assertOpen = assertOpen; this.procedureCaller = new ProcedureCaller.ForThreadExecutionContextScope( executionContext, globalProcedures, databaseDependencies, overridableSecurityContext, assertOpen, securityAuthorizationHandler, clockContextSupplier); } @Override public long lockingNodeUniqueIndexSeek( IndexDescriptor index, NodeValueIndexCursor cursor, PropertyIndexQuery.ExactPredicate... predicates) { // This is currently a problematic operation for parallel execution, because it takes exclusive locks. // In transactions deadlocks is a problem for another day :) . throw new UnsupportedOperationException("Locking unique index seek not allowed during parallel execution"); } @Override public long lockingRelationshipUniqueIndexSeek( IndexDescriptor index, RelationshipValueIndexCursor cursor, PropertyIndexQuery.ExactPredicate... predicates) throws KernelException { // This is currently a problematic operation for parallel execution, because it takes exclusive locks. // In transactions deadlocks is a problem for another day :) . throw new UnsupportedOperationException("Locking unique index seek not allowed during parallel execution"); } @Override public RawIterator procedureCallWrite( int id, AnyValue[] arguments, ProcedureCallContext context) { throw new UnsupportedOperationException( "Invoking procedure with WRITE access mode is not allowed during parallel execution."); } @Override public RawIterator procedureCallSchema( int id, AnyValue[] arguments, ProcedureCallContext context) { throw new UnsupportedOperationException( "Invoking procedure with SCHEMA access mode is not allowed during parallel execution."); } @Override public TransactionState txState() { throw new UnsupportedOperationException( "Accessing transaction state is not allowed during parallel execution"); } @Override public boolean hasTxStateWithChanges() { return false; } @Override void performCheckBeforeOperation() { assertOpen.assertOpen(); } @Override AccessMode getAccessMode() { return overridableSecurityContext.currentSecurityContext().mode(); } @Override Locks.Client getLockClient() { return lockClient; } @Override public CursorContext cursorContext() { return cursorContext; } @Override ProcedureCaller procedureCaller() { return procedureCaller; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy