/** * Copyright (c) 2002-2013 "Neo Technology," * Network Engine for Objects in Lund AB [http://neotechnology.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 <http://www.gnu.org/licenses/>. */ package org.neo4j.kernel.impl.nioneo.xa; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.transaction.xa.XAResource; import org.neo4j.helpers.Pair; import org.neo4j.kernel.impl.core.PropertyIndex; import org.neo4j.kernel.impl.nioneo.store.InvalidRecordException; import org.neo4j.kernel.impl.nioneo.store.NeoStore; import org.neo4j.kernel.impl.nioneo.store.NodeStore; import org.neo4j.kernel.impl.nioneo.store.PropertyBlock; import org.neo4j.kernel.impl.nioneo.store.PropertyData; import org.neo4j.kernel.impl.nioneo.store.PropertyIndexData; import org.neo4j.kernel.impl.nioneo.store.PropertyIndexRecord; import org.neo4j.kernel.impl.nioneo.store.PropertyIndexStore; import org.neo4j.kernel.impl.nioneo.store.PropertyRecord; import org.neo4j.kernel.impl.nioneo.store.PropertyStore; import org.neo4j.kernel.impl.nioneo.store.Record; import org.neo4j.kernel.impl.nioneo.store.RelationshipRecord; import org.neo4j.kernel.impl.nioneo.store.RelationshipStore; import org.neo4j.kernel.impl.nioneo.store.RelationshipTypeData; import org.neo4j.kernel.impl.persistence.NeoStoreTransaction; import org.neo4j.kernel.impl.transaction.xaframework.XaConnection; import org.neo4j.kernel.impl.util.ArrayMap; import org.neo4j.kernel.impl.util.RelIdArray; import org.neo4j.kernel.impl.util.RelIdArray.DirectionWrapper; class ReadTransaction implements NeoStoreTransaction { private final NeoStore neoStore; public ReadTransaction( NeoStore neoStore ) { this.neoStore = neoStore; } private NodeStore getNodeStore() { return neoStore.getNodeStore(); } private int getRelGrabSize() { return neoStore.getRelationshipGrabSize(); } private RelationshipStore getRelationshipStore() { return neoStore.getRelationshipStore(); } private PropertyStore getPropertyStore() { return neoStore.getPropertyStore(); } @Override public boolean nodeLoadLight( long nodeId ) { return getNodeStore().loadLightNode( nodeId ); } @Override public RelationshipRecord relLoadLight( long id ) { return getRelationshipStore().getLightRel( id ); } @Override public long getRelationshipChainPosition( long nodeId ) { return getNodeStore().getRecord( nodeId ).getNextRel(); } @Override public Pair<Map<DirectionWrapper, Iterable<RelationshipRecord>>, Long> getMoreRelationships( long nodeId, long position ) { return getMoreRelationships( nodeId, position, getRelGrabSize(), getRelationshipStore() ); } static Pair<Map<DirectionWrapper, Iterable<RelationshipRecord>>, Long> getMoreRelationships( long nodeId, long position, int grabSize, RelationshipStore relStore ) { // initialCapacity=grabSize saves the lists the trouble of resizing List<RelationshipRecord> out = new ArrayList<RelationshipRecord>(); List<RelationshipRecord> in = new ArrayList<RelationshipRecord>(); List<RelationshipRecord> loop = null; Map<DirectionWrapper, Iterable<RelationshipRecord>> result = new EnumMap<DirectionWrapper, Iterable<RelationshipRecord>>( DirectionWrapper.class ); result.put( DirectionWrapper.OUTGOING, out ); result.put( DirectionWrapper.INCOMING, in ); for ( int i = 0; i < grabSize && position != Record.NO_NEXT_RELATIONSHIP.intValue(); i++ ) { RelationshipRecord relRecord = relStore.getChainRecord( position ); if ( relRecord == null ) { // return what we got so far return Pair.of( result, position ); } long firstNode = relRecord.getFirstNode(); long secondNode = relRecord.getSecondNode(); if ( relRecord.inUse() ) { if ( firstNode == secondNode ) { if ( loop == null ) { // This is done lazily because loops are probably quite // rarely encountered loop = new ArrayList<RelationshipRecord>(); result.put( DirectionWrapper.BOTH, loop ); } loop.add( relRecord ); } else if ( firstNode == nodeId ) { out.add( relRecord ); } else if ( secondNode == nodeId ) { in.add( relRecord ); } } else { i--; } if ( firstNode == nodeId ) { position = relRecord.getFirstNextRel(); } else if ( secondNode == nodeId ) { position = relRecord.getSecondNextRel(); } else { throw new InvalidRecordException( "Node[" + nodeId + "] is neither firstNode[" + firstNode + "] nor secondNode[" + secondNode + "] for Relationship[" + relRecord.getId() + "]" ); } } return Pair.of( result, position ); } static List<PropertyRecord> getPropertyRecordChain( PropertyStore propertyStore, long nextProp ) { List<PropertyRecord> toReturn = new LinkedList<PropertyRecord>(); if ( nextProp == Record.NO_NEXT_PROPERTY.intValue() ) { return null; } while ( nextProp != Record.NO_NEXT_PROPERTY.intValue() ) { PropertyRecord propRecord = propertyStore.getLightRecord( nextProp ); toReturn.add(propRecord); nextProp = propRecord.getNextProp(); } return toReturn; } static ArrayMap<Integer, PropertyData> propertyChainToMap( Collection<PropertyRecord> chain ) { if ( chain == null ) { return null; } ArrayMap<Integer, PropertyData> propertyMap = new ArrayMap<Integer, PropertyData>( chain.size(), false, true ); for ( PropertyRecord propRecord : chain ) { for ( PropertyBlock propBlock : propRecord.getPropertyBlocks() ) { propertyMap.put( propBlock.getKeyIndexId(), propBlock.newPropertyData( propRecord ) ); } } return propertyMap; } static ArrayMap<Integer, PropertyData> loadProperties( PropertyStore propertyStore, long nextProp ) { Collection<PropertyRecord> chain = getPropertyRecordChain( propertyStore, nextProp ); if ( chain == null ) { return null; } return propertyChainToMap( chain ); } @Override public ArrayMap<Integer,PropertyData> relLoadProperties( long relId, boolean light ) { RelationshipRecord relRecord = getRelationshipStore().getRecord( relId ); if ( !relRecord.inUse() ) { throw new InvalidRecordException( "Relationship[" + relId + "] not in use" ); } return loadProperties( getPropertyStore(), relRecord.getNextProp() ); } @Override public ArrayMap<Integer,PropertyData> nodeLoadProperties( long nodeId, boolean light ) { return loadProperties( getPropertyStore(), getNodeStore().getRecord( nodeId ).getNextProp() ); } // Duplicated code public Object propertyGetValueOrNull( PropertyBlock propertyBlock ) { return propertyBlock.getType().getValue( propertyBlock, null ); } @Override public Object loadPropertyValue( PropertyData property ) { PropertyRecord propertyRecord = getPropertyStore().getRecord( property.getId() ); PropertyBlock propertyBlock = propertyRecord.getPropertyBlock( property.getIndex() ); if ( propertyBlock.isLight() ) { getPropertyStore().makeHeavy( propertyBlock ); } return propertyBlock.getType().getValue( propertyBlock, getPropertyStore() ); } @Override public String loadIndex( int id ) { PropertyIndexStore indexStore = getPropertyStore().getIndexStore(); PropertyIndexRecord index = indexStore.getRecord( id ); if ( index.isLight() ) { indexStore.makeHeavy( index ); } return indexStore.getStringFor( index ); } @Override public PropertyIndexData[] loadPropertyIndexes( int count ) { PropertyIndexStore indexStore = getPropertyStore().getIndexStore(); return indexStore.getPropertyIndexes( count ); } /* @Override public int getKeyIdForProperty( long propertyId ) { PropertyRecord propRecord = getPropertyStore().getLightRecord( propertyId ); return propRecord.getKeyIndexId(); } */ @Override public void setXaConnection( XaConnection connection ) { } @Override public XAResource getXAResource() { throw readOnlyException(); } private IllegalStateException readOnlyException() { return new IllegalStateException( "This is a read only transaction, " + "this method should never be invoked" ); } @Override public void destroy() { throw readOnlyException(); } @Override public ArrayMap<Integer, PropertyData> nodeDelete( long nodeId ) { throw readOnlyException(); } @Override public PropertyData nodeAddProperty( long nodeId, PropertyIndex index, Object value ) { throw readOnlyException(); } @Override public PropertyData nodeChangeProperty( long nodeId, PropertyData data, Object value ) { throw readOnlyException(); } @Override public void nodeRemoveProperty( long nodeId, PropertyData data ) { throw readOnlyException(); } @Override public void nodeCreate( long id ) { throw readOnlyException(); } @Override public void relationshipCreate( long id, int typeId, long startNodeId, long endNodeId ) { throw readOnlyException(); } @Override public ArrayMap<Integer, PropertyData> relDelete( long relId ) { throw readOnlyException(); } @Override public PropertyData relAddProperty( long relId, PropertyIndex index, Object value ) { throw readOnlyException(); } @Override public PropertyData relChangeProperty( long relId, PropertyData data, Object value ) { throw readOnlyException(); } @Override public void relRemoveProperty( long relId, PropertyData data ) { throw readOnlyException(); } @Override public RelationshipTypeData[] loadRelationshipTypes() { RelationshipTypeData relTypeData[] = neoStore.getRelationshipTypeStore().getRelationshipTypes(); RelationshipTypeData rawRelTypeData[] = new RelationshipTypeData[relTypeData.length]; for ( int i = 0; i < relTypeData.length; i++ ) { rawRelTypeData[i] = new RelationshipTypeData( relTypeData[i].getId(), relTypeData[i].getName() ); } return rawRelTypeData; } @Override public void createPropertyIndex( String key, int id ) { throw readOnlyException(); } @Override public void createRelationshipType( int id, String name ) { throw readOnlyException(); } @Override public RelIdArray getCreatedNodes() { return RelIdArray.EMPTY; } @Override public boolean isNodeCreated( long nodeId ) { return false; } @Override public boolean isRelationshipCreated( long relId ) { return false; } public static int getKeyIdForProperty( PropertyData property, PropertyStore store ) { // PropertyRecord propRecord = store.getLightRecord( property.getId() ); // return propRecord.getKeyIndexIds(); return property.getIndex(); } @Override public int getKeyIdForProperty( PropertyData property ) { return getKeyIdForProperty( property, getPropertyStore() ); } }