/** 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 Dec 11, 2006 */ package com.bigdata.btree; import org.apache.log4j.Level; import com.bigdata.btree.filter.TupleFilter; import com.bigdata.btree.keys.TestKeyBuilder; import com.bigdata.util.BytesUtil; /** * Test suite for iterators. The tests are presented from the least dependencies * to the most dependencies ((traversal of the entries for a single leaf, then * children of a node, then dirty child of a node, then post-order traversal, * then post-order traversal of dirty nodes). * * @see Leaf#entryIterator() * @see Node#childIterator(boolean) * @see AbstractNode#postOrderNodeIterator(boolean, boolean) * * @see TestDirtyIterators, which handles tests when some nodes or leaves are * NOT dirty and verifies that the iterators do NOT visit such nodes or * leaves. This tests {@link AbstractNode#postOrderNodeIterator()} as well * since that is just {@link AbstractNode#postOrderNodeIterator(boolean, boolean)} * with <code>false</code> passed in. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ public class TestIterators extends AbstractBTreeTestCase { /** * */ public TestIterators() { } /** * @param name */ public TestIterators(String name) { super(name); } final int flags = IRangeQuery.KEYS | IRangeQuery.VALS; /** * Test ability to visit the entries on a leaf in key order. */ public void test_leaf_entryIterator01() { final BTree btree = getBTree(3); final Leaf root = (Leaf) btree.root; final byte[] k1 = i2k(1); // before any used key. final byte[] k3 = i2k(3); final byte[] k5 = i2k(5); final byte[] k7 = i2k(7); final byte[] k8 = i2k(8); // successor of all used keys. SimpleEntry v3 = new SimpleEntry(3); SimpleEntry v5 = new SimpleEntry(5); SimpleEntry v7 = new SimpleEntry(7); // insert keys until the root leaf is full. assertSameIterator(new Object[]{},root.entryIterator()); btree.insert(k7, v7); assertSameIterator(new Object[]{v7},root.entryIterator()); btree.insert(k5, v5); assertSameIterator(new Object[]{v5,v7},root.entryIterator()); btree.insert(k3, v3); assertSameIterator(new Object[]{v3,v5,v7},root.entryIterator()); // node range iterator tests. assertSameIterator(new Object[]{root},root.postOrderIterator(null,null)); assertSameIterator(new Object[]{root},root.postOrderIterator(k1,k8)); // entry range iterator tests. assertSameIterator(new Object[]{v3,v5,v7},root.rangeIterator(null,null,flags)); assertSameIterator(new Object[]{v3,v5,v7},root.rangeIterator(k3, k8,flags)); assertSameIterator(new Object[]{v3,v5},root.rangeIterator(k3, k7,flags)); assertSameIterator(new Object[]{v5},root.rangeIterator(k5, k7,flags)); assertSameIterator(new Object[]{v5,v7},root.rangeIterator(k5, k8,flags)); try { /* * try with search keys out of order. * * Note: calling next() is required to force construction of an * EntryIterator that actually detects the search key ordering * problem. */ root.rangeIterator(k8, k3,flags).next(); fail("Expecting: "+IllegalArgumentException.class); } catch(IllegalArgumentException ex) { if(log.isInfoEnabled()) log.info("Ignoring expected exception: "+ex); } // remove keys until the root leaf is empty. assertEquals(v5,btree.remove(k5)); assertSameIterator(new Object[]{v3,v7},root.entryIterator()); assertEquals(v7,btree.remove(k7)); assertSameIterator(new Object[]{v3},root.entryIterator()); assertEquals(v3,btree.remove(k3)); assertSameIterator(new Object[]{},root.entryIterator()); // node range iterator tests. assertSameIterator(new Object[]{root},root.postOrderIterator(null,null)); assertSameIterator(new Object[]{root},root.postOrderIterator(k1,k8)); // entry range iterator tests. assertSameIterator(new Object[] {}, root.rangeIterator(k3, k8, flags)); assertSameIterator(new Object[] {}, root.rangeIterator(k3, k7, flags)); assertSameIterator(new Object[] {}, root.rangeIterator(k5, k7, flags)); assertSameIterator(new Object[] {}, root.rangeIterator(k5, k8, flags)); } /** * Test ability to visit the direct children of a node. */ public void test_childIterator01() { final BTree btree = getBTree(3); final Leaf a = (Leaf) btree.root; // final byte[] k0 = i2k(0); // lies before any used key. final byte[] k1 = i2k(1); final byte[] k2 = i2k(2); final byte[] k3 = i2k(3); final byte[] k5 = i2k(5); final byte[] k6 = i2k(6); // lies between leaf(a) and leaf(b) final byte[] k7 = i2k(7); final byte[] k9 = i2k(9); final byte[] k10 = i2k(10); // lies after any used key. SimpleEntry v1 = new SimpleEntry(1); SimpleEntry v2 = new SimpleEntry(2); SimpleEntry v3 = new SimpleEntry(3); SimpleEntry v5 = new SimpleEntry(5); SimpleEntry v7 = new SimpleEntry(7); SimpleEntry v9 = new SimpleEntry(9); // fill up the root leaf. btree.insert(TestKeyBuilder.asSortKey(3), v3); btree.insert(TestKeyBuilder.asSortKey(5), v5); btree.insert(TestKeyBuilder.asSortKey(7), v7); // split the root leaf. btree.insert(TestKeyBuilder.asSortKey(9), v9); final Node c = (Node) btree.root; assertKeys(new int[]{7},c); assertEquals(a,c.getChild(0)); final Leaf b = (Leaf)c.getChild(1); assertKeys(new int[]{3,5},a); assertValues(new Object[]{v3,v5}, a); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); // verify visiting all children. assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root) .childIterator(false)); // verify visiting all entries. assertSameIterator(new Object[]{v3,v5,v7,v9},btree.rangeIterator()); /* * verify child range iterator. * * Note: there are interesting fence posts here. The key range is * sensitive to the separator key in (c) (which is 7) NOT to the keys * actually found in (a) and (b). For this reason, a child range query * with fromKey := 6 will visit (a) since the separator key is 7 and we * would insert 6 into (a) if the key existed. */ assertSameIterator(new Object[]{a,b},c.childIterator(null,null)); assertSameIterator(new Object[]{a},c.childIterator(k3,k6)); assertSameIterator(new Object[]{b},c.childIterator(k7,k10)); assertSameIterator(new Object[]{a},c.childIterator(k3,k5)); // fence post - only visits (a). assertSameIterator(new Object[]{b},c.childIterator(k7,k9)); // fence post - only visits (b). assertSameIterator(new Object[]{a,b},c.childIterator(k6,k7)); // fence post - visits (a) and (b). // verify node range iterator. assertSameIterator(new Object[]{a,b,c},c.postOrderIterator(null,null)); assertSameIterator(new Object[]{a,c},c.postOrderIterator(k3,k6)); assertSameIterator(new Object[]{b,c},c.postOrderIterator(k7,k10)); assertSameIterator(new Object[]{a,b,c},c.postOrderIterator(k6,k7)); // fence post - visits both leaves even though no keys lie in the range. // verify entry range iterator. assertSameIterator(new Object[]{v3,v5,v7,v9},btree.rangeIterator(null,null)); assertSameIterator(new Object[]{v3},btree.rangeIterator(k3,k5)); assertSameIterator(new Object[]{v5,v7,v9},btree.rangeIterator(k5,k10)); try { // try with search keys out of order. c.childIterator(k9, k3); fail("Expecting: "+IllegalArgumentException.class); } catch(IllegalArgumentException ex) { if(log.isInfoEnabled()) log.info("Ignoring expected exception: "+ex); } try { // try with search keys out of order. btree.rangeIterator(k9, k3); fail("Expecting: "+IllegalArgumentException.class); } catch(IllegalArgumentException ex) { if(log.isInfoEnabled()) log.info("Ignoring expected exception: "+ex); } /* * split another leaf so that there are now three children to visit. at * this point the root is full. */ btree.insert(TestKeyBuilder.asSortKey(1), v1); btree.insert(TestKeyBuilder.asSortKey(2), v2); assertKeys(new int[]{3,7},c); assertEquals(a,c.getChild(0)); Leaf d = (Leaf)c.getChild(1); assertEquals(b,c.getChild(2)); assertKeys(new int[]{1,2},a); assertValues(new Object[]{v1,v2}, a); assertKeys(new int[]{3,5},d); assertValues(new Object[]{v3,v5}, d); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); // verify visiting all children. assertSameIterator(new IAbstractNode[] { a, d, b }, ((Node) btree.root) .childIterator(false)); // verify visiting children in a key range. assertSameIterator(new Object[]{a,d,b},c.childIterator(null,null)); assertSameIterator(new Object[]{a},c.childIterator(k1,k2)); assertSameIterator(new Object[]{d},c.childIterator(k3,k5)); assertSameIterator(new Object[]{b},c.childIterator(k7,k9)); assertSameIterator(new Object[]{a,d},c.childIterator(k1,k3)); assertSameIterator(new Object[]{d,b},c.childIterator(k3,k9)); // verify node range iterator. assertSameIterator(new Object[]{a,d,b,c},c.postOrderIterator(null,null)); assertSameIterator(new Object[]{a,c},c.postOrderIterator(k1,k2)); assertSameIterator(new Object[]{d,c},c.postOrderIterator(k3,k5)); assertSameIterator(new Object[]{b,c},c.postOrderIterator(k7,k9)); assertSameIterator(new Object[]{a,d,c},c.postOrderIterator(k1,k3)); assertSameIterator(new Object[]{d,b,c},c.postOrderIterator(k3,k9)); assertSameIterator(new Object[]{a,d,b,c},c.postOrderIterator(k1,k9)); // verify entry range iterator. assertSameIterator(new Object[]{v1,v2,v3,v5,v7,v9},btree.rangeIterator(null,null)); assertSameIterator(new Object[]{v3,v5,v7,v9},btree.rangeIterator(k3,k10)); assertSameIterator(new Object[]{v2,v3},btree.rangeIterator(k2,i2k(4))); assertSameIterator(new Object[]{v3},btree.rangeIterator(k3,i2k(4))); assertSameIterator(new Object[]{v5,v7},btree.rangeIterator(k5,i2k(8))); assertSameIterator(new Object[]{v5,v7,v9},btree.rangeIterator(k5,k10)); /* * remove a key from a leaf forcing two leaves to join and verify the * visitation order. */ assertEquals(v1,btree.remove(TestKeyBuilder.asSortKey(1))); assertKeys(new int[]{7},c); assertEquals(a,c.getChild(0)); assertEquals(b,c.getChild(1)); assertKeys(new int[]{2,3,5},a); assertValues(new Object[]{v2,v3,v5}, a); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); assertTrue(d.isDeleted()); // verify visiting all children. assertSameIterator(new IAbstractNode[] { a, b }, ((Node) btree.root) .childIterator(false)); /* * Note: the test ends here since there must be either 2 or 3 children * for the root node. If we force the remaining leaves to join, then * the root node will be replaced by a root leaf. */ } /** * Test ability to visit the nodes of the tree in a post-order traversal. */ public void test_postOrderIterator01() { final BTree btree = getBTree(3); final Leaf a = (Leaf) btree.root; SimpleEntry v1 = new SimpleEntry(1); SimpleEntry v2 = new SimpleEntry(2); SimpleEntry v3 = new SimpleEntry(3); SimpleEntry v4 = new SimpleEntry(4); SimpleEntry v6 = new SimpleEntry(6); SimpleEntry v5 = new SimpleEntry(5); SimpleEntry v7 = new SimpleEntry(7); SimpleEntry v9 = new SimpleEntry(9); // empty tree visits the root leaf. assertSameIterator(new IAbstractNode[] { btree.root }, btree.root .postOrderNodeIterator()); // fill up the root leaf. btree.insert(TestKeyBuilder.asSortKey(3), v3); btree.insert(TestKeyBuilder.asSortKey(5), v5); btree.insert(TestKeyBuilder.asSortKey(7), v7); // split the root leaf. btree.insert(TestKeyBuilder.asSortKey(9), v9); final Node c = (Node) btree.root; assertKeys(new int[]{7},c); assertEquals(a,c.getChild(0)); final Leaf b = (Leaf)c.getChild(1); assertKeys(new int[]{3,5},a); assertValues(new Object[]{v3,v5}, a); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); // verify iterator. assertSameIterator(new IAbstractNode[] { a, b, c }, btree.root .postOrderNodeIterator()); /* * split another leaf so that there are now three children to visit. at * this point the root is full. */ btree.insert(TestKeyBuilder.asSortKey(1), v1); btree.insert(TestKeyBuilder.asSortKey(2), v2); assertKeys(new int[]{3,7},c); assertEquals(a,c.getChild(0)); Leaf d = (Leaf)c.getChild(1); assertEquals(b,c.getChild(2)); assertKeys(new int[]{1,2},a); assertValues(new Object[]{v1,v2}, a); assertKeys(new int[]{3,5},d); assertValues(new Object[]{v3,v5}, d); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); // verify iterator assertSameIterator(new IAbstractNode[] { a, d, b, c }, btree.root .postOrderNodeIterator()); /* * cause another leaf (d) to split, forcing the split to propagate to and * split the root and the tree to increase in height. */ btree.insert(TestKeyBuilder.asSortKey(4), v4); btree.insert(TestKeyBuilder.asSortKey(6), v6); // btree.dump(Level.DEBUG,System.err); assertNotSame(c,btree.root); final Node g = (Node)btree.root; assertKeys(new int[]{5},g); assertEquals(c,g.getChild(0)); final Node f = (Node)g.getChild(1); assertKeys(new int[]{3},c); assertEquals(a,c.getChild(0)); assertEquals(d,c.getChild(1)); assertKeys(new int[]{1,2},a); assertValues(new Object[]{v1,v2}, a); assertKeys(new int[]{3,4},d); assertValues(new Object[]{v3,v4}, d); assertKeys(new int[]{7},f); Leaf e = (Leaf)f.getChild(0); assertEquals(b,f.getChild(1)); assertKeys(new int[]{5,6},e); assertValues(new Object[]{v5,v6}, e); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); // verify iterator assertSameIterator(new IAbstractNode[] { a, d, c, e, b, f, g }, btree.root .postOrderNodeIterator()); /* * remove a key (4) from (d) forcing (d,a) to merge into (d) and (a) to * be deleted. this causes (c,f) to merge as well, which in turn forces * the root to be replaced by (c). */ assertEquals(v4,btree.remove(TestKeyBuilder.asSortKey(4))); // btree.dump(Level.DEBUG,System.err); assertKeys(new int[]{5,7},c); assertEquals(d,c.getChild(0)); assertEquals(e,c.getChild(1)); assertEquals(b,c.getChild(2)); assertKeys(new int[]{1,2,3},d); assertValues(new Object[]{v1,v2,v3}, d); assertKeys(new int[]{5,6},e); assertValues(new Object[]{v5,v6}, e); assertKeys(new int[]{7,9},b); assertValues(new Object[]{v7,v9}, b); assertTrue(a.isDeleted()); // verify iterator assertSameIterator(new IAbstractNode[] { d, e, b, c }, btree.root .postOrderNodeIterator()); /* * remove a key (7) from a leaf (b) forcing two leaves to join and * verify the visitation order. */ assertEquals(v7,btree.remove(TestKeyBuilder.asSortKey(7))); btree.dump(Level.DEBUG,System.err); assertKeys(new int[]{5},c); assertEquals(d,c.getChild(0)); assertEquals(b,c.getChild(1)); assertKeys(new int[]{1,2,3},d); assertValues(new Object[]{v1,v2,v3}, d); assertKeys(new int[]{5,6,9},b); assertValues(new Object[]{v5,v6,v9}, b); assertTrue(e.isDeleted()); // verify iterator assertSameIterator(new IAbstractNode[] { d, b, c }, btree.root .postOrderNodeIterator()); /* * remove keys from a leaf forcing the remaining two leaves to join and * verify the visitation order. */ assertEquals(v3,btree.remove(TestKeyBuilder.asSortKey(3))); assertEquals(v5,btree.remove(TestKeyBuilder.asSortKey(5))); assertEquals(v6,btree.remove(TestKeyBuilder.asSortKey(6))); assertKeys(new int[]{1,2,9},b); assertValues(new Object[]{v1,v2,v9}, b); assertTrue(d.isDeleted()); assertTrue(c.isDeleted()); // verify iterator assertSameIterator(new IAbstractNode[] { b }, btree.root .postOrderNodeIterator()); } /** * Test the use stacked iterators to visit only select values. */ public void test_filters() { final byte[] k3 = i2k(3); final byte[] k5 = i2k(5); final byte[] k7 = i2k(7); final byte[] v3 = new byte[] { 3 }; final byte[] v5 = new byte[] { 5 }; final byte[] v7 = new byte[] { 7 }; final BTree btree = getBTree(3, NOPTupleSerializer.INSTANCE); btree.insert(k3, v3); btree.insert(k5, v5); btree.insert(k7, v7); // final Leaf a = (Leaf) btree.root; // visit everything in the root leaf. assertSameIterator(new byte[][]{v3,v5,v7},btree.rangeIterator()); // // visit everything in the root leaf. // assertSameIterator(new byte[][]{v3,v5,v7},btree.rangeIterator(null,null)); // // // visit everything in the root leaf using an explicit EntryIterator ctor. // assertSameIterator(new byte[][] { v3, v5, v7 }, new LeafTupleIterator(a, // new Tuple(btree,IRangeQuery.DEFAULT), null, null, null)); // visit everything except v3. assertSameIterator(new byte[][] { v5, v7 },// btree.rangeIterator(null/* fromKey */, null/* toKey */, 0/* capacity */, flags, new TupleFilter() { private static final long serialVersionUID = 1L; public boolean isValid(ITuple tuple) { return !BytesUtil.bytesEqual(v3, tuple .getValue()); } })); // visit everything except v5. assertSameIterator(new byte[][] { v3, v7 },// btree.rangeIterator(null/* fromKey */, null/* toKey */, 0/* capacity */, flags, new TupleFilter() { private static final long serialVersionUID = 1L; public boolean isValid(ITuple tuple) { return !BytesUtil.bytesEqual(v5, tuple .getValue()); } })); // visit everything except v7. assertSameIterator(new byte[][] { v3, v5 },// btree.rangeIterator(null/* fromKey */, null/* toKey */, 0/* capacity */, flags, new TupleFilter() { private static final long serialVersionUID = 1L; public boolean isValid(ITuple tuple) { return !BytesUtil.bytesEqual(v7, tuple .getValue()); } })); } }