/* Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Feb 13, 2008 */ package com.bigdata.btree; import java.util.UUID; import com.bigdata.rawstore.IRawStore; import com.bigdata.rawstore.SimpleMemoryRawStore; /** * Test of basic btree operations when delete markers are maintained. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestDeleteMarkers extends AbstractBTreeTestCase { /** * */ public TestDeleteMarkers() { } /** * @param name */ public TestDeleteMarkers(String name) { super(name); } /** * Test of basic index operation semantics when delete markers are enabled. */ public void test_crud() { IndexMetadata metadata = new IndexMetadata(UUID.randomUUID()); metadata.setDeleteMarkers(true); // Note: timestamp is 0L since version timestamps are not enabled. final long timestamp = 0L; // create index. BTree btree = BTree.create(new SimpleMemoryRawStore(), metadata); // verify delete markers are in use. assertTrue(btree.getIndexMetadata().getDeleteMarkers()); final byte[] k1 = new byte[] { 1 }; final byte[] v1 = new byte[] { 1 }; /* * verify initial conditions. */ assertFalse(btree.contains( k1 )); assertEquals(null,btree.lookup( k1 )); assertEquals(0,btree.getEntryCount()); assertEquals(0,btree.rangeCount(null, null)); assertEquals(0,btree.rangeCountExact(null, null)); assertEquals(0,btree.rangeCountExactWithDeleted(null, null)); assertSameIterator(new byte[][] {}, btree.rangeIterator(null, null, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); /* * insert a null value under the key. */ btree.insert(k1, null, false/* delete */, false/*putIfAbsent*/, timestamp, null/* tuple */); assertTrue(btree.contains( k1 )); assertEquals(null,btree.lookup( k1 )); assertEquals(1, btree.getEntryCount()); assertEquals(1, btree.rangeCount(null, null)); assertEquals(1, btree.rangeCountExact(null, null)); assertEquals(1, btree.rangeCountExactWithDeleted(null, null)); assertSameIterator(new byte[][] { null }, btree.rangeIterator(null, null, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); /* * insert a non-null value under that key. */ btree.insert(k1, v1, false/* delete */, false/*putIfAbsent*/, timestamp, null/* tuple */); assertTrue(btree.contains( k1 )); assertEquals(v1, btree.lookup(k1)); assertEquals(1, btree.getEntryCount()); assertEquals(1, btree.rangeCount(null, null)); assertEquals(1, btree.rangeCountExact(null, null)); assertEquals(1, btree.rangeCountExactWithDeleted(null, null)); assertSameIterator(new byte[][] { v1 }, btree .rangeIterator(null, null, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); /* * insert a delete marker under that key. */ btree.insert(k1, null, true/* delete */, false/*putIfAbsent*/, timestamp, null/* tuple */); assertFalse(btree.contains(k1)); assertEquals(null, btree.lookup(k1)); // verify that the delete marker is present. assertTrue(btree.lookup(k1, btree.getLookupTuple()).isDeletedVersion()); assertEquals(1, btree.getEntryCount()); assertEquals(1, btree.rangeCount(null, null)); assertEquals(0, btree.rangeCountExact(null, null)); assertEquals(1, btree.rangeCountExactWithDeleted(null, null)); // the deleted entry is NOT visible when we do NOT specify DELETED. assertSameIterator(new byte[][] {}, btree.rangeIterator(null, null, 0/* capacity */, IRangeQuery.DEFAULT, null/* filter */)); // the deleted entry is visible when we specify DELETED. assertSameIterator(new byte[][] { null }, btree.rangeIterator(null, null, 0/* capacity */, IRangeQuery.DEFAULT | IRangeQuery.DELETED, null/* filter */)); } /** * Test that {@link BTree#remove(byte[],ITuple)} is disabled when delete * markers are enabled. * * @todo test the semantics of {@link BTree#remove(byte[])}, which should * be equivalent to an insert of a delete marker. */ public void test_removeNotAllowed() { IndexMetadata metadata = new IndexMetadata(UUID.randomUUID()); metadata.setDeleteMarkers(true); // create index. BTree btree = BTree.create(new SimpleMemoryRawStore(),metadata); // verify delete markers are in use. assertTrue(btree.getIndexMetadata().getDeleteMarkers()); try { btree.remove(new byte[] { 1 }, null); fail("Expecting: " + UnsupportedOperationException.class); } catch (UnsupportedOperationException ex) { log.info("Ignoring expected exception: " + ex); } } /** * Test verifies that {@link BTree#removeAll()} causes deletion markers to * be written for each undeleted entry in the B+Tree. * <p> * Note: This test only tests removal with a root leaf and therefore relies * on the underlying iterator to correctly support removal or update of an * entry in the btree in any leaf regardless of the height of the btree. */ public void test_removeAll_rootLeaf() { final byte[] k3 = new byte[]{3}; final byte[] k5 = new byte[]{5}; final byte[] k7 = new byte[]{7}; final byte[] v3 = new byte[]{3}; final byte[] v5 = new byte[]{5}; final byte[] v7 = new byte[]{7}; final IRawStore store = new SimpleMemoryRawStore(); // create btrees final BTree btree; { IndexMetadata md = new IndexMetadata(UUID.randomUUID()); md.setBranchingFactor(3); md.setDeleteMarkers(true); btree = BTree.create(store, md); } /* * fill the root leaf on the btree. */ btree.insert(k3,v3); btree.insert(k5,v5); btree.insert(k7,v7); assertEquals(3,btree.getEntryCount()); // verify view iterator. assertSameIterator(new byte[][]{v3,v5,v7},btree.rangeIterator(null,null)); /* * remove all un-deleted entries by writing deletion markers. */ btree.removeAll(); assertEquals(3,btree.getEntryCount()); assertFalse(btree.contains(k3)); assertFalse(btree.contains(k5)); assertFalse(btree.contains(k7)); assertSameIterator(new byte[][]{},btree.rangeIterator()); /* * verify presence of deletion markers for the deleted index entries. */ { Tuple tuple = btree.getLookupTuple(); tuple = btree.lookup(k3, tuple); assertNotNull(tuple); assertTrue(tuple.isDeletedVersion()); } { Tuple tuple = btree.getLookupTuple(); tuple = btree.lookup(k5, tuple); assertNotNull(tuple); assertTrue(tuple.isDeletedVersion()); } { Tuple tuple = btree.getLookupTuple(); tuple = btree.lookup(k7, tuple); assertNotNull(tuple); assertTrue(tuple.isDeletedVersion()); } } }