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

org.neo4j.kernel.impl.transaction.command.Command Maven / Gradle / Ivy

Go to download

Neo4j kernel is a lightweight, embedded Java database designed to store data structured as graphs rather than tables. For more information, see http://neo4j.org.

There is a newer version: 2025.02.0
Show newest version
/*
 * Copyright (c) 2002-2015 "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 .
 */
package org.neo4j.kernel.impl.transaction.command;

import java.io.IOException;
import java.util.Collection;

import org.neo4j.kernel.impl.store.record.AbstractBaseRecord;
import org.neo4j.kernel.impl.store.record.DynamicRecord;
import org.neo4j.kernel.impl.store.record.LabelTokenRecord;
import org.neo4j.kernel.impl.store.record.NeoStoreRecord;
import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.PropertyKeyTokenRecord;
import org.neo4j.kernel.impl.store.record.PropertyRecord;
import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord;
import org.neo4j.kernel.impl.store.record.RelationshipTypeTokenRecord;
import org.neo4j.kernel.impl.store.record.SchemaRule;
import org.neo4j.kernel.impl.store.record.TokenRecord;
import org.neo4j.kernel.impl.transaction.state.PropertyRecordChange;

import static java.lang.String.format;
import static java.util.Collections.unmodifiableCollection;

import static org.neo4j.helpers.collection.IteratorUtil.first;
import static org.neo4j.kernel.impl.util.IdPrettyPrinter.label;
import static org.neo4j.kernel.impl.util.IdPrettyPrinter.relationshipType;

/**
 * Command implementations for all the commands that can be performed on a Neo
 * store.
 */
public abstract class Command
{
    private int keyHash;
    private long key;
    private Mode mode;

    /*
     * TODO: This is techdebt
     * This is used to control the order of how commands are applied, which is done because
     * we don't take read locks, and so the order or how we change things lowers the risk
     * of reading invalid state. This should be removed once eg. MVCC or read locks has been
     * implemented.
     */
    public enum Mode
    {
        CREATE,
        UPDATE,
        DELETE;

        public static Mode fromRecordState( boolean created, boolean inUse )
        {
            if ( !inUse )
            {
                return DELETE;
            }
            if ( created )
            {
                return CREATE;
            }
            return UPDATE;
        }

        public static Mode fromRecordState( AbstractBaseRecord record )
        {
            return fromRecordState( record.isCreated(), record.inUse() );
        }
    }

    protected final void setup( long key, Mode mode )
    {
        this.mode = mode;
        this.keyHash = (int) (( key >>> 32 ) ^ key );
        this.key = key;
    }

    @Override
    public int hashCode()
    {
        return keyHash;
    }

    // Force implementors to implement toString
    @Override
    public abstract String toString();

    public long getKey()
    {
        return key;
    }

    public Mode getMode()
    {
        return mode;
    }

    @Override
    public boolean equals( Object o )
    {
        return o != null && o.getClass().equals( getClass() ) && getKey() == ((Command) o).getKey();
    }

    public abstract boolean handle( CommandHandler handler ) throws IOException;

    protected String beforeAndAfterToString( AbstractBaseRecord before, AbstractBaseRecord after )
    {
        return format( " -%s%n         +%s", before, after );
    }

    public static class NodeCommand extends Command
    {
        private NodeRecord before;
        private NodeRecord after;

        public NodeCommand init( NodeRecord before, NodeRecord after )
        {
            setup( after.getId(), Mode.fromRecordState( after ) );
            this.before = before;
            this.after = after;
            return this;
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitNodeCommand( this );
        }

        @Override
        public String toString()
        {
            return beforeAndAfterToString( before, after );
        }

        public NodeRecord getBefore()
        {
            return before;
        }

        public NodeRecord getAfter()
        {
            return after;
        }
    }

    public static class RelationshipCommand extends Command
    {
        private RelationshipRecord record;

        public RelationshipCommand init( RelationshipRecord record )
        {
            setup( record.getId(), Mode.fromRecordState( record ) );
            this.record = record;
            return this;
        }

        @Override
        public String toString()
        {
            return record.toString();
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitRelationshipCommand( this );
        }

        public RelationshipRecord getRecord()
        {
            return record;
        }
    }

    public static class RelationshipGroupCommand extends Command
    {
        private RelationshipGroupRecord record;

        public RelationshipGroupCommand init( RelationshipGroupRecord record )
        {
            setup( record.getId(), Mode.fromRecordState( record ) );
            this.record = record;
            return this;
        }

        @Override
        public String toString()
        {
            return record.toString();
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitRelationshipGroupCommand( this );
        }

        public RelationshipGroupRecord getRecord()
        {
            return record;
        }
    }

    public static class NeoStoreCommand extends Command
    {
        private NeoStoreRecord record;

        public NeoStoreCommand init( NeoStoreRecord record )
        {
            if( record != null )
            {
                setup( record.getId(), Mode.fromRecordState( record ) );
            }
            this.record = record;
            return this;
        }

