From 1a384d409db2f070730384c6f83083233044673a Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 11 May 2008 01:33:53 +0000 Subject: Added JDBC tests which use H2. --- .../carbonado/repo/jdbc/DerbySchemaResolver.java | 256 ++++++++++++++ .../carbonado/repo/jdbc/H2SchemaResolver.java | 267 +++++++++++++++ .../com/amazon/carbonado/repo/jdbc/TestH2.java | 366 +++++++++++++++++++++ 3 files changed, 889 insertions(+) create mode 100644 src/test/java/com/amazon/carbonado/repo/jdbc/DerbySchemaResolver.java create mode 100644 src/test/java/com/amazon/carbonado/repo/jdbc/H2SchemaResolver.java create mode 100644 src/test/java/com/amazon/carbonado/repo/jdbc/TestH2.java (limited to 'src') diff --git a/src/test/java/com/amazon/carbonado/repo/jdbc/DerbySchemaResolver.java b/src/test/java/com/amazon/carbonado/repo/jdbc/DerbySchemaResolver.java new file mode 100644 index 0000000..76f7cdf --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/jdbc/DerbySchemaResolver.java @@ -0,0 +1,256 @@ +/* + * Copyright 2008 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.repo.jdbc; + +import java.util.ArrayList; +import java.util.List; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.joda.time.DateTime; + +import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryBuilder; +import com.amazon.carbonado.RepositoryException; +import com.amazon.carbonado.Storable; + +import com.amazon.carbonado.lob.Blob; +import com.amazon.carbonado.lob.Clob; + +import com.amazon.carbonado.info.OrderedProperty; +import com.amazon.carbonado.info.StorableIndex; +import com.amazon.carbonado.info.StorableInfo; +import com.amazon.carbonado.info.StorableKey; +import com.amazon.carbonado.info.StorableProperty; + +/** + * Creates tables as needed automatically for the Derby database. + * + * @author Brian S O'Neill + */ +public class DerbySchemaResolver implements SchemaResolver { + public boolean resolve(StorableInfo info, + Connection con, String catalog, String schema) + throws SQLException + { + // Create the table, keys, indexes and sequences. + + StringBuilder b = new StringBuilder(); + b.append("CREATE TABLE "); + + String tableName; + if (info.getAliasCount() > 0) { + tableName = info.getAlias(0); + } else { + tableName = info.getName(); + } + if (needsQuotes(tableName)) { + tableName = '"' + tableName + '"'; + } + b.append(tableName); + b.append(" ("); + + int i = 0; + for (StorableProperty property : info.getAllProperties().values()) { + if (property.isJoin() || property.isDerived()) { + continue; + } + + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + b.append(' '); + + Class type = property.getType(); + String typeName; + if (type == String.class) { + typeName = "VARCHAR(255)"; + } else if (type == int.class || type == Integer.class) { + typeName = "INT"; + } else if (type == long.class || type == Long.class) { + typeName = "BIGINT"; + } else if (type == float.class || type == Float.class) { + typeName = "REAL"; + } else if (type == double.class || type == Double.class) { + typeName = "DOUBLE"; + } else if (type == short.class || type == Short.class) { + typeName = "SMALLINT"; + } else if (type == byte.class || type == Byte.class) { + typeName = "TINYINT"; + } else if (type == char.class || type == Character.class) { + typeName = "CHAR(1)"; + } else if (type == boolean.class || type == Boolean.class) { + typeName = "BOOLEAN"; + } else if (type == DateTime.class) { + typeName = "TIMESTAMP"; + } else if (type == byte[].class) { + typeName = "BINARY"; + } else if (type == Blob.class) { + typeName = "BLOB"; + } else if (type == Clob.class) { + typeName = "CLOB"; + } else { + return false; + } + + b.append(typeName); + + if (!property.isNullable()) { + b.append(" NOT NULL"); + } + } + + b.append(')'); + String createTable = b.toString(); + + b = new StringBuilder(); + b.append("ALTER TABLE "); + b.append(tableName); + b.append(" ADD PRIMARY KEY ("); + + i = 0; + for (StorableProperty property : info.getPrimaryKeyProperties().values()) { + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + } + + b.append(')'); + String createPrimaryKey = b.toString(); + + int indexCount = 0; + + List createAltKeys = new ArrayList(); + + for (StorableKey key : info.getAlternateKeys()) { + b = new StringBuilder(); + b.append("CREATE UNIQUE INDEX "); + b.append(tableName); + b.append('_'); + b.append(Integer.valueOf(++indexCount)); + b.append(" ON "); + b.append(tableName); + b.append(" ("); + + i = 0; + for (OrderedProperty op : key.getProperties()) { + StorableProperty property = op.getChainedProperty().getPrimeProperty(); + if (property.isDerived()) { + continue; + } + + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + } + + if (i > 0) { + b.append(')'); + createAltKeys.add(b.toString()); + } + } + + List createIndexes = new ArrayList(); + + for (StorableIndex index : info.getIndexes()) { + b = new StringBuilder(); + b.append("CREATE INDEX "); + b.append(tableName); + b.append('_'); + b.append(Integer.valueOf(++indexCount)); + b.append(" ON "); + b.append(tableName); + b.append(" ("); + + i = 0; + for (StorableProperty property : index.getProperties()) { + if (property.isDerived()) { + continue; + } + + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + } + + if (i > 0) { + b.append(')'); + createIndexes.add(b.toString()); + } + } + + Statement st = con.createStatement(); + try { + st.executeUpdate(createTable); + + st.executeUpdate(createPrimaryKey); + + for (String createAltKey : createAltKeys) { + st.executeUpdate(createAltKey); + } + + for (String createIndex : createIndexes) { + st.executeUpdate(createIndex); + } + } finally { + st.close(); + } + + return true; + } + + private boolean needsQuotes(String str) { + if (!Character.isUnicodeIdentifierStart(str.charAt(0))) { + return true; + } + for (int i=1; i boolean resolve(StorableInfo info, + Connection con, String catalog, String schema) + throws SQLException + { + // Create the table, keys, indexes and sequences. + List sequenceNames = new ArrayList(); + + StringBuilder b = new StringBuilder(); + b.append("CREATE TABLE "); + + String tableName; + if (info.getAliasCount() > 0) { + tableName = info.getAlias(0); + } else { + tableName = info.getName(); + } + if (needsQuotes(tableName)) { + tableName = '"' + tableName + '"'; + } + b.append(tableName); + b.append(" ("); + + int i = 0; + for (StorableProperty property : info.getAllProperties().values()) { + if (property.isJoin() || property.isDerived()) { + continue; + } + + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + b.append(' '); + + Class type = property.getType(); + String typeName; + if (type == String.class) { + typeName = "VARCHAR"; + } else if (type == int.class || type == Integer.class) { + typeName = "INT"; + } else if (type == long.class || type == Long.class) { + typeName = "BIGINT"; + } else if (type == float.class || type == Float.class) { + typeName = "REAL"; + } else if (type == double.class || type == Double.class) { + typeName = "DOUBLE"; + } else if (type == short.class || type == Short.class) { + typeName = "SMALLINT"; + } else if (type == byte.class || type == Byte.class) { + typeName = "TINYINT"; + } else if (type == char.class || type == Character.class) { + typeName = "CHAR(1)"; + } else if (type == boolean.class || type == Boolean.class) { + typeName = "BOOLEAN"; + } else if (type == DateTime.class) { + typeName = "TIMESTAMP"; + } else if (type == byte[].class) { + typeName = "BINARY"; + } else if (type == Blob.class) { + typeName = "BLOB"; + } else if (type == Clob.class) { + typeName = "CLOB"; + } else { + return false; + } + + b.append(typeName); + + if (property.isNullable()) { + b.append(" NULL"); + } else { + b.append(" NOT NULL"); + } + + if (property.getSequenceName() != null) { + sequenceNames.add(property.getSequenceName()); + } + } + + b.append(')'); + String createTable = b.toString(); + + b = new StringBuilder(); + b.append("ALTER TABLE "); + b.append(tableName); + b.append(" ADD PRIMARY KEY ("); + + i = 0; + for (StorableProperty property : info.getPrimaryKeyProperties().values()) { + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + } + + b.append(')'); + String createPrimaryKey = b.toString(); + + int indexCount = 0; + + List createAltKeys = new ArrayList(); + + for (StorableKey key : info.getAlternateKeys()) { + b = new StringBuilder(); + b.append("CREATE UNIQUE INDEX "); + b.append(tableName); + b.append('_'); + b.append(Integer.valueOf(++indexCount)); + b.append(" ON "); + b.append(tableName); + b.append(" ("); + + i = 0; + for (OrderedProperty op : key.getProperties()) { + StorableProperty property = op.getChainedProperty().getPrimeProperty(); + if (property.isDerived()) { + continue; + } + + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + } + + if (i > 0) { + b.append(')'); + createAltKeys.add(b.toString()); + } + } + + List createIndexes = new ArrayList(); + + for (StorableIndex index : info.getIndexes()) { + b = new StringBuilder(); + b.append("CREATE INDEX "); + b.append(tableName); + b.append('_'); + b.append(Integer.valueOf(++indexCount)); + b.append(" ON "); + b.append(tableName); + b.append(" ("); + + i = 0; + for (StorableProperty property : index.getProperties()) { + if (property.isDerived()) { + continue; + } + + if (i++ > 0) { + b.append(", "); + } + + if (property.getAliasCount() > 0) { + b.append(property.getAlias(0)); + } else { + b.append(property.getName()); + } + } + + if (i > 0) { + b.append(')'); + createIndexes.add(b.toString()); + } + } + + Statement st = con.createStatement(); + try { + st.executeUpdate(createTable); + + st.executeUpdate(createPrimaryKey); + + for (String createAltKey : createAltKeys) { + st.executeUpdate(createAltKey); + } + + for (String createIndex : createIndexes) { + st.executeUpdate(createIndex); + } + + for (String sequenceName : sequenceNames) { + st.executeUpdate("CREATE SEQUENCE IF NOT EXISTS " + sequenceName); + } + } finally { + st.close(); + } + + return true; + } + + private boolean needsQuotes(String str) { + if (!Character.isUnicodeIdentifierStart(str.charAt(0))) { + return true; + } + for (int i=1; i 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 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()); + } + } + + // Override because H2 does not fully support LOBs. + @Override + public void test_lobUpdate() throws Exception { + Storage 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.asString(), lobs.getClobValue().asString()); + + lobs.load(); + + assertNull(lobs.getBlobValue()); + assertEquals(clob.asString(), lobs.getClobValue().asString()); + + lobs.setClobValue(null); + + lobs.update(); + + assertNull(lobs.getBlobValue()); + assertNull(lobs.getClobValue()); + + lobs.load(); + + assertNull(lobs.getBlobValue()); + assertNull(lobs.getClobValue()); + } + + // 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.asString().equals(lobs.getBlobValue().asString())); + assertTrue(clob.asString().equals(lobs.getClobValue().asString())); + + 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.asString().equals(lobs.getBlobValue().asString())); + assertTrue(clob.asString().equals(lobs.getClobValue().asString())); + } + + // 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.asString().equals(lobs.getBlobValue().asString())); + assertTrue(clob.asString().equals(lobs.getClobValue().asString())); + + 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.asString().equals(lobs.getBlobValue().asString())); + assertTrue(clob.asString().equals(lobs.getClobValue().asString())); + } + + // 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.asString().equals(lobs.getBlobValue().asString())); + assertTrue(clob.asString().equals(lobs.getClobValue().asString())); + + 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.asString().equals(lobs.getBlobValue().asString())); + assertTrue(clob.asString().equals(lobs.getClobValue().asString())); + } + + // 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()); + } + } + + @Override + public void test_insertLobBig() throws Exception { + // Not a useful test. + } + + private RepositoryBuilder jdbcBuilder(boolean isMaster) throws RepositoryException { + JDBCRepositoryBuilder builder = new JDBCRepositoryBuilder(); + builder.setName("jdbc"); + builder.setAutoVersioningEnabled(true, null); + builder.setMaster(isMaster); + BasicDataSource ds = new BasicDataSource(); + builder.setDataSource(ds); + + builder.setSchemaResolver(new H2SchemaResolver()); + + File dir = new File(TestUtilities.makeTestDirectory("jdbc"), "/h2"); + String url = "jdbc:h2:" + dir.getPath(); + ds.setDriverClassName("org.h2.Driver"); + ds.setUrl(url); + ds.setUsername("sa"); + ds.setPassword(""); + + return builder; + } +} -- cgit v1.2.3