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

org.neo4j.consistency.checking.full.MandatoryProperties Maven / Gradle / Ivy

There is a newer version: 5.26.0
Show newest version
/*
 * Copyright (c) 2002-2020 "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.consistency.checking.full;

import org.eclipse.collections.api.iterator.IntIterator;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.api.set.primitive.MutableIntSet;
import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap;
import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet;

import java.util.Arrays;
import java.util.function.Function;

import org.neo4j.consistency.RecordType;
import org.neo4j.consistency.report.ConsistencyReport;
import org.neo4j.consistency.report.ConsistencyReporter;
import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor;
import org.neo4j.internal.kernel.api.schema.RelationTypeSchemaDescriptor;
import org.neo4j.internal.kernel.api.schema.SchemaDescriptor;
import org.neo4j.internal.kernel.api.schema.SchemaProcessor;
import org.neo4j.kernel.impl.store.SchemaStorage;
import org.neo4j.kernel.impl.store.StoreAccess;
import org.neo4j.kernel.impl.store.record.ConstraintRule;
import org.neo4j.kernel.impl.store.record.NodeRecord;
import org.neo4j.kernel.impl.store.record.PrimitiveRecord;
import org.neo4j.kernel.impl.store.record.RelationshipRecord;

import static org.neo4j.helpers.Numbers.safeCastLongToInt;

public class MandatoryProperties
{
    private final MutableIntObjectMap nodes = new IntObjectHashMap<>();
    private final MutableIntObjectMap relationships = new IntObjectHashMap<>();
    private final StoreAccess storeAccess;

    public MandatoryProperties( StoreAccess storeAccess )
    {
        this.storeAccess = storeAccess;
        SchemaStorage schemaStorage = new SchemaStorage( storeAccess.getSchemaStore() );
        for ( ConstraintRule rule : constraintsIgnoringMalformed( schemaStorage ) )
        {
            if ( rule.getConstraintDescriptor().enforcesPropertyExistence() )
            {
                rule.schema().processWith( constraintRecorder );
            }
        }
    }

    private SchemaProcessor constraintRecorder = new SchemaProcessor()
    {
        @Override
        public void processSpecific( LabelSchemaDescriptor schema )
        {
            for ( int propertyId : schema.getPropertyIds() )
            {
                recordConstraint( schema.getLabelId(), propertyId, nodes );
            }
        }

        @Override
        public void processSpecific( RelationTypeSchemaDescriptor schema )
        {
            for ( int propertyId : schema.getPropertyIds() )
            {
                recordConstraint( schema.getRelTypeId(), propertyId, relationships );
            }
        }

        @Override
        public void processSpecific( SchemaDescriptor schema )
        {
            throw new IllegalStateException( "General SchemaDescriptors cannot support constraints" );
        }
    };

    public Function> forNodes(
            final ConsistencyReporter reporter )
    {
        return node ->
        {
            MutableIntSet keys = null;
            for ( long labelId : NodeLabelReader.getListOfLabels( node, storeAccess.getNodeDynamicLabelStore() ) )
            {
                // labelId _is_ actually an int. A technical detail in the store format has these come in a long[]
                int[] propertyKeys = nodes.get( safeCastLongToInt( labelId ) );
                if ( propertyKeys != null )
                {
                    if ( keys == null )
                    {
                        keys = new IntHashSet( 16 );
                    }
                    for ( int key : propertyKeys )
                    {
                        keys.add( key );
                    }
                }
            }
            return keys != null
                    ? new RealCheck<>( node, ConsistencyReport.NodeConsistencyReport.class, reporter,
                            RecordType.NODE, keys )
                    : MandatoryProperties.noCheck();
        };
    }

    public Function>
            forRelationships( final ConsistencyReporter reporter )
    {
        return relationship ->
        {
            int[] propertyKeys = relationships.get( relationship.getType() );
            if ( propertyKeys != null )
            {
                final MutableIntSet keys = new IntHashSet( propertyKeys.length );
                for ( int key : propertyKeys )
                {
                    keys.add( key );
                }
                return new RealCheck<>( relationship, ConsistencyReport.RelationshipConsistencyReport.class,
                        reporter, RecordType.RELATIONSHIP, keys );
            }
            return noCheck();
        };
    }

    private Iterable constraintsIgnoringMalformed( SchemaStorage schemaStorage )
    {
        return schemaStorage::constraintsGetAllIgnoreMalformed;
    }

    private static void recordConstraint( int labelOrRelType, int propertyKey, MutableIntObjectMap storage )
    {
        int[] propertyKeys = storage.get( labelOrRelType );
        if ( propertyKeys == null )
        {
            propertyKeys = new int[]{propertyKey};
        }
        else
        {
            propertyKeys = Arrays.copyOf( propertyKeys, propertyKeys.length + 1 );
            propertyKeys[propertyKeys.length - 1] = propertyKey;
        }
        storage.put( labelOrRelType, propertyKeys );
    }

    public interface Check
            extends AutoCloseable
    {
        void receive( int[] keys );

        @Override
        void close();
    }

    @SuppressWarnings( "unchecked" )
    private static  Check noCheck()
    {
        return NONE;
    }

    @SuppressWarnings( "rawtypes" )
    private static final Check NONE = new Check()
    {
        @Override
        public void receive( int[] keys )
        {
        }

        @Override
        public void close()
        {
        }

        @Override
        public String toString()
        {
            return "NONE";
        }
    };

    private static class RealCheck
            implements Check
    {
        private final RECORD record;
        private final MutableIntSet mandatoryKeys;
        private final Class reportClass;
        private final ConsistencyReporter reporter;
        private final RecordType recordType;

        RealCheck( RECORD record, Class reportClass, ConsistencyReporter reporter, RecordType recordType,
            MutableIntSet mandatoryKeys )
        {
            this.record = record;
            this.reportClass = reportClass;
            this.reporter = reporter;
            this.recordType = recordType;
            this.mandatoryKeys = mandatoryKeys;
        }

        @Override
        public void receive( int[] keys )
        {
            for ( int key : keys )
            {
                mandatoryKeys.remove( key );
            }
        }

        @Override
        public void close()
        {
            if ( !mandatoryKeys.isEmpty() )
            {
                for ( IntIterator key = mandatoryKeys.intIterator(); key.hasNext(); )
                {
                    reporter.report( record, reportClass, recordType ).missingMandatoryProperty( key.next() );
                }
            }
        }

        @Override
        public String toString()
        {
            return "Mandatory properties: " + mandatoryKeys;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy