diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/core/GraphPropertiesProxy.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/core/GraphPropertiesProxy.java index 27069b832c7a..a9a93036f321 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/core/GraphPropertiesProxy.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/core/GraphPropertiesProxy.java @@ -20,23 +20,25 @@ package org.neo4j.kernel.impl.core; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; -import org.neo4j.collection.primitive.PrimitiveIntIterator; import org.neo4j.graphdb.ConstraintViolationException; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.NotFoundException; +import org.neo4j.graphdb.TransactionTerminatedException; +import org.neo4j.internal.kernel.api.PropertyCursor; +import org.neo4j.internal.kernel.api.TokenRead; import org.neo4j.internal.kernel.api.exceptions.InvalidTransactionTypeKernelException; import org.neo4j.internal.kernel.api.exceptions.PropertyKeyIdNotFoundKernelException; -import org.neo4j.kernel.api.ReadOperations; -import org.neo4j.kernel.api.Statement; -import org.neo4j.kernel.api.StatementTokenNameLookup; -import org.neo4j.kernel.api.exceptions.PropertyNotFoundException; import org.neo4j.internal.kernel.api.exceptions.schema.IllegalTokenNameException; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.Statement; +import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.impl.api.operations.KeyReadOperations; -import org.neo4j.storageengine.api.EntityType; import org.neo4j.values.storable.Value; import org.neo4j.values.storable.Values; @@ -65,11 +67,23 @@ public boolean hasProperty( String key ) return false; } - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + int propertyKey = transaction.tokenRead().propertyKey( key ); + if ( propertyKey == KeyReadOperations.NO_SUCH_PROPERTY_KEY ) { - int propertyKeyId = statement.readOperations().propertyKeyGetForName( key ); - return statement.readOperations().graphHasProperty( propertyKeyId ); + return false; } + + PropertyCursor properties = transaction.propertyCursor(); + transaction.dataRead().graphProperties( properties ); + while ( properties.next() ) + { + if ( propertyKey == properties.propertyKey() ) + { + return true; + } + } + return false; } @Override @@ -79,32 +93,29 @@ public Object getProperty( String key ) { throw new IllegalArgumentException( "(null) property key is not allowed" ); } - - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + int propertyKey = transaction.tokenRead().propertyKey( key ); + if ( propertyKey == KeyReadOperations.NO_SUCH_PROPERTY_KEY ) { - try - { - int propertyKeyId = statement.readOperations().propertyKeyGetForName( key ); - if ( propertyKeyId == KeyReadOperations.NO_SUCH_PROPERTY_KEY ) - { - throw new NotFoundException( format( "No such property, '%s'.", key ) ); - } + throw new NotFoundException( format( "No such property, '%s'.", key ) ); + } - Value value = statement.readOperations().graphGetProperty( propertyKeyId ); + PropertyCursor properties = transaction.propertyCursor(); + transaction.dataRead().graphProperties( properties ); + while ( properties.next() ) + { + if ( propertyKey == properties.propertyKey() ) + { + Value value = properties.propertyValue(); if ( value == Values.NO_VALUE ) { - throw new PropertyNotFoundException( propertyKeyId, EntityType.GRAPH, -1 ); + throw new NotFoundException( format( "No such property, '%s'.", key ) ); } - return value.asObjectCopy(); } - catch ( PropertyNotFoundException e ) - { - throw new NotFoundException( - e.getUserMessage( new StatementTokenNameLookup( statement.readOperations() ) ), e ); - } } + throw new NotFoundException( format( "No such property, '%s'.", key ) ); } @Override @@ -114,36 +125,43 @@ public Object getProperty( String key, Object defaultValue ) { throw new IllegalArgumentException( "(null) property key is not allowed" ); } - - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + PropertyCursor properties = transaction.propertyCursor(); + int propertyKey = transaction.tokenRead().propertyKey( key ); + if ( propertyKey == KeyReadOperations.NO_SUCH_PROPERTY_KEY ) + { + return defaultValue; + } + transaction.dataRead().graphProperties( properties ); + while ( properties.next() ) { - int propertyKeyId = statement.readOperations().propertyKeyGetForName( key ); - Value value = statement.readOperations().graphGetProperty( propertyKeyId ); - return value == Values.NO_VALUE ? defaultValue : value.asObjectCopy(); + if ( propertyKey == properties.propertyKey() ) + { + Value value = properties.propertyValue(); + return value == Values.NO_VALUE ? defaultValue : value.asObjectCopy(); + } } + return defaultValue; } @Override public void setProperty( String key, Object value ) { - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + int propertyKeyId; + try { - int propertyKeyId = statement.tokenWriteOperations().propertyKeyGetOrCreateForName( key ); - try - { - statement.dataWriteOperations().graphSetProperty( propertyKeyId, Values.of( value, false ) ); - } - catch ( IllegalArgumentException e ) - { - // Trying to set an illegal value is a critical error - fail this transaction - actions.failTransaction(); - throw e; - } + propertyKeyId = transaction.tokenWrite().propertyKeyGetOrCreateForName( key ); } catch ( IllegalTokenNameException e ) { throw new IllegalArgumentException( format( "Invalid property key '%s'.", key ), e ); } + + try ( Statement ignore = transaction.acquireStatement() ) + { + transaction.dataWrite().graphSetProperty( propertyKeyId, Values.of( value, false ) ); + } catch ( InvalidTransactionTypeKernelException e ) { throw new ConstraintViolationException( e.getMessage(), e ); @@ -153,15 +171,20 @@ public void setProperty( String key, Object value ) @Override public Object removeProperty( String key ) { - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + int propertyKeyId; + try { - int propertyKeyId = statement.tokenWriteOperations().propertyKeyGetOrCreateForName( key ); - return statement.dataWriteOperations().graphRemoveProperty( propertyKeyId ).asObjectCopy(); + propertyKeyId = transaction.tokenWrite().propertyKeyGetOrCreateForName( key ); } catch ( IllegalTokenNameException e ) { throw new IllegalArgumentException( format( "Invalid property key '%s'.", key ), e ); } + try ( Statement ignore = transaction.acquireStatement() ) + { + return transaction.dataWrite().graphRemoveProperty( propertyKeyId ).asObjectCopy(); + } catch ( InvalidTransactionTypeKernelException e ) { throw new ConstraintViolationException( e.getMessage(), e ); @@ -171,62 +194,97 @@ public Object removeProperty( String key ) @Override public Iterable getPropertyKeys() { - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + List keys = new ArrayList<>(); + try { - List keys = new ArrayList<>(); - PrimitiveIntIterator properties = statement.readOperations().graphGetPropertyKeys(); - while ( properties.hasNext() ) + PropertyCursor properties = transaction.propertyCursor(); + TokenRead token = transaction.tokenRead(); + transaction.dataRead().graphProperties( properties ); + while ( properties.next() ) { - keys.add( statement.readOperations().propertyKeyGetName( properties.next() ) ); + keys.add( token.propertyKeyName( properties.propertyKey() ) ); } - return keys; } catch ( PropertyKeyIdNotFoundKernelException e ) { throw new IllegalStateException( "Property key retrieved through kernel API should exist.", e ); } + return keys; } @Override - public Map getProperties( String... names ) + public Map getProperties( String... names ) { - try ( Statement statement = actions.statement() ) + Objects.requireNonNull( names, "Properties keys should be not null array." ); + + if ( names.length == 0 ) { - Map properties = new HashMap<>(); - ReadOperations readOperations = statement.readOperations(); - for ( String name : names ) + return Collections.emptyMap(); + } + + KernelTransaction transaction = safeAcquireTransaction(); + + int itemsToReturn = names.length; + Map properties = new HashMap<>( itemsToReturn ); + TokenRead token = transaction.tokenRead(); + + //Find ids, note we are betting on that the number of keys + //is small enough not to use a set here. + int[] propertyIds = new int[itemsToReturn]; + for ( int i = 0; i < itemsToReturn; i++ ) + { + String key = names[i]; + if ( key == null ) + { + throw new NullPointerException( String.format( "Key %d was null", i ) ); + } + propertyIds[i] = token.propertyKey( key ); + } + + PropertyCursor propertyCursor = transaction.propertyCursor(); + transaction.dataRead().graphProperties( propertyCursor ); + int propertiesToFind = itemsToReturn; + while ( propertiesToFind > 0 && propertyCursor.next() ) + { + //Do a linear check if this is a property we are interested in. + int currentKey = propertyCursor.propertyKey(); + for ( int i = 0; i < itemsToReturn; i++ ) { - int propertyKeyId = readOperations.propertyKeyGetForName( name ); - Object value = readOperations.graphGetProperty( propertyKeyId ); - if ( value != null ) + if ( propertyIds[i] == currentKey ) { - properties.put( name, value ); + properties.put( names[i], + propertyCursor.propertyValue().asObjectCopy() ); + propertiesToFind--; + break; } } - return properties; } + return properties; } @Override public Map getAllProperties() { - try ( Statement statement = actions.statement() ) + KernelTransaction transaction = safeAcquireTransaction(); + Map properties = new HashMap<>(); + + try { - Map properties = new HashMap<>(); - ReadOperations readOperations = statement.readOperations(); - PrimitiveIntIterator propertyKeys = readOperations.graphGetPropertyKeys(); - while ( propertyKeys.hasNext() ) + PropertyCursor propertyCursor = transaction.propertyCursor(); + TokenRead token = transaction.tokenRead(); + transaction.dataRead().graphProperties( propertyCursor ); + while ( propertyCursor.next() ) { - int propertyKeyId = propertyKeys.next(); - properties.put( readOperations.propertyKeyGetName( propertyKeyId ), - readOperations.graphGetProperty( propertyKeyId ).asObjectCopy() ); + properties.put( token.propertyKeyName( propertyCursor.propertyKey() ), + propertyCursor.propertyValue().asObjectCopy() ); } - return properties; } catch ( PropertyKeyIdNotFoundKernelException e ) { throw new IllegalStateException( "Property key retrieved through kernel API should exist.", e ); } + return properties; } @Override @@ -243,4 +301,15 @@ public int hashCode() { return actions.getGraphDatabase().hashCode(); } + + private KernelTransaction safeAcquireTransaction() + { + org.neo4j.kernel.api.KernelTransaction transaction = actions.kernelTransaction(); + if ( transaction.isTerminated() ) + { + Status terminationReason = transaction.getReasonIfTerminated().orElse( Status.Transaction.Terminated ); + throw new TransactionTerminatedException( terminationReason ); + } + return transaction; + } }