Skip to content

Commit

Permalink
Value validation for indexed properties.
Browse files Browse the repository at this point in the history
Introduce validation of property values for legacy and schema indexes.
Create validators for empty/null/allowable cases. Perform validation during property updated and index population.
  • Loading branch information
MishaDemianenko committed Nov 23, 2016
1 parent 9e7c009 commit f814082
Show file tree
Hide file tree
Showing 16 changed files with 683 additions and 55 deletions.
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2002-2016 "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.api;

import org.neo4j.kernel.impl.util.Validator;

public class IndexSimpleValueValidator implements Validator
{

public static IndexSimpleValueValidator INSTANCE = new IndexSimpleValueValidator();

private IndexSimpleValueValidator()
{
}

@Override
public void validate( Object value )
{
if ( value == null )
{
throw new IllegalArgumentException( "Null value" );
}
if ( value instanceof String )
{
IndexValueLengthValidator.INSTANCE.validate( ((String) value).getBytes() );
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2002-2016 "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.api;

import org.neo4j.kernel.impl.util.Validator;

public class IndexValueLengthValidator implements Validator<byte[]>
{
// Maximum bytes value length that supported by indexes.
// Absolute hard maximum length for a term, in bytes once
// encoded as UTF8. If a term arrives from the analyzer
// longer than this length, an IllegalArgumentException
// when lucene writer trying to add or update document
private static final int MAX_TERM_LENGTH = (1 << 15) - 2;

public static final IndexValueLengthValidator INSTANCE = new IndexValueLengthValidator();

private IndexValueLengthValidator()
{
}

@Override
public void validate( byte[] bytes )
{
if ( bytes.length > MAX_TERM_LENGTH )
{
throw new IllegalArgumentException( "Property value bytes length: " + bytes.length +
" is longer then " + MAX_TERM_LENGTH + ", which is maximum supported length" +
" of indexed property value." );
}
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2002-2016 "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.api;

import org.neo4j.kernel.api.index.ArrayEncoder;
import org.neo4j.kernel.impl.util.Validator;

public class IndexValueValidator implements Validator
{
public static IndexValueValidator INSTANCE = new IndexValueValidator();

private IndexValueValidator()
{
}

@Override
public void validate( Object value )
{
IndexSimpleValueValidator.INSTANCE.validate( value );
if ( value.getClass().isArray() )
{
IndexValueLengthValidator.INSTANCE.validate( ArrayEncoder.encode( value ).getBytes() );
}
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2002-2016 "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.api;

import org.neo4j.kernel.impl.util.Validator;

public class LegacyIndexValueValidator implements Validator
{
public static LegacyIndexValueValidator INSTANCE = new LegacyIndexValueValidator();

private LegacyIndexValueValidator()
{
}

@Override
public void validate( Object value )
{
IndexSimpleValueValidator.INSTANCE.validate( value );
if ( !(value instanceof Number) && value.toString() == null )
{
throw new IllegalArgumentException( "Value of type " + value.getClass() + " has null toString" );
}
}
}
Expand Up @@ -79,6 +79,7 @@
import org.neo4j.kernel.impl.index.IndexEntityType;
import org.neo4j.kernel.impl.index.LegacyIndexStore;
import org.neo4j.kernel.impl.util.Cursors;
import org.neo4j.kernel.impl.util.Validators;
import org.neo4j.register.Register.DoubleLongRegister;
import org.neo4j.storageengine.api.EntityType;
import org.neo4j.storageengine.api.LabelItem;
Expand Down Expand Up @@ -1117,6 +1118,11 @@ private void indexUpdateProperty( KernelStatement state, long nodeId, int labelI
IndexDescriptor descriptor = indexGetForLabelAndPropertyKey( state, labelId, propertyKey );
if ( descriptor != null )
{
if (after != null)
{
Validators.INDEX_VALUE_VALIDATOR.validate( after.value() );
}

state.txState().indexDoUpdateProperty( descriptor, nodeId, before, after );
}
}
Expand Down
Expand Up @@ -40,6 +40,7 @@
import org.neo4j.kernel.impl.store.record.PropertyBlock;
import org.neo4j.kernel.impl.store.record.PropertyRecord;
import org.neo4j.kernel.impl.store.record.Record;
import org.neo4j.kernel.impl.util.Validators;

import static org.neo4j.collection.primitive.PrimitiveLongCollections.EMPTY_LONG_ARRAY;
import static org.neo4j.kernel.api.labelscan.NodeLabelUpdate.labelChanges;
Expand Down Expand Up @@ -103,7 +104,9 @@ public void process( NodeRecord node ) throws FAILURE
if ( propertyKeyIdFilter.test( propertyKeyId ) )
{
// This node has a property of interest to us
updates.add( propertyKeyId, valueOf( property ), labels );
Object value = valueOf( property );
Validators.INDEX_VALUE_VALIDATOR.validate( value );
updates.add( propertyKeyId, value, labels );
}
}
if ( updates.containsUpdates() )
Expand Down
Expand Up @@ -29,11 +29,18 @@

import org.neo4j.io.fs.DefaultFileSystemAbstraction;
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.kernel.impl.api.IndexValueValidator;
import org.neo4j.kernel.impl.api.LegacyIndexValueValidator;
import org.neo4j.kernel.impl.store.MetaDataStore;
import org.neo4j.kernel.impl.storemigration.StoreFileType;

public class Validators
{

public static final IndexValueValidator INDEX_VALUE_VALIDATOR = IndexValueValidator.INSTANCE;

public static final LegacyIndexValueValidator LEGACY_INDEX_VALUE_VALIDATOR = LegacyIndexValueValidator.INSTANCE;

public static final Validator<File> REGEX_FILE_EXISTS = file ->
{
if ( matchingFiles( file ).isEmpty() )
Expand Down
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2002-2016 "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.api;

import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import org.neo4j.kernel.impl.util.Validator;

import static org.neo4j.kernel.impl.api.IndexSimpleValueValidator.INSTANCE;

public class IndexSimpleValueValidatorTest
{

@Rule
public ExpectedException expectedException = ExpectedException.none();

@Test
public void nullIsNotAllowed() throws Exception
{
expectedException.expect( IllegalArgumentException.class );
expectedException.expectMessage( "Null value" );
getValidator().validate( null );
}

@Test
public void tooLongStringIsNotAllowed()
{
expectedException.expect( IllegalArgumentException.class );
expectedException.expectMessage(
"Property value bytes length: 35000 is longer then 32766, " +
"which is maximum supported length of indexed property value." );
getValidator().validate( RandomStringUtils.randomAlphabetic( 35000 ) );
}

@Test
public void numberIsValidValue()
{
getValidator().validate( 5 );
getValidator().validate( 5.0d );
getValidator().validate( 5.0f );
getValidator().validate( 5L );
}

@Test
public void shortStringIsValidValue()
{
getValidator().validate( RandomStringUtils.randomAlphabetic( 5 ) );
getValidator().validate( RandomStringUtils.randomAlphabetic( 10 ) );
getValidator().validate( RandomStringUtils.randomAlphabetic( 250 ) );
getValidator().validate( RandomStringUtils.randomAlphabetic( 450 ) );
}

protected Validator<Object> getValidator()
{
return INSTANCE;
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2002-2016 "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.api;

import org.apache.commons.lang3.RandomUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.neo4j.kernel.impl.api.IndexValueLengthValidator.INSTANCE;

public class IndexValueLengthValidatorTest
{
@Rule
public ExpectedException expectedException = ExpectedException.none();

@Test
public void tooLongByteArrayIsNotAllowed()
{
expectedException.expect( IllegalArgumentException.class );
expectedException.expectMessage( "Property value bytes length: 35000 is longer then 32766, which is maximum supported length of indexed property value." );
INSTANCE.validate( RandomUtils.nextBytes( 35000 ) );
}

@Test
public void shortByteArrayIsValid()
{
INSTANCE.validate( RandomUtils.nextBytes( 3 ) );
INSTANCE.validate( RandomUtils.nextBytes( 30 ) );
INSTANCE.validate( RandomUtils.nextBytes( 300 ) );
INSTANCE.validate( RandomUtils.nextBytes( 4303 ) );
INSTANCE.validate( RandomUtils.nextBytes( 13234 ) );
}

}

0 comments on commit f814082

Please sign in to comment.