/** * 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.store; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.neo4j.kernel.IdGeneratorFactory; import org.neo4j.kernel.IdType; /** * Implementation of the relationship store. */ public class RelationshipStore extends AbstractStore implements Store { public static final String TYPE_DESCRIPTOR = "RelationshipStore"; // record header size // directed|in_use(byte)+first_node(int)+second_node(int)+rel_type(int)+ // first_prev_rel_id(int)+first_next_rel_id+second_prev_rel_id(int)+ // second_next_rel_id+next_prop_id(int) public static final int RECORD_SIZE = 33; /** * See {@link AbstractStore#AbstractStore(String, Map)} */ public RelationshipStore( String fileName, Map<?,?> config ) { super( fileName, config, IdType.RELATIONSHIP ); } public String getTypeDescriptor() { return TYPE_DESCRIPTOR; } public int getRecordSize() { return RECORD_SIZE; } @Override public void close() { super.close(); } /** * Creates a new relationship store contained in <CODE>fileName</CODE> If * filename is <CODE>null</CODE> or the file already exists an <CODE>IOException</CODE> * is thrown. * * @param fileName * File name of the new relationship store * @throws IOException * If unable to create relationship store or name null */ public static void createStore( String fileName, IdGeneratorFactory idGeneratorFactory ) { createEmptyStore( fileName, buildTypeDescriptorAndVersion( TYPE_DESCRIPTOR ), idGeneratorFactory ); } public RelationshipRecord getRecord( long id ) { PersistenceWindow window = acquireWindow( id, OperationType.READ ); try { RelationshipRecord record = getRecord( id, window, false ); return record; } finally { releaseWindow( window ); } } public RelationshipRecord getLightRel( long id ) { PersistenceWindow window = null; try { window = acquireWindow( id, OperationType.READ ); } catch ( InvalidRecordException e ) { // ok to high id return null; } try { RelationshipRecord record = getRecord( id, window, true ); return record; } finally { releaseWindow( window ); } } public void updateRecord( RelationshipRecord record, boolean recovered ) { assert recovered; setRecovered(); try { updateRecord( record ); registerIdFromUpdateRecord( record.getId() ); } finally { unsetRecovered(); } } public void updateRecord( RelationshipRecord record ) { PersistenceWindow window = acquireWindow( record.getId(), OperationType.WRITE ); try { updateRecord( record, window ); } finally { releaseWindow( window ); } } private void updateRecord( RelationshipRecord record, PersistenceWindow window ) { long id = record.getId(); Buffer buffer = window.getOffsettedBuffer( id ); if ( record.inUse() ) { long firstNode = record.getFirstNode(); short firstNodeMod = (short)((firstNode & 0x700000000L) >> 31); long secondNode = record.getSecondNode(); long secondNodeMod = (secondNode & 0x700000000L) >> 4; long firstPrevRel = record.getFirstPrevRel(); long firstPrevRelMod = firstPrevRel == Record.NO_NEXT_RELATIONSHIP.intValue() ? 0 : (firstPrevRel & 0x700000000L) >> 7; long firstNextRel = record.getFirstNextRel(); long firstNextRelMod = firstNextRel == Record.NO_NEXT_RELATIONSHIP.intValue() ? 0 : (firstNextRel & 0x700000000L) >> 10; long secondPrevRel = record.getSecondPrevRel(); long secondPrevRelMod = secondPrevRel == Record.NO_NEXT_RELATIONSHIP.intValue() ? 0 : (secondPrevRel & 0x700000000L) >> 13; long secondNextRel = record.getSecondNextRel(); long secondNextRelMod = secondNextRel == Record.NO_NEXT_RELATIONSHIP.intValue() ? 0 : (secondNextRel & 0x700000000L) >> 16; long nextProp = record.getNextProp(); long nextPropMod = nextProp == Record.NO_NEXT_PROPERTY.intValue() ? 0 : (nextProp & 0xF00000000L) >> 28; // [ , x] in use flag // [ ,xxx ] first node high order bits // [xxxx, ] next prop high order bits short inUseUnsignedByte = (short)(Record.IN_USE.byteValue() | firstNodeMod | nextPropMod); // [ xxx, ][ , ][ , ][ , ] second node high order bits, 0x70000000 // [ ,xxx ][ , ][ , ][ , ] first prev rel high order bits, 0xE000000 // [ , x][xx , ][ , ][ , ] first next rel high order bits, 0x1C00000 // [ , ][ xx,x ][ , ][ , ] second prev rel high order bits, 0x380000 // [ , ][ , xxx][ , ][ , ] second next rel high order bits, 0x70000 // [ , ][ , ][xxxx,xxxx][xxxx,xxxx] type int typeInt = (int)(record.getType() | secondNodeMod | firstPrevRelMod | firstNextRelMod | secondPrevRelMod | secondNextRelMod); buffer.put( (byte)inUseUnsignedByte ).putInt( (int) firstNode ).putInt( (int) secondNode ) .putInt( typeInt ).putInt( (int) firstPrevRel ).putInt( (int) firstNextRel ) .putInt( (int) secondPrevRel ).putInt( (int) secondNextRel ).putInt( (int) nextProp ); } else { buffer.put( Record.NOT_IN_USE.byteValue() ); if ( !isInRecoveryMode() ) { freeId( id ); } } } private RelationshipRecord getRecord( long id, PersistenceWindow window, boolean checkInUse ) { Buffer buffer = window.getOffsettedBuffer( id ); // [ , x] in use flag // [ ,xxx ] first node high order bits // [xxxx, ] next prop high order bits long inUseByte = buffer.get(); boolean inUse = (inUseByte & 0x1) == Record.IN_USE.intValue(); if ( !inUse ) { if ( checkInUse ) { return null; } throw new InvalidRecordException( "Record[" + id + "] not in use" ); } long firstNode = buffer.getUnsignedInt(); long firstNodeMod = (inUseByte & 0xEL) << 31; long secondNode = buffer.getUnsignedInt(); // [ xxx, ][ , ][ , ][ , ] second node high order bits, 0x70000000 // [ ,xxx ][ , ][ , ][ , ] first prev rel high order bits, 0xE000000 // [ , x][xx , ][ , ][ , ] first next rel high order bits, 0x1C00000 // [ , ][ xx,x ][ , ][ , ] second prev rel high order bits, 0x380000 // [ , ][ , xxx][ , ][ , ] second next rel high order bits, 0x70000 // [ , ][ , ][xxxx,xxxx][xxxx,xxxx] type long typeInt = buffer.getInt(); long secondNodeMod = (typeInt & 0x70000000L) << 4; int type = (int)(typeInt & 0xFFFF); RelationshipRecord record = new RelationshipRecord( id, longFromIntAndMod( firstNode, firstNodeMod ), longFromIntAndMod( secondNode, secondNodeMod ), type ); record.setInUse( inUse ); long firstPrevRel = buffer.getUnsignedInt(); long firstPrevRelMod = (typeInt & 0xE000000L) << 7; record.setFirstPrevRel( longFromIntAndMod( firstPrevRel, firstPrevRelMod ) ); long firstNextRel = buffer.getUnsignedInt(); long firstNextRelMod = (typeInt & 0x1C00000L) << 10; record.setFirstNextRel( longFromIntAndMod( firstNextRel, firstNextRelMod ) ); long secondPrevRel = buffer.getUnsignedInt(); long secondPrevRelMod = (typeInt & 0x380000L) << 13; record.setSecondPrevRel( longFromIntAndMod( secondPrevRel, secondPrevRelMod ) ); long secondNextRel = buffer.getUnsignedInt(); long secondNextRelMod = (typeInt & 0x70000L) << 16; record.setSecondNextRel( longFromIntAndMod( secondNextRel, secondNextRelMod ) ); long nextProp = buffer.getUnsignedInt(); long nextPropMod = (inUseByte & 0xF0L) << 28; record.setNextProp( longFromIntAndMod( nextProp, nextPropMod ) ); return record; } // private RelationshipRecord getFullRecord( long id, PersistenceWindow window ) // { // Buffer buffer = window.getOffsettedBuffer( id ); // byte inUse = buffer.get(); // boolean inUseFlag = ((inUse & Record.IN_USE.byteValue()) == // Record.IN_USE.byteValue()); // RelationshipRecord record = new RelationshipRecord( id, // buffer.getInt(), buffer.getInt(), buffer.getInt() ); // record.setInUse( inUseFlag ); // record.setFirstPrevRel( buffer.getInt() ); // record.setFirstNextRel( buffer.getInt() ); // record.setSecondPrevRel( buffer.getInt() ); // record.setSecondNextRel( buffer.getInt() ); // record.setNextProp( buffer.getInt() ); // return record; // } public String toString() { return "RelStore"; } public RelationshipRecord getChainRecord( long relId ) { PersistenceWindow window = null; try { window = acquireWindow( relId, OperationType.READ ); } catch ( InvalidRecordException e ) { // ok to high id return null; } try { // return getFullRecord( relId, window ); return getRecord( relId, window, false ); } finally { releaseWindow( window ); } } public List<WindowPoolStats> getAllWindowPoolStats() { List<WindowPoolStats> list = new ArrayList<WindowPoolStats>(); list.add( getWindowPoolStats() ); return list; } }