o added support for replacing value of an existing key
o fixed an issue in InMemoryBTree when a key already exists
o fixed an NPE in findLeftMost() findRightMost() of PersistedLeaf of a persisted sub-BTree
o made RevisionName serializable
o added and updated tests
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java
index 053d3e1..3038fbc 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/AbstractValueHolder.java
@@ -375,4 +375,25 @@
addInBtree( value );
}
}
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public V replaceValueArray( V newValue )
+ {
+ if( isSubBtree() )
+ {
+ throw new IllegalStateException( "method is not applicable for the duplicate B-Trees" );
+ }
+
+ V tmp = valueArray[0];
+
+ nbArrayElems = 1;
+ valueArray[0] = newValue;
+
+ return tmp;
+ }
+
}
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java
index fb09eac..920afcc 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryBTree.java
@@ -389,6 +389,11 @@
// a Node or a Leaf
InsertResult<K, V> result = newBtreeHeader.getRootPage().insert( key, value, revision );
+ if ( result instanceof ExistsResult )
+ {
+ return result;
+ }
+
if ( result instanceof ModifyResult )
{
ModifyResult<K, V> modifyResult = ( ( ModifyResult<K, V> ) result );
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java
index ba5e10f..83c18ab 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/InMemoryLeaf.java
@@ -748,12 +748,6 @@
boolean valueExists = valueHolder.contains( value );
- // Check we can add a new value
- if ( !valueExists && !btree.isAllowDuplicates() )
- {
- throw new DuplicateValueNotAllowedException( "Duplicate values are not allowed" );
- }
-
if ( this.revision != revision )
{
// The page hasn't been modified yet, we need to copy it first
@@ -764,12 +758,12 @@
valueHolder = newLeaf.values[pos];
V replacedValue = null;
- if ( !valueExists )
+ if ( !valueExists && btree.isAllowDuplicates() )
{
valueHolder.add( value );
newLeaf.values[pos] = valueHolder;
}
- else
+ else if ( valueExists && btree.isAllowDuplicates() )
{
// As strange as it sounds, we need to remove the value to reinject it.
// There are cases where the value retrieval just use one part of the
@@ -777,6 +771,10 @@
replacedValue = valueHolder.remove( value );
valueHolder.add( value );
}
+ else if ( !btree.isAllowDuplicates() )
+ {
+ replacedValue = valueHolder.replaceValueArray( value );
+ }
// Create the result
InsertResult<K, V> result = new ModifyResult<K, V>( newLeaf, replacedValue );
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java
index b1d08db..97f5bb3 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedLeaf.java
@@ -844,26 +844,29 @@
// Copy the keys and the values
System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
- // It' not enough to copy the ValueHolder, we have to clone them
- // as ValueHolders are mutable
- int pos = 0;
-
- for ( ValueHolder<V> valueHolder : values )
+ if ( values != null )
{
- try
+ // It' not enough to copy the ValueHolder, we have to clone them
+ // as ValueHolders are mutable
+ int pos = 0;
+
+ for ( ValueHolder<V> valueHolder : values )
{
- newLeaf.values[pos++] = valueHolder.clone();
- }
- catch ( CloneNotSupportedException e )
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- // Stop when we have copied nbElems values
- if ( pos == nbElems )
- {
- break;
+ try
+ {
+ newLeaf.values[pos++] = valueHolder.clone();
+ }
+ catch ( CloneNotSupportedException e )
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ // Stop when we have copied nbElems values
+ if ( pos == nbElems )
+ {
+ break;
+ }
}
}
@@ -891,17 +894,6 @@
boolean valueExists = valueHolder.contains( value );
- // Check we can add a new value
- if ( !valueExists && !btree.isAllowDuplicates() )
- {
- throw new DuplicateValueNotAllowedException( "Duplicate values are not allowed" );
- }
-
- if ( valueExists )
- {
- return ExistsResult.EXISTS;
- }
-
if ( this.revision != revision )
{
// The page hasn't been modified yet, we need to copy it first
@@ -912,21 +904,24 @@
valueHolder = newLeaf.values[pos];
V replacedValue = null;
- if ( !valueExists )
+ if ( !valueExists && btree.isAllowDuplicates() )
{
valueHolder.add( value );
newLeaf.values[pos] = valueHolder;
}
- else
+ else if ( valueExists && btree.isAllowDuplicates() )
{
- // this block should be deleted after fixing MAVIBOT-39
// As strange as it sounds, we need to remove the value to reinject it.
// There are cases where the value retrieval just use one part of the
// value only (typically for LDAP Entries, where we use the DN)
- //replacedValue = valueHolder.remove( value );
- //valueHolder.add( value );
+ replacedValue = valueHolder.remove( value );
+ valueHolder.add( value );
}
-
+ else if ( !btree.isAllowDuplicates() )
+ {
+ replacedValue = valueHolder.replaceValueArray( value );
+ }
+
// Create the result
InsertResult<K, V> result = new ModifyResult<K, V>( newLeaf, replacedValue );
result.addCopiedPage( this );
@@ -1096,6 +1091,15 @@
*/
public Tuple<K, V> findLeftMost() throws IOException
{
+ K key = keys[0].getKey();
+
+ boolean isSubTree = ( btree.getType() == PERSISTED_SUB );
+
+ if ( isSubTree )
+ {
+ return new Tuple<K, V>( key, null );
+ }
+
ValueCursor<V> cursor = values[0].getCursor();
try
@@ -1103,12 +1107,12 @@
cursor.beforeFirst();
if ( cursor.hasNext() )
{
- return new Tuple<K, V>( keys[0].getKey(), cursor.next() );
+ return new Tuple<K, V>( key, cursor.next() );
}
else
{
// Null value
- return new Tuple<K, V>( keys[0].getKey(), null );
+ return new Tuple<K, V>( key, null );
}
}
finally
@@ -1123,6 +1127,16 @@
*/
public Tuple<K, V> findRightMost() throws EndOfFileExceededException, IOException
{
+
+ K key = keys[nbElems - 1].getKey();
+
+ boolean isSubTree = ( btree.getType() == PERSISTED_SUB );
+
+ if ( isSubTree )
+ {
+ return new Tuple<K, V>( key, null );
+ }
+
ValueCursor<V> cursor = values[nbElems - 1].getCursor();
try
@@ -1131,12 +1145,12 @@
if ( cursor.hasPrev() )
{
- return new Tuple<K, V>( keys[nbElems - 1].getKey(), cursor.prev() );
+ return new Tuple<K, V>( key, cursor.prev() );
}
else
{
// Null value
- return new Tuple<K, V>( keys[nbElems - 1].getKey(), null );
+ return new Tuple<K, V>( key, null );
}
}
finally
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java
index ba6b3e0..3e9dea5 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/PersistedValueHolder.java
@@ -410,6 +410,8 @@
}
}
+ cursor.close();
+
return returnedValue;
}
else
@@ -636,6 +638,17 @@
}
+ @Override
+ public V replaceValueArray( V newValue )
+ {
+ V val = super.replaceValueArray( newValue );
+ // The raw value is not anymore up to date with the content
+ isRawUpToDate = false;
+
+ return val;
+ }
+
+
/**
* Deserialize the values stored in an array
*/
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionName.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionName.java
index 4d2445b..ae7a24f 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionName.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/RevisionName.java
@@ -19,6 +19,8 @@
*/
package org.apache.directory.mavibot.btree;
+import java.io.Serializable;
+
/**
* A data structure that stores a revision associated to a BTree name. We use
@@ -26,7 +28,7 @@
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
-/* no qualifier*/class RevisionName extends Tuple<Long, String>
+/* no qualifier*/class RevisionName extends Tuple<Long, String> implements Serializable
{
/**
* A constructor for the RevisionName class
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/SpaceReclaimer.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/SpaceReclaimer.java
index b04a7d1..33747cb 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/SpaceReclaimer.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/SpaceReclaimer.java
@@ -83,7 +83,7 @@
try
{
- LOG.debug( "Storing {} RevisionName of Copied page map", rm.copiedPageMap.size() );
+ LOG.debug( "Storing {} RevisionNames of Copied page map", rm.copiedPageMap.size() );
OutputStream fileOut = new FileOutputStream( file );
diff --git a/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java b/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java
index 5f56511..6215d99 100644
--- a/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java
+++ b/mavibot/src/main/java/org/apache/directory/mavibot/btree/ValueHolder.java
@@ -69,6 +69,18 @@
*/
V remove( V removedValue );
+
+ /**
+ * Replaces the single value present in the array.
+ *
+ * This is only applicable for B-Trees that don't
+ * support duplicate values.
+ *
+ * @param newValue the new value
+ * @return the value that was replaced
+ */
+ V replaceValueArray( V newValue );
+
/**
* Create a clone of this instance
diff --git a/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeDuplicateKeyTest.java b/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeDuplicateKeyTest.java
index 68b7a88..1721992 100644
--- a/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeDuplicateKeyTest.java
+++ b/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeDuplicateKeyTest.java
@@ -37,6 +37,7 @@
import org.apache.directory.mavibot.btree.serializer.IntSerializer;
import org.apache.directory.mavibot.btree.serializer.LongSerializer;
import org.apache.directory.mavibot.btree.serializer.StringSerializer;
+import org.junit.Ignore;
import org.junit.Test;
@@ -743,6 +744,7 @@
* Test that a BTree which forbid duplicate values does not accept them
*/
@Test(expected = DuplicateValueNotAllowedException.class)
+ @Ignore("this condition is removed")
public void testBTreeForbidDups() throws IOException, BTreeAlreadyManagedException
{
BTree<Long, String> singleValueBtree = BTreeFactory.createInMemoryBTree( "test2", LongSerializer.INSTANCE,
diff --git a/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeTest.java b/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeTest.java
index f32cebf..751a257 100644
--- a/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeTest.java
+++ b/mavibot/src/test/java/org/apache/directory/mavibot/btree/InMemoryBTreeTest.java
@@ -1982,4 +1982,29 @@
btree.close();
}
+
+
+ /**
+ * Test the overwriting of elements
+ */
+ @Test
+ public void testOverwrite() throws Exception
+ {
+ BTree<Integer, Integer> btree = BTreeFactory.createInMemoryBTree( "test", IntSerializer.INSTANCE,
+ IntSerializer.INSTANCE );
+
+ // Adding an element with a null value
+ btree.insert( 1, 1 );
+
+ assertTrue( btree.hasKey( 1 ) );
+
+ assertEquals( Integer.valueOf( 1 ), btree.get( 1 ) );
+
+ btree.insert( 1, 10 );
+
+ assertTrue( btree.hasKey( 1 ) );
+ assertEquals( Integer.valueOf( 10 ), btree.get( 1 ) );
+
+ btree.close();
+ }
}
diff --git a/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeBrowseTest.java b/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeBrowseTest.java
index 612479b..8346bde 100644
--- a/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeBrowseTest.java
+++ b/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeBrowseTest.java
@@ -36,6 +36,7 @@
import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
+import org.apache.directory.mavibot.btree.serializer.IntSerializer;
import org.apache.directory.mavibot.btree.serializer.LongSerializer;
import org.apache.directory.mavibot.btree.serializer.StringSerializer;
import org.junit.After;
@@ -1107,6 +1108,30 @@
}
+ /**
+ * Test the overwriting of elements
+ */
+ @Test
+ public void testOverwrite() throws Exception
+ {
+ btree.setAllowDuplicates( false );
+
+ // Adding an element with a null value
+ btree.insert( 1L, "1" );
+
+ assertTrue( btree.hasKey( 1L ) );
+
+ assertEquals( "1", btree.get( 1L ) );
+
+ btree.insert( 1L, "10" );
+
+ assertTrue( btree.hasKey( 1L ) );
+ assertEquals( "10", btree.get( 1L ) );
+
+ btree.close();
+ }
+
+
@Ignore("test used for debugging")
@Test
public void testAdd20Random() throws IOException, BTreeAlreadyManagedException, KeyNotFoundException
diff --git a/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeDuplicateKeyTest.java b/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeDuplicateKeyTest.java
index c428797..8dfd94c 100644
--- a/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeDuplicateKeyTest.java
+++ b/mavibot/src/test/java/org/apache/directory/mavibot/btree/PersistedBTreeDuplicateKeyTest.java
@@ -41,6 +41,7 @@
import org.apache.directory.mavibot.btree.serializer.StringSerializer;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -792,11 +793,40 @@
cursor.close();
}
+
+ @Test
+ public void testFindLeftAndRightMosetInSubBTree() throws Exception
+ {
+ PersistedBTreeConfiguration<Integer, Integer> config = new PersistedBTreeConfiguration<Integer, Integer>();
+
+ config.setName( "test" );
+ config.setKeySerializer( IntSerializer.INSTANCE );
+ config.setValueSerializer( IntSerializer.INSTANCE );
+ config.setAllowDuplicates( false );
+ config.setBtreeType( BTreeTypeEnum.PERSISTED_SUB );
+
+ PersistedBTree<Integer, Integer> subBtree = new PersistedBTree<Integer, Integer>( config );
+
+ subBtree.setRecordManager( recordManager1 );
+
+ subBtree.insert( 1, 1 ); // the values will be discarded in this BTree type
+ subBtree.insert( 2, 2 );
+ subBtree.insert( 3, 3 );
+ subBtree.insert( 4, 4 );
+ subBtree.insert( 5, 5 );
+
+ Tuple<Integer, Integer> t = subBtree.getRootPage().findLeftMost();
+ assertEquals( Integer.valueOf( 1 ), t.getKey() );
+
+ t = subBtree.getRootPage().findRightMost();
+ assertEquals( Integer.valueOf( 5 ), t.getKey() );
+ }
/**
* Test that a BTree which forbid duplicate values does not accept them
*/
@Test(expected = DuplicateValueNotAllowedException.class)
+ @Ignore("this condition is removed")
public void testBTreeForbidDups() throws IOException, BTreeAlreadyManagedException
{
BTree<Long, String> singleValueBtree = recordManager1.addBTree( "test2", LongSerializer.INSTANCE,