diff options
Diffstat (limited to 'src')
3 files changed, 596 insertions, 0 deletions
diff --git a/src/test/java/com/amazon/carbonado/TestStorables.java b/src/test/java/com/amazon/carbonado/TestStorables.java index b3824da..ecb7c8c 100644 --- a/src/test/java/com/amazon/carbonado/TestStorables.java +++ b/src/test/java/com/amazon/carbonado/TestStorables.java @@ -18,8 +18,13 @@ package com.amazon.carbonado; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; + import java.util.Comparator; import java.util.List; +import java.util.Random; import junit.framework.TestCase; import junit.framework.TestSuite; @@ -46,6 +51,11 @@ import com.amazon.carbonado.UniqueConstraintException; import com.amazon.carbonado.cursor.SortedCursor; +import com.amazon.carbonado.lob.Blob; +import com.amazon.carbonado.lob.ByteArrayBlob; +import com.amazon.carbonado.lob.Clob; +import com.amazon.carbonado.lob.StringClob; + import com.amazon.carbonado.spi.RepairExecutor; import com.amazon.carbonado.gen.WrappedSupport; @@ -1246,6 +1256,33 @@ public class TestStorables extends TestCase { assertEquals("4", seq.getSomeString()); } + public void test_sequenceRollback() throws Exception { + // Make sure sequence does not rollback with the main + // transaction. Sequences must always increase, even if enclosing + // transaction rolls back. Otherwise, you get race conditions and + // sequence values might get re-used. + + Storage<SequenceAndAltKey> storage = getRepository().storageFor(SequenceAndAltKey.class); + + SequenceAndAltKey obj = storage.prepare(); + obj.setName("foo"); + obj.setData("hello"); + obj.insert(); + + int lastID = obj.getID(); + + for (int i=0; i<10000; i++) { + obj = storage.prepare(); + obj.setName("foo"); + obj.setData("world"); + // Alternate key constraint. + assertFalse(obj.tryInsert()); + // Sequence must always increase, even if insert failed. + assertTrue(obj.getID() > lastID); + lastID = obj.getID(); + } + } + public void test_oldIndexEntryDeletion() throws Exception { // Very simple test that ensures that old index entries are deleted // when the master record is updated. There is no guarantee that the @@ -2288,6 +2325,474 @@ public class TestStorables extends TestCase { assertEquals(total, actual); } + public void test_lobInsert() throws Exception { + Storage<StorableWithLobs> storage = getRepository().storageFor(StorableWithLobs.class); + + // Test null insert + { + StorableWithLobs lobs = storage.prepare(); + lobs.insert(); + assertEquals(null, lobs.getBlobValue()); + assertEquals(null, lobs.getClobValue()); + lobs.load(); + assertEquals(null, lobs.getBlobValue()); + assertEquals(null, lobs.getClobValue()); + } + + // Test content insert + int id; + { + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world")); + lobs.insert(); + assertEquals("hello", lobs.getBlobValue().asString()); + assertEquals("world", lobs.getClobValue().asString()); + lobs.load(); + assertEquals("hello", lobs.getBlobValue().asString()); + assertEquals("world", lobs.getClobValue().asString()); + id = lobs.getId(); + } + + // Test update of inserted lobs + { + StorableWithLobs lobs = storage.prepare(); + lobs.setId(id); + lobs.load(); + + Blob blob = lobs.getBlobValue(); + OutputStream out = blob.openOutputStream(); + out.write("the sky is falling".getBytes()); + out.close(); + + assertEquals("the sky is falling", blob.asString()); + + lobs.load(); + assertEquals(blob, lobs.getBlobValue()); + + Clob clob = lobs.getClobValue(); + Writer writer = clob.openWriter(); + writer.write("the quick brown fox"); + writer.close(); + + assertEquals("the quick brown fox", clob.asString()); + + lobs.load(); + assertEquals(blob, lobs.getBlobValue()); + assertEquals(clob, lobs.getClobValue()); + } + + // Test insert failure + { + StorableWithLobs lobs = storage.prepare(); + lobs.setId(id); + + Blob newBlob = new ByteArrayBlob("blob insert should fail".getBytes()); + Clob newClob = new StringClob("clob insert should fail"); + + lobs.setBlobValue(newBlob); + lobs.setClobValue(newClob); + + try { + lobs.insert(); + fail(); + } catch (UniqueConstraintException e) { + } + + assertTrue(newBlob == lobs.getBlobValue()); + assertTrue(newClob == lobs.getClobValue()); + } + } + + public void test_lobUpdate() throws Exception { + Storage<StorableWithLobs> storage = getRepository().storageFor(StorableWithLobs.class); + + // Test null replaces null + { + StorableWithLobs lobs = storage.prepare(); + lobs.insert(); + + lobs.setBlobValue(null); + lobs.setClobValue(null); + + lobs.update(); + + assertEquals(null, lobs.getBlobValue()); + assertEquals(null, lobs.getClobValue()); + + lobs.load(); + assertEquals(null, lobs.getBlobValue()); + assertEquals(null, lobs.getClobValue()); + } + + // Test null replaces content and verify content deleted + { + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world!!!")); + lobs.insert(); + + Blob blob = lobs.getBlobValue(); + Clob clob = lobs.getClobValue(); + + assertEquals(5, blob.getLength()); + assertEquals(8, clob.getLength()); + + lobs.setBlobValue(null); + + lobs.update(); + + assertNull(lobs.getBlobValue()); + assertEquals(clob, lobs.getClobValue()); + + try { + blob.getLength(); + fail(); + } catch (FetchNoneException e) { + } + assertEquals(8, clob.getLength()); + + lobs.load(); + + assertNull(lobs.getBlobValue()); + assertEquals(clob, lobs.getClobValue()); + + lobs.setClobValue(null); + + lobs.update(); + + assertNull(lobs.getBlobValue()); + assertNull(lobs.getClobValue()); + + try { + blob.getLength(); + fail(); + } catch (FetchNoneException e) { + } + try { + clob.getLength(); + fail(); + } catch (FetchNoneException e) { + } + + lobs.load(); + + assertNull(lobs.getBlobValue()); + assertNull(lobs.getClobValue()); + + try { + blob.setLength(100); + fail(); + } catch (PersistNoneException e) { + } + try { + clob.setLength(100); + fail(); + } catch (PersistNoneException e) { + } + + try { + blob.setValue("hello"); + fail(); + } catch (PersistNoneException e) { + } + try { + clob.setValue("hello"); + fail(); + } catch (PersistNoneException e) { + } + } + + // Test content replaces null + { + StorableWithLobs lobs = storage.prepare(); + lobs.insert(); + + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world")); + + assertTrue(lobs.getBlobValue() instanceof ByteArrayBlob); + assertTrue(lobs.getClobValue() instanceof StringClob); + + lobs.update(); + + assertEquals("hello", lobs.getBlobValue().asString()); + assertEquals("world", lobs.getClobValue().asString()); + + assertFalse(lobs.getBlobValue() instanceof ByteArrayBlob); + assertFalse(lobs.getClobValue() instanceof StringClob); + + lobs.load(); + + assertEquals("hello", lobs.getBlobValue().asString()); + assertEquals("world", lobs.getClobValue().asString()); + + assertFalse(lobs.getBlobValue() instanceof ByteArrayBlob); + assertFalse(lobs.getClobValue() instanceof StringClob); + } + + // Test content replaces content of same length + { + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world?")); + lobs.insert(); + + Blob blob = lobs.getBlobValue(); + Clob clob = lobs.getClobValue(); + + lobs.setBlobValue(new ByteArrayBlob("12345".getBytes())); + lobs.update(); + + assertEquals(5, lobs.getBlobValue().getLength()); + assertEquals(6, lobs.getClobValue().getLength()); + + assertEquals("12345", lobs.getBlobValue().asString()); + assertEquals("world?", lobs.getClobValue().asString()); + + assertTrue(blob.equals(lobs.getBlobValue())); + assertTrue(clob.equals(lobs.getClobValue())); + + lobs.setClobValue(new StringClob("123456")); + lobs.update(); + + assertEquals(5, lobs.getBlobValue().getLength()); + assertEquals(6, lobs.getClobValue().getLength()); + + assertEquals("12345", lobs.getBlobValue().asString()); + assertEquals("123456", lobs.getClobValue().asString()); + + assertTrue(blob.equals(lobs.getBlobValue())); + assertTrue(clob.equals(lobs.getClobValue())); + } + + // Test content replaces content of longer length + { + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world?")); + lobs.insert(); + + Blob blob = lobs.getBlobValue(); + Clob clob = lobs.getClobValue(); + + lobs.setBlobValue(new ByteArrayBlob("123".getBytes())); + lobs.update(); + + assertEquals(3, lobs.getBlobValue().getLength()); + assertEquals(6, lobs.getClobValue().getLength()); + + assertEquals("123", lobs.getBlobValue().asString()); + assertEquals("world?", lobs.getClobValue().asString()); + + assertTrue(blob.equals(lobs.getBlobValue())); + assertTrue(clob.equals(lobs.getClobValue())); + + lobs.setClobValue(new StringClob("12")); + lobs.update(); + + assertEquals(3, lobs.getBlobValue().getLength()); + assertEquals(2, lobs.getClobValue().getLength()); + + assertEquals("123", lobs.getBlobValue().asString()); + assertEquals("12", lobs.getClobValue().asString()); + + assertTrue(blob.equals(lobs.getBlobValue())); + assertTrue(clob.equals(lobs.getClobValue())); + } + + // Test content replaces content of shorter length + { + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world?")); + lobs.insert(); + + Blob blob = lobs.getBlobValue(); + Clob clob = lobs.getClobValue(); + + lobs.setBlobValue(new ByteArrayBlob("123456789".getBytes())); + lobs.update(); + + assertEquals(9, lobs.getBlobValue().getLength()); + assertEquals(6, lobs.getClobValue().getLength()); + + assertEquals("123456789", lobs.getBlobValue().asString()); + assertEquals("world?", lobs.getClobValue().asString()); + + assertTrue(blob.equals(lobs.getBlobValue())); + assertTrue(clob.equals(lobs.getClobValue())); + + lobs.setClobValue(new StringClob("1234567890")); + lobs.update(); + + assertEquals(9, lobs.getBlobValue().getLength()); + assertEquals(10, lobs.getClobValue().getLength()); + + assertEquals("123456789", lobs.getBlobValue().asString()); + assertEquals("1234567890", lobs.getClobValue().asString()); + + assertTrue(blob.equals(lobs.getBlobValue())); + assertTrue(clob.equals(lobs.getClobValue())); + } + + // Test update failure + { + StorableWithLobs lobs = storage.prepare(); + lobs.setId(10000); + + Blob newBlob = new ByteArrayBlob("blob update should fail".getBytes()); + Clob newClob = new StringClob("clob update should fail"); + + lobs.setBlobValue(newBlob); + lobs.setClobValue(newClob); + + try { + lobs.update(); + fail(); + } catch (PersistNoneException e) { + } + + assertTrue(newBlob == lobs.getBlobValue()); + assertTrue(newClob == lobs.getClobValue()); + } + } + + public void test_lobDelete() throws Exception { + Storage<StorableWithLobs> storage = getRepository().storageFor(StorableWithLobs.class); + + // Test delete of null lob + { + StorableWithLobs lobs = storage.prepare(); + lobs.insert(); + lobs.delete(); + assertEquals(null, lobs.getBlobValue()); + assertEquals(null, lobs.getClobValue()); + } + + // Test delete of non-null lob + { + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob("hello".getBytes())); + lobs.setClobValue(new StringClob("world?")); + lobs.insert(); + + Blob blob = lobs.getBlobValue(); + Clob clob = lobs.getClobValue(); + + lobs.delete(); + + try { + blob.getLength(); + fail(); + } catch (FetchNoneException e) { + } + + try { + clob.getLength(); + fail(); + } catch (FetchNoneException e) { + } + + try { + blob.setLength(100); + fail(); + } catch (PersistNoneException e) { + } + try { + clob.setLength(100); + fail(); + } catch (PersistNoneException e) { + } + + try { + blob.setValue("hello"); + fail(); + } catch (PersistNoneException e) { + } + try { + clob.setValue("hello"); + fail(); + } catch (PersistNoneException e) { + } + } + + // Test delete failure + { + StorableWithLobs lobs = storage.prepare(); + lobs.setId(10000); + + Blob newBlob = new ByteArrayBlob("blob update should fail".getBytes()); + Clob newClob = new StringClob("clob update should fail"); + + lobs.setBlobValue(newBlob); + lobs.setClobValue(newClob); + + try { + lobs.delete(); + fail(); + } catch (PersistNoneException e) { + } + + assertTrue(newBlob == lobs.getBlobValue()); + assertTrue(newClob == lobs.getClobValue()); + } + } + + public void test_insertLobBig() throws Exception { + // LobEngine tests are fairly exhaustive when it comes to large + // content. This is just a basic check. + + final long seed = 287623451234L; + final int length = 123456; + + Storage<StorableWithLobs> storage = getRepository().storageFor(StorableWithLobs.class); + + StorableWithLobs lobs = storage.prepare(); + lobs.setBlobValue(new ByteArrayBlob(1)); + lobs.insert(); + + Random rnd = new Random(seed); + OutputStream out = lobs.getBlobValue().openOutputStream(0, 500); + for (int i=0; i<length; i++) { + out.write(rnd.nextInt()); + } + out.close(); + + assertEquals(length, lobs.getBlobValue().getLength()); + + lobs.load(); + + rnd = new Random(seed); + InputStream in = lobs.getBlobValue().openInputStream(0, 2000); + for (int i=0; i<length; i++) { + assertEquals(rnd.nextInt() & 0xff, in.read()); + } + assertEquals(-1, in.read()); + in.close(); + + // Verify content stored in StoredLob.Block. + // Only applicable if LobEngine is used. + /* + Query<StoredLob.Block> query = getRepository().storageFor(StoredLob.Block.class).query(); + Cursor<StoredLob.Block> cursor = query.fetch(); + assertTrue(cursor.hasNext()); + cursor.close(); + + // Verify its all gone after delete. + lobs.delete(); + + cursor = query.fetch(); + assertFalse(cursor.hasNext()); + cursor.close(); + + // Master record should be gone too. + Cursor<?> c = getRepository().storageFor(StoredLob.class).query().fetch(); + assertFalse(c.hasNext()); + c.close(); + */ + } + private void assertUninitialized(boolean expected, Storable storable, String... properties) { for (String property : properties) { assertEquals(expected, storable.isPropertyUninitialized(property)); diff --git a/src/test/java/com/amazon/carbonado/stored/SequenceAndAltKey.java b/src/test/java/com/amazon/carbonado/stored/SequenceAndAltKey.java new file mode 100644 index 0000000..564a501 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/SequenceAndAltKey.java @@ -0,0 +1,44 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.AlternateKeys;
+import com.amazon.carbonado.Key;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+import com.amazon.carbonado.Storable;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+@AlternateKeys(@Key("name"))
+public interface SequenceAndAltKey extends Storable {
+ @Sequence("SEQ_ID")
+ int getID();
+ void setID(int id);
+
+ String getName();
+ void setName(String name);
+
+ String getData();
+ void setData(String data);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableWithLobs.java b/src/test/java/com/amazon/carbonado/stored/StorableWithLobs.java new file mode 100644 index 0000000..5f3b0bc --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableWithLobs.java @@ -0,0 +1,47 @@ +/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Sequence;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+
+import com.amazon.carbonado.lob.Blob;
+import com.amazon.carbonado.lob.Clob;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("id")
+public interface StorableWithLobs extends Storable {
+ @Sequence("foo")
+ int getId();
+ void setId(int id);
+
+ @Nullable
+ Blob getBlobValue();
+ void setBlobValue(Blob value);
+
+ @Nullable
+ Clob getClobValue();
+ void setClobValue(Clob value);
+}
|
