package com.bigdata.htree; import java.nio.ByteBuffer; import java.util.Random; import java.util.UUID; import com.bigdata.btree.HTreeIndexMetadata; import com.bigdata.io.TestCase3; import com.bigdata.rawstore.IRawStore; import com.bigdata.rawstore.SimpleMemoryRawStore; /** * Unit tests for a {@link HTree} with raw record support enabled (this is where * a large <code>byte[]</code> value is written directly onto the backing store * rather than being stored within the leaf). * * @author thompsonbry */ public class TestRawRecords extends AbstractHTreeTestCase { public TestRawRecords() { } public TestRawRecords(String name) { super(name); } /** * Unit test for the insert of a large <code>byte[]</code> value into an * index such that it is represented as a raw record on the backing store * rather than inline within the B+Tree leaf. */ public void test_insertLargeValue() { final IRawStore store = new SimpleMemoryRawStore(); try { final Random r = new Random(); final HTreeIndexMetadata metadata = new HTreeIndexMetadata(UUID.randomUUID()); metadata.setAddressBits(2); metadata.setRawRecords(true); metadata.setMaxRecLen(64); final HTree htree = HTree.create(store, metadata); assertEquals(64, htree.getMaxRecLen()); assertTrue(htree.rawRecords); final byte[] key = new byte[] { 1, 2, 3 }; final byte[] val = new byte[htree.getMaxRecLen() + 1]; r.nextBytes(val); // insert an entry under that key. assertNull(htree.insert(key, val)); // this test assumes that everything is in a single bucket page. assertEquals(1, htree.getLeafCount()); // examine the root. final DirectoryPage root = (DirectoryPage) htree.getRoot(); // all references should be to the same bucket page. final BucketPage bucket = (BucketPage) root.childRefs[0].get(); // the addr of the raw record. final long addr = bucket.getRawRecord(0/* entryIndex */); assertTrue(addr != IRawStore.NULL); // read the raw record from the store. final ByteBuffer actual = htree.readRawRecord(addr); // verify that the expected data were read. TestCase3.assertEquals(val, actual); } finally { store.destroy(); } } /* * FIXME The rest of the tests in this file deal with update, which we do * not yet support on the HTree. */ // /** // * Unit test in which we update a small value (inline within the leaf) with // * a large value (stored as a raw record). // */ // public void test_updateSmallValueWithLargeValue() { // // final Random r = new Random(); // // final IRawStore store = new SimpleMemoryRawStore(); // // try { // // final IndexMetadata metadata = new IndexMetadata(UUID.randomUUID()); // // metadata.setAddressBits(2); // // metadata.setRawRecords(true); // metadata.setMaxRecLen(64); // // final HTree btree = HTree.create(store, metadata); // // assertEquals(64, btree.getMaxRecLen()); // // assertTrue(((ILeafData) btree.getRoot()).hasRawRecords()); // // assertTrue(btree.rawRecords); // // final byte[] key = new byte[] { 1, 2, 3 }; // final byte[] val = new byte[btree.getMaxRecLen() - 1]; // r.nextBytes(val); // // // insert an entry under that key. // assertNull(btree.insert(key, val)); // // // this test assumes that everything is in a single bucket page. // assertEquals(1, btree.getLeafCount()); // // // examine the root. // final DirectoryPage root = (DirectoryPage) btree.getRoot(); // // // all references should be to the same bucket page. // final BucketPage bucket = (BucketPage) root.childRefs[0].get(); // // // verify not a raw record. // assertEquals(IRawStore.NULL,bucket.getRawRecord(0/* entryIndex */)); // // // create a new byte[] which will be handled as a large record. // final byte[] newval = new byte[btree.getMaxRecLen() + 1]; // r.nextBytes(newval); // // // update the value under that key. // final byte[] oldval = btree.insert(key, newval); // if(true) { // /* // * This test will not work as written because insert() does not // * have update semantics for the HTree. // */ // fail("insert() does NOT have update semantics for the HTree"); // } // // verify the old value was returned. // assertEquals(val, oldval); // // // the addr of the raw record. // final long addr = bucket.getRawRecord(0/* entryIndex */); // // // verify a raw record. // assertTrue(addr != IRawStore.NULL); // // // read the raw record from the store. // final ByteBuffer actual = btree.readRawRecord(addr); // // // verify that the expected data were read. // TestCase3.assertEquals(newval, actual); // // } finally { // // store.destroy(); // // } // // } // // /** // * Unit test in which we update a large value (represented directly on the // * backing store) with a small value (inline within the leaf). The test // * verifies that the original large value is deleted. // */ // public void test_updateLargeValueWithSmallValue() { // // final MyRawStore store = new MyRawStore(new SimpleMemoryRawStore()); // // final IndexMetadata metadata = new IndexMetadata(UUID.randomUUID()); // // metadata.setRawRecords(true); // metadata.setMaxRecLen(64); // // final BTree btree = BTree.create(store, metadata); // // assertEquals(64, btree.getMaxRecLen()); // // assertTrue(((ILeafData) btree.getRoot()).hasRawRecords()); // // // create a byte[] which will be handled as a large record. // final byte[] key = new byte[] { 1 }; // final byte[] val = new byte[btree.getMaxRecLen() + 1]; // r.nextBytes(val); // // // insert an entry under that key. // assertNull(btree.insert(key, val)); // // // examine the root leaf. // final Leaf root = (Leaf) btree.getRoot(); // // // the addr of the raw record. // final long addr = root.getRawRecord(0/* entryIndex */); // // // verify a raw record. // assertTrue(addr != IRawStore.NULL); // // // read the raw record from the store. // final ByteBuffer actual = btree.readRawRecord(addr); // // // verify that the expected data were read. // TestCase3.assertEquals(val, actual); // // // create a new byte[] which not will be handled as a large record. // final byte[] newval = new byte[btree.getMaxRecLen() - 1]; // r.nextBytes(newval); // // // setup for the expected delete of the raw record. // store.expectDelete = addr; // // // update the value under that key. // final byte[] oldval = btree.insert(key, newval); // // // verify that the record was deleted. // assertEquals(IRawStore.NULL, store.expectDelete); // // // verify the old value was returned. // assertEquals(val, oldval); // // // verify not a raw record. // assertEquals(IRawStore.NULL,root.getRawRecord(0/* entryIndex */)); // // } // // /** // * Unit test in which we update a large value (represented directly on the // * backing store) with another large value (also represented directly on the // * store). The test verifies that the original large value is deleted. // */ // public void test_updateLargeValueWithLargeValue() { // // final MyRawStore store = new MyRawStore(new SimpleMemoryRawStore()); // // final IndexMetadata metadata = new IndexMetadata(UUID.randomUUID()); // // metadata.setRawRecords(true); // metadata.setMaxRecLen(64); // // final BTree btree = BTree.create(store, metadata); // // assertEquals(64, btree.getMaxRecLen()); // // assertTrue(((ILeafData) btree.getRoot()).hasRawRecords()); // // // create a byte[] which will be handled as a large record. // final byte[] key = new byte[] { 1 }; // final byte[] val = new byte[btree.getMaxRecLen() + 1]; // r.nextBytes(val); // // // insert an entry under that key. // assertNull(btree.insert(key, val)); // // // examine the root leaf. // final Leaf root = (Leaf) btree.getRoot(); // // // the addr of the raw record. // final long addr = root.getRawRecord(0/* entryIndex */); // // // verify a raw record. // assertTrue(addr != IRawStore.NULL); // // // read the raw record from the store. // final ByteBuffer actual = btree.readRawRecord(addr); // // // verify that the expected data were read. // TestCase3.assertEquals(val, actual); // // // create a new byte[] which will be handled as a large record. // final byte[] newval = new byte[btree.getMaxRecLen() + 1]; // r.nextBytes(newval); // // // setup for the expected delete of the raw record. // store.expectDelete = addr; // // // update the value under that key. // final byte[] oldval = btree.insert(key, newval); // // // verify that the record was deleted. // assertEquals(IRawStore.NULL, store.expectDelete); // // // verify the old value was returned. // assertEquals(val, oldval); // // // the addr of the raw record. // final long addr2 = root.getRawRecord(0/* entryIndex */); // // // verify a raw record. // assertTrue(addr2 != IRawStore.NULL); // // // read the raw record from the store. // final ByteBuffer actual2 = btree.readRawRecord(addr2); // // // verify that the expected data were read. // TestCase3.assertEquals(newval, actual2); // // } // // /** // * Unit test in which we insert a large value (represented directly on the // * backing store) and then delete the key under which that value was stored. // * The test verifies that the original large value is deleted. // */ // public void test_insertLargeValueThenDelete() { // // final MyRawStore store = new MyRawStore(new SimpleMemoryRawStore()); // // final IndexMetadata metadata = new IndexMetadata(UUID.randomUUID()); // // metadata.setRawRecords(true); // metadata.setMaxRecLen(64); // // final BTree btree = BTree.create(store, metadata); // // assertEquals(64, btree.getMaxRecLen()); // // assertTrue(((ILeafData) btree.getRoot()).hasRawRecords()); // // // create a byte[] which will be handled as a large record. // final byte[] key = new byte[] { 1 }; // final byte[] val = new byte[btree.getMaxRecLen() + 1]; // r.nextBytes(val); // // // insert an entry under that key. // assertNull(btree.insert(key, val)); // // // examine the root leaf. // final Leaf root = (Leaf) btree.getRoot(); // // // the addr of the raw record. // final long addr = root.getRawRecord(0/* entryIndex */); // // // verify a raw record. // assertTrue(addr != IRawStore.NULL); // // // read the raw record from the store. // final ByteBuffer actual = btree.readRawRecord(addr); // // // verify that the expected data were read. // TestCase3.assertEquals(val, actual); // // // setup for the expected delete of the raw record. // store.expectDelete = addr; // // // update the value under that key. // final byte[] oldval = btree.remove(key); // // // verify that the record was deleted. // assertEquals(IRawStore.NULL, store.expectDelete); // // // verify the old value was returned. // assertEquals(val, oldval); // // } // // /** // * Helper class is used to watch for deletes of raw records from the backing // * store. // * // * @author thompsonbry // */ // private static class MyRawStore implements IRawStore { // // final IRawStore delegate; // // long expectDelete = IRawStore.NULL; // // public MyRawStore(final IRawStore delegate) { // this.delegate = delegate; // } // // public void close() { // delegate.close(); // } // // public void delete(long addr) { // if (expectDelete != IRawStore.NULL) { // assertEquals(expectDelete, addr); // expectDelete = IRawStore.NULL; // } // delegate.delete(addr); // } // // public void deleteResources() { // delegate.deleteResources(); // } // // public void destroy() { // delegate.destroy(); // } // // public void force(boolean metadata) { // delegate.force(metadata); // } // // public int getByteCount(long addr) { // return delegate.getByteCount(addr); // } // // public CounterSet getCounters() { // return delegate.getCounters(); // } // // public File getFile() { // return delegate.getFile(); // } // // public long getOffset(long addr) { // return delegate.getOffset(addr); // } // // public IResourceMetadata getResourceMetadata() { // return delegate.getResourceMetadata(); // } // // public UUID getUUID() { // return delegate.getUUID(); // } // // public boolean isFullyBuffered() { // return delegate.isFullyBuffered(); // } // // public boolean isOpen() { // return delegate.isOpen(); // } // // public boolean isReadOnly() { // return delegate.isReadOnly(); // } // // public boolean isStable() { // return delegate.isStable(); // } // // public ByteBuffer read(long addr) { // return delegate.read(addr); // } // // public long size() { // return delegate.size(); // } // // public long toAddr(int nbytes, long offset) { // return delegate.toAddr(nbytes, offset); // } // // public String toString(long addr) { // return delegate.toString(addr); // } // // public long write(ByteBuffer data, long oldAddr) { // return delegate.write(data, oldAddr); // } // // public long write(ByteBuffer data) { // return delegate.write(data); // } // // } }