        @Override
        public String toString()
        {
            return record.toString();
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitNeoStoreCommand( this );
        }

        public NeoStoreRecord getRecord()
        {
            return record;
        }
    }

    public static class PropertyKeyTokenCommand extends TokenCommand
    {

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitPropertyKeyTokenCommand( this );
        }
    }

    public static class PropertyCommand extends Command implements PropertyRecordChange
    {
        private PropertyRecord before;
        private PropertyRecord after;

        // TODO as optimization the deserialized key/values could be passed in here
        // so that the cost of deserializing them only applies in recovery/HA
        public PropertyCommand init( PropertyRecord before, PropertyRecord after )
        {
            setup( after.getId(), Mode.fromRecordState( after ) );
            this.before = before;
            this.after = after;
            return this;
        }

        @Override
        public String toString()
        {
            return beforeAndAfterToString( before, after );
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitPropertyCommand( this );
        }

        @Override
        public PropertyRecord getBefore()
        {
            return before;
        }

        @Override
        public PropertyRecord getAfter()
        {
            return after;
        }

        public long getNodeId()
        {
            return after.getNodeId();
        }

        public long getRelId()
        {
            return after.getRelId();
        }
    }

    public static abstract class TokenCommand extends Command
    {
        protected RECORD record;

        public TokenCommand init( RECORD record )
        {
            setup( record.getId(), Mode.fromRecordState( record ) );
            this.record = record;
            return this;
        }

        public RECORD getRecord()
        {
            return record;
        }

        @Override
        public String toString()
        {
            return record.toString();
        }
    }

    public static class RelationshipTypeTokenCommand extends TokenCommand
    {

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitRelationshipTypeTokenCommand( this );
        }
    }

    public static class LabelTokenCommand extends TokenCommand
    {

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitLabelTokenCommand( this );
        }
    }

    public static class SchemaRuleCommand extends Command
    {
        private Collection recordsBefore;
        private Collection recordsAfter;
        private SchemaRule schemaRule;

        public SchemaRuleCommand init( Collection recordsBefore,
                           Collection recordsAfter, SchemaRule schemaRule )
        {
            setup( first( recordsAfter ).getId(), Mode.fromRecordState( first( recordsAfter ) ) );
            this.recordsBefore = recordsBefore;
            this.recordsAfter = recordsAfter;
            this.schemaRule = schemaRule;
            return this;
        }

        @Override
        public String toString()
        {
            if ( schemaRule != null )
            {
                return getMode() + ":" + schemaRule.toString();
            }
            return "SchemaRule" + recordsAfter;
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitSchemaRuleCommand( this );
        }

        public Collection getRecordsAfter()
        {
            return unmodifiableCollection( recordsAfter );
        }

        public SchemaRule getSchemaRule()
        {
            return schemaRule;
        }

        public Collection getRecordsBefore()
        {
            return recordsBefore;
        }
    }

    public static class NodeCountsCommand extends Command
    {
        private int labelId;
        private long delta;

        public NodeCountsCommand init( int labelId, long delta )
        {
            setup( labelId, Mode.UPDATE );
            assert delta != 0 : "Tried to create a NodeCountsCommand for something that didn't change any count";
            this.labelId = labelId;
            this.delta = delta;
            return this;
        }

        @Override
        public String toString()
        {
            return String.format( "UpdateCounts[(%s) %s %d]",
                                  label( labelId ), delta < 0 ? "-" : "+", Math.abs( delta ) );
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitNodeCountsCommand( this );
        }

        public int labelId()
        {
            return labelId;
        }

        public long delta()
        {
            return delta;
        }
    }

    public static class RelationshipCountsCommand extends Command
    {
        private int startLabelId;
        private int typeId;
        private int endLabelId;
        private long delta;

        public RelationshipCountsCommand init( int startLabelId, int typeId, int endLabelId, long delta )
        {
            setup( typeId, Mode.UPDATE );
            assert delta != 0 : "Tried to create a RelationshipCountsCommand for something that didn't change any count";
            this.startLabelId = startLabelId;
            this.typeId = typeId;
            this.endLabelId = endLabelId;
            this.delta = delta;
            return this;
        }

        @Override
        public String toString()
        {
            return String.format( "UpdateCounts[(%s)-%s->(%s) %s %d]",
                    label( startLabelId ), relationshipType( typeId ), label( endLabelId ),
                    delta < 0 ? "-" : "+", Math.abs( delta ) );
        }

        @Override
        public boolean handle( CommandHandler handler ) throws IOException
        {
            return handler.visitRelationshipCountsCommand( this );
        }

        public int startLabelId()
        {
            return startLabelId;
        }

        public int typeId()
        {
            return typeId;
        }

        public int endLabelId()
        {
            return endLabelId;
        }

        public long delta()
        {
            return delta;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy