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 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.stream.Stream;
import org.neo4j.collection.Dependencies;
import org.neo4j.collection.RawIterator;
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.PopulationProgress;
import org.neo4j.internal.kernel.api.RelationshipScanCursor;
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.UserAggregator;
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.AdminAccessMode;
import org.neo4j.internal.kernel.api.security.SecurityContext;
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.KernelTransaction;
import org.neo4j.kernel.api.Statement;
import org.neo4j.kernel.api.index.IndexSample;
import org.neo4j.kernel.api.index.TokenIndexReader;
import org.neo4j.kernel.api.index.ValueIndexReader;
import org.neo4j.kernel.api.procedure.Context;
import org.neo4j.kernel.api.procedure.GlobalProcedures;
import org.neo4j.kernel.api.txstate.TransactionState;
import org.neo4j.kernel.impl.api.IndexReaderCache;
import org.neo4j.kernel.impl.api.KernelTransactionImplementation;
import org.neo4j.kernel.impl.api.index.IndexingService;
import org.neo4j.kernel.impl.api.index.stats.IndexStatisticsStore;
import org.neo4j.kernel.impl.api.security.OverriddenAccessMode;
import org.neo4j.kernel.impl.api.security.RestrictedAccessMode;
import org.neo4j.kernel.impl.coreapi.InternalTransaction;
import org.neo4j.kernel.impl.util.DefaultValueMapper;
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.txstate.DiffSets;
import org.neo4j.storageengine.api.txstate.TransactionCountingStateVisitor;
import org.neo4j.values.AnyValue;
import org.neo4j.values.storable.Value;
import static java.lang.String.format;
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.kernel.api.procedure.BasicContext.buildContext;
import static org.neo4j.storageengine.api.txstate.TxStateVisitor.EMPTY;
public class AllStoreHolder extends Read
{
private final GlobalProcedures globalProcedures;
private final SchemaState schemaState;
private final IndexingService indexingService;
private final IndexStatisticsStore indexStatisticsStore;
private final Dependencies databaseDependencies;
private final MemoryTracker memoryTracker;
private final IndexReaderCache valueIndexReaderCache;
private final IndexReaderCache tokenIndexReaderCache;
public AllStoreHolder( StorageReader storageReader, KernelTransactionImplementation ktx, StorageLocks storageLocks,
DefaultPooledCursors cursors, GlobalProcedures globalProcedures, SchemaState schemaState, IndexingService indexingService,
IndexStatisticsStore indexStatisticsStore, Dependencies databaseDependencies, MemoryTracker memoryTracker )
{
super( storageReader, cursors, ktx, storageLocks );
this.globalProcedures = globalProcedures;
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.databaseDependencies = databaseDependencies;
this.memoryTracker = memoryTracker;
}
@Override
public boolean nodeExists( long reference )
{
ktx.assertOpen();
if ( hasTxStateWithChanges() )
{
TransactionState txState = txState();
if ( txState.nodeIsDeletedInThisTx( reference ) )
{
return false;
}
else if ( txState.nodeIsAddedInThisTx( reference ) )
{
return true;
}
}
AccessMode mode = ktx.securityContext().mode();
boolean existsInNodeStore = storageReader.nodeExists( reference, ktx.storeCursors() );
if ( mode.allowsTraverseAllLabels() )
{
return existsInNodeStore;
}
else if ( !existsInNodeStore )
{
return false;
}
else
{
// DefaultNodeCursor already contains traversal checks within next()
try ( DefaultNodeCursor node = cursors.allocateNodeCursor( ktx.cursorContext() ) )
{
ktx.dataRead().singleNode( reference, node );
return node.next();
}
}
}
@Override
public boolean nodeDeletedInTransaction( long node )
{
ktx.assertOpen();
return hasTxStateWithChanges() && txState().nodeIsDeletedInThisTx( node );
}
@Override
public boolean relationshipDeletedInTransaction( long relationship )
{
ktx.assertOpen();
return hasTxStateWithChanges() && txState().relationshipIsDeletedInThisTx( relationship );
}
@Override
public Value nodePropertyChangeInTransactionOrNull( long node, int propertyKeyId )
{
ktx.assertOpen();
return hasTxStateWithChanges() ? txState().getNodeState( node ).propertyValue( propertyKeyId ) : null;
}
@Override
public Value relationshipPropertyChangeInTransactionOrNull( long relationship, int propertyKeyId )
{
ktx.assertOpen();
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 )
{
AccessMode mode = ktx.securityContext().mode();
if ( mode.allowsTraverseAllNodesWithLabel( labelId ) )
{
// All nodes with the specified label can be traversed, so the count store can be used.
return storageReader.countsForNode( labelId, ktx.cursorContext() );
}
else if ( mode.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( ktx.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 ( ktx.hasTxStateWithChanges() )
{
CountsDelta counts = new CountsDelta();
try
{
TransactionState txState = ktx.txState();
CursorContext cursorContext = ktx.cursorContext();
try ( var countingVisitor = new TransactionCountingStateVisitor( EMPTY, storageReader, txState, counts, cursorContext, ktx.storeCursors() ) )
{
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 )
{
AccessMode mode = ktx.securityContext().mode();
CursorContext cursorContext = ktx.cursorContext();
if ( mode.allowsTraverseRelType( typeId ) &&
mode.allowsTraverseNode( startLabelId ) &&
mode.allowsTraverseNode( endLabelId ) )
{
return storageReader.countsForRelationship( startLabelId, typeId, endLabelId, cursorContext );
}
if ( mode.disallowsTraverseRelType( typeId ) ||
mode.disallowsTraverseLabel( startLabelId ) ||
mode.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 ( DefaultRelationshipTypeIndexCursor relationshipsWithType = cursors.allocateRelationshipTypeIndexCursor( cursorContext );
DefaultRelationshipScanCursor relationship = cursors.allocateRelationshipScanCursor( cursorContext );
DefaultNodeCursor sourceNode = cursors.allocateNodeCursor( cursorContext );
DefaultNodeCursor targetNode = cursors.allocateNodeCursor( cursorContext ) )
{
var session = tokenReadSession( index );
this.relationshipTypeScan( session, relationshipsWithType, unconstrained(), new TokenPredicate( typeId ), ktx.cursorContext() );
while ( relationshipsWithType.next() )
{
relationshipsWithType.relationship( relationship );
count += countRelationshipsWithEndLabels( relationship, 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( RelationshipScanCursor relationship, DefaultNodeCursor sourceNode, DefaultNodeCursor targetNode,
int startLabelId, int endLabelId )
{
long internalCount = 0;
while ( relationship.next() )
{
relationship.source( sourceNode );
relationship.target( targetNode );
if ( sourceNode.next() && (startLabelId == TokenRead.ANY_LABEL || sourceNode.hasLabel( startLabelId )) &&
targetNode.next() && (endLabelId == TokenRead.ANY_LABEL || targetNode.hasLabel( endLabelId )) )
{
internalCount++;
}
}
return internalCount;
}
private long countsForRelationshipInTxState( int startLabelId, int typeId, int endLabelId )
{
long count = 0;
if ( ktx.hasTxStateWithChanges() )
{
CursorContext cursorContext = ktx.cursorContext();
CountsDelta counts = new CountsDelta();
try
{
TransactionState txState = ktx.txState();
try ( var countingVisitor = new TransactionCountingStateVisitor( EMPTY, storageReader, txState, counts, cursorContext, ktx.storeCursors() ) )
{
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 )
{
ktx.assertOpen();
if ( hasTxStateWithChanges() )
{
TransactionState txState = txState();
if ( txState.relationshipIsDeletedInThisTx( reference ) )
{
return false;
}
else if ( txState.relationshipIsAddedInThisTx( reference ) )
{
return true;
}
}
AccessMode mode = ktx.securityContext().mode();
CursorContext cursorContext = ktx.cursorContext();
boolean existsInRelStore = storageReader.relationshipExists( reference, ktx.storeCursors() );
if ( mode.allowsTraverseAllRelTypes() )
{
return existsInRelStore;
}
else if ( !existsInRelStore )
{
return false;
}
else
{
// DefaultNodeCursor already contains traversal checks within next()
try ( DefaultRelationshipScanCursor rels = cursors.allocateRelationshipScanCursor( cursorContext ) )
{
ktx.dataRead().singleRelationship( reference, rels );
return rels.next();
}
}
}
@Override
public ValueIndexReader newValueIndexReader( IndexDescriptor index ) throws IndexNotFoundKernelException
{
assertValidIndex( index );
return indexingService.getIndexProxy( index ).newValueReader();
}
@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 ( ktx.hasTxStateWithChanges() )
{
DiffSets changes = ktx.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 );
ktx.assertOpen();
if ( ktx.hasTxStateWithChanges() )
{
DiffSets changes = ktx.txState().constraintsChanges();
return changes.isAdded( constraint ) || (storageReader.constraintExists( constraint ) && !changes.isRemoved( constraint ) );
}
return storageReader.constraintExists( constraint );
}
@Override
public Iterator index( SchemaDescriptor schema )
{
ktx.assertOpen();
return lockIndexes( indexGetForSchema( storageReader, schema ) );
}
Iterator indexGetForSchema( StorageSchemaReader reader, SchemaDescriptor schema )
{
Iterator indexes = reader.indexGetForSchema( schema );
if ( ktx.hasTxStateWithChanges() )
{
DiffSets diffSets = ktx.txState().indexDiffSetsBySchema( schema );
indexes = diffSets.apply( indexes );
}
return indexes;
}
@Override
public IndexDescriptor index( SchemaDescriptor schema, IndexType type )
{
ktx.assertOpen();
return lockIndex( indexGetForSchemaAndType( storageReader, schema, type ) );
}
IndexDescriptor indexGetForSchemaAndType( StorageSchemaReader reader, SchemaDescriptor schema, IndexType type )
{
var index = reader.indexGetForSchemaAndType( schema, type );
if ( ktx.hasTxStateWithChanges() )
{
var indexChanges = ktx.txState().indexChanges();
if ( index == null )
{
// check if such index was added in this tx
var added = indexChanges.filterAdded( id -> id.schema().equals( schema ) && id.getIndexType() == type ).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 );
ktx.assertOpen();
return lockIndexes( indexesGetForLabel( storageReader, labelId ) );
}
Iterator indexesGetForLabel( StorageSchemaReader reader, int labelId )
{
if ( ktx.securityContext().mode().allowsTraverseNode( labelId ) )
{
Iterator iterator = reader.indexesGetForLabel( labelId );
if ( ktx.hasTxStateWithChanges() )
{
iterator = ktx.txState().indexDiffSetsByLabel( labelId ).apply( iterator );
}
return iterator;
}
else
{
return Collections.emptyIterator();
}
}
@Override
public Iterator indexesGetForRelationshipType( int relationshipType )
{
acquireSharedLock( ResourceTypes.RELATIONSHIP_TYPE, relationshipType );
ktx.assertOpen();
return lockIndexes( indexesGetForRelationshipType( storageReader, relationshipType ) );
}
Iterator indexesGetForRelationshipType( StorageSchemaReader reader, int relationshipType )
{
Iterator iterator = reader.indexesGetForRelationshipType( relationshipType );
if ( ktx.hasTxStateWithChanges() )
{
iterator = ktx.txState().indexDiffSetsByRelationshipType( relationshipType ).apply( iterator );
}
return iterator;
}
@Override
public IndexDescriptor indexGetForName( String name )
{
return indexGetForName( storageReader, name );
}
IndexDescriptor indexGetForName( StorageSchemaReader reader, String name )
{
ktx.assertOpen();
IndexDescriptor index = reader.indexGetForName( name );
if ( ktx.hasTxStateWithChanges() )
{
Predicate namePredicate = indexDescriptor -> indexDescriptor.getName().equals( name );
Iterator indexes = ktx.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 )
{
ktx.assertOpen();
ConstraintDescriptor constraint = reader.constraintGetForName( name );
if ( ktx.hasTxStateWithChanges() )
{
Predicate namePredicate = constraintDescriptor -> constraintDescriptor.getName().equals( name );
Iterator constraints =
ktx.txState().constraintsChanges().filterAdded( namePredicate ).apply( Iterators.iterator( constraint ) );
constraint = singleOrNull( constraints );
}
return lockConstraint( constraint );
}
@Override
public Iterator indexesGetAll()
{
ktx.assertOpen();
Iterator iterator = indexesGetAll( storageReader );
return lockIndexes( iterator );
}
Iterator indexesGetAll( StorageSchemaReader reader )
{
Iterator iterator = reader.indexesGetAll();
if ( ktx.hasTxStateWithChanges() )
{
iterator = ktx.txState().indexChanges().apply( iterator );
}
return iterator;
}
@Override
public InternalIndexState indexGetState( IndexDescriptor index ) throws IndexNotFoundKernelException
{
assertValidIndex( index );
acquireSharedSchemaLock( index );
ktx.assertOpen();
return indexGetStateLocked( index );
}
@Override
public InternalIndexState indexGetStateNonLocking( IndexDescriptor index ) throws IndexNotFoundKernelException
{
assertValidIndex( index );
ktx.assertOpen();
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 ( ktx.hasTxStateWithChanges() )
{
if ( checkIndexState( index, ktx.txState().indexDiffSetsBySchema( schema ) ) )
{
return InternalIndexState.POPULATING;
}
}
return indexingService.getIndexProxy( index ).getState();
}
@Override
public PopulationProgress indexGetPopulationProgress( IndexDescriptor index )
throws IndexNotFoundKernelException
{
assertValidIndex( index );
acquireSharedSchemaLock( index );
ktx.assertOpen();
return indexGetPopulationProgressLocked( index );
}
PopulationProgress indexGetPopulationProgressLocked( IndexDescriptor index ) throws IndexNotFoundKernelException
{
if ( ktx.hasTxStateWithChanges() )
{
if ( checkIndexState( index, ktx.txState().indexDiffSetsBySchema( index.schema() ) ) )
{
return PopulationProgress.NONE;
}
}
return indexingService.getIndexProxy( index ).getIndexPopulationProgress();
}
@Override
public Long indexGetOwningUniquenessConstraintId( IndexDescriptor index )
{
acquireSharedSchemaLock( index );
ktx.assertOpen();
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
{
assertValidIndex( index );
acquireSharedSchemaLock( index );
ktx.assertOpen();
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
{
assertValidIndex( index );
acquireSharedSchemaLock( index );
ktx.assertOpen();
return indexStatisticsStore.indexSample( index.getId() ).indexSize();
}
@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
{
ktx.assertOpen();
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 )
{
ktx.assertOpen();
Iterator constraints = storageReader.constraintsGetForSchema( schema );
if ( ktx.hasTxStateWithChanges() )
{
return ktx.txState().constraintsChangesForSchema( schema ).apply( constraints );
}
return constraints;
}
@Override
public Iterator constraintsGetForLabel( int labelId )
{
acquireSharedLock( ResourceTypes.LABEL, labelId );
ktx.assertOpen();
return constraintsGetForLabel( storageReader, labelId );
}
@Override
public Iterator constraintsGetForLabelNonLocking( int labelId )
{
ktx.assertOpen();
return constraintsGetForLabel( storageReader, labelId );
}
Iterator constraintsGetForLabel( StorageSchemaReader reader, int labelId )
{
Iterator constraints = reader.constraintsGetForLabel( labelId );
if ( ktx.hasTxStateWithChanges() )
{
return ktx.txState().constraintsChangesForLabel( labelId ).apply( constraints );
}
return constraints;
}
@Override
public Iterator constraintsGetAll()
{
ktx.assertOpen();
Iterator constraints = constraintsGetAll( storageReader );
return lockConstraints( constraints );
}
@Override
public Iterator constraintsGetAllNonLocking()
{
ktx.assertOpen();
return constraintsGetAll( storageReader );
}
Iterator constraintsGetAll( StorageSchemaReader reader )
{
Iterator constraints = reader.constraintsGetAll();
if ( ktx.hasTxStateWithChanges() )
{
constraints = ktx.txState().constraintsChanges().apply( constraints );
}
return constraints;
}
@Override
public Iterator constraintsGetForRelationshipType( int typeId )
{
acquireSharedLock( ResourceTypes.RELATIONSHIP_TYPE, typeId );
ktx.assertOpen();
return constraintsGetForRelationshipType( storageReader, typeId );
}
@Override
public Iterator constraintsGetForRelationshipTypeNonLocking( int typeId )
{
ktx.assertOpen();
return constraintsGetForRelationshipType( storageReader, typeId );
}
Iterator constraintsGetForRelationshipType( StorageSchemaReader reader, int typeId )
{
Iterator constraints = reader.constraintsGetForRelationshipType( typeId );
if ( ktx.hasTxStateWithChanges() )
{
return ktx.txState().constraintsChangesForRelationshipType( typeId ).apply( constraints );
}
return constraints;
}
@Override
public SchemaReadCore snapshot()
{
ktx.assertOpen();
StorageSchemaReader snapshot = storageReader.schemaSnapshot();
return new SchemaReadCoreSnapshot( snapshot, ktx, this );
}
@Override
public UserFunctionHandle functionGet( QualifiedName name )
{
ktx.assertOpen();
return globalProcedures.function( name );
}
@Override
public Stream functionGetAll( )
{
ktx.assertOpen();
return globalProcedures.getAllNonAggregatingFunctions();
}
@Override
public ProcedureHandle procedureGet( QualifiedName name ) throws ProcedureException
{
ktx.assertOpen();
return globalProcedures.procedure( name );
}
@Override
public Set proceduresGetAll( )
{
ktx.assertOpen();
return globalProcedures.getAllProcedures();
}
@Override
public UserFunctionHandle aggregationFunctionGet( QualifiedName name )
{
ktx.assertOpen();
return globalProcedures.aggregationFunction( name );
}
@Override
public Stream aggregationFunctionGetAll( )
{
ktx.assertOpen();
return globalProcedures.getAllAggregatingFunctions();
}
@Override
public RawIterator procedureCallRead( int id, AnyValue[] arguments, ProcedureCallContext context )
throws ProcedureException
{
return callProcedure( id, arguments, AccessMode.Static.READ, context );
}
@Override
public RawIterator procedureCallWrite( int id, AnyValue[] arguments, ProcedureCallContext context )
throws ProcedureException
{
return callProcedure( id, arguments, AccessMode.Static.TOKEN_WRITE, context );
}
@Override
public RawIterator procedureCallSchema( int id, AnyValue[] arguments, ProcedureCallContext context )
throws ProcedureException
{
return callProcedure( id, arguments, AccessMode.Static.SCHEMA, context );
}
@Override
public RawIterator procedureCallDbms( int id, AnyValue[] arguments, ProcedureCallContext context )
throws ProcedureException
{
return callProcedure( id, arguments, AccessMode.Static.ACCESS, context );
}
@Override
public AnyValue functionCall( int id, AnyValue[] arguments ) throws ProcedureException
{
return callFunction( id, arguments );
}
@Override
public AnyValue builtInFunctionCall( int id, AnyValue[] arguments ) throws ProcedureException
{
return callBuiltInFunction( id, arguments );
}
@Override
public UserAggregator aggregationFunction( int id ) throws ProcedureException
{
return createAggregationFunction( id );
}
@Override
public UserAggregator builtInAggregationFunction( int id ) throws ProcedureException
{
return createBuiltInAggregationFunction( id );
}
@Override
public V schemaStateGetOrCreate( K key, Function creator )
{
return schemaState.getOrCreate( key, creator );
}
@Override
public void schemaStateFlush()
{
schemaState.clear();
}
@Override
public boolean transactionStateHasChanges()
{
return ktx.hasTxStateWithChanges();
}
private RawIterator callProcedure(
int id, AnyValue[] input, final AccessMode.Static procedureMode, ProcedureCallContext procedureCallContext )
throws ProcedureException
{
ktx.assertOpen();
AccessMode mode = ktx.securityContext().mode();
if ( !mode.allowsExecuteProcedure( id ).allowsAccess() )
{
String message = format("Executing procedure is not allowed for %s.", ktx.securityContext().description() );
throw ktx.securityAuthorizationHandler().logAndGetAuthorizationException( ktx.securityContext(), message );
}
final SecurityContext procedureSecurityContext = mode.shouldBoostProcedure( id ).allowsAccess() ?
ktx.securityContext().withMode( new OverriddenAccessMode( mode, procedureMode ) ).withMode( AdminAccessMode.FULL ) :
ktx.securityContext().withMode( new RestrictedAccessMode( mode, procedureMode ) );
final RawIterator procedureCall;
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( procedureSecurityContext );
Statement statement = ktx.acquireStatement() )
{
procedureCall = globalProcedures
.callProcedure( prepareContext( procedureSecurityContext, procedureCallContext ), id, input, statement );
}
return createIterator( procedureSecurityContext, procedureCall );
}
private RawIterator createIterator( SecurityContext procedureSecurityContext,
RawIterator procedureCall )
{
return new RawIterator<>()
{
@Override
public boolean hasNext() throws ProcedureException
{
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( procedureSecurityContext ) )
{
return procedureCall.hasNext();
}
}
@Override
public AnyValue[] next() throws ProcedureException
{
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( procedureSecurityContext ) )
{
return procedureCall.next();
}
}
};
}
private AnyValue callFunction( int id, AnyValue[] input ) throws ProcedureException
{
ktx.assertOpen();
AccessMode mode = ktx.securityContext().mode();
if ( !mode.allowsExecuteFunction( id ).allowsAccess() )
{
String message = format( "Executing a user defined function is not allowed for %s.", ktx.securityContext().description() );
throw ktx.securityAuthorizationHandler().logAndGetAuthorizationException( ktx.securityContext(), message );
}
final SecurityContext securityContext = mode.shouldBoostFunction( id ).allowsAccess() ?
ktx.securityContext().withMode( new OverriddenAccessMode( mode, AccessMode.Static.READ ) ) :
ktx.securityContext().withMode( new RestrictedAccessMode( mode, AccessMode.Static.READ ) );
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( securityContext ) )
{
return globalProcedures.callFunction( prepareContext( securityContext, ProcedureCallContext.EMPTY ), id, input );
}
}
private AnyValue callBuiltInFunction( int id, AnyValue[] input ) throws ProcedureException
{
ktx.assertOpen();
return globalProcedures.callFunction( prepareContext( ktx.securityContext(), ProcedureCallContext.EMPTY ), id, input );
}
private UserAggregator createAggregationFunction( int id ) throws ProcedureException
{
ktx.assertOpen();
AccessMode mode = ktx.securityContext().mode();
if ( !mode.allowsExecuteAggregatingFunction( id ).allowsAccess() )
{
String message = format( "Executing a user defined aggregating function is not allowed for %s.", ktx.securityContext().description() );
throw ktx.securityAuthorizationHandler().logAndGetAuthorizationException( ktx.securityContext(), message );
}
final SecurityContext securityContext = mode.shouldBoostAggregatingFunction( id ).allowsAccess() ?
ktx.securityContext().withMode( new OverriddenAccessMode( mode, AccessMode.Static.READ ) ) :
ktx.securityContext().withMode( new RestrictedAccessMode( mode, AccessMode.Static.READ ) );
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( securityContext ) )
{
UserAggregator aggregator = globalProcedures.createAggregationFunction( prepareContext( securityContext, ProcedureCallContext.EMPTY ), id );
return new UserAggregator()
{
@Override
public void update( AnyValue[] input ) throws ProcedureException
{
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( securityContext ) )
{
aggregator.update( input );
}
}
@Override
public AnyValue result() throws ProcedureException
{
try ( KernelTransaction.Revertable ignore = ktx.overrideWith( securityContext ) )
{
return aggregator.result();
}
}
};
}
}
private UserAggregator createBuiltInAggregationFunction( int id ) throws ProcedureException
{
ktx.assertOpen();
UserAggregator aggregator = globalProcedures.createAggregationFunction( prepareContext( ktx.securityContext(), ProcedureCallContext.EMPTY ), id );
return new UserAggregator()
{
@Override
public void update( AnyValue[] input ) throws ProcedureException
{
aggregator.update( input );
}
@Override
public AnyValue result() throws ProcedureException
{
return aggregator.result();
}
};
}
private Context prepareContext( SecurityContext securityContext, ProcedureCallContext procedureContext )
{
final InternalTransaction internalTransaction = ktx.internalTransaction();
return buildContext( databaseDependencies, new DefaultValueMapper( internalTransaction ) )
.withTransaction( internalTransaction )
.withSecurityContext( securityContext )
.withProcedureCallContext( procedureContext )
.context();
}
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 CursorContext cursorContext()
{
return ktx.cursorContext();
}
@Override
public MemoryTracker memoryTracker()
{
return memoryTracker;
}
@Override
public IndexMonitor monitor()
{
return indexingService.getMonitor();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy