</classpathentry>\r
<classpathentry kind="lib" path="ext/hsqldb-2.2.4.jar"/>\r
<classpathentry kind="lib" path="ext/derby-10.8.1.2.jar"/>\r
+ <classpathentry kind="lib" path="ext/mysql-connector-java-5.1.15.jar"/>\r
<classpathentry kind="lib" path="ext/h2-1.3.159.jar" sourcepath="ext/h2-1.3.159-sources.jar">\r
<attributes>\r
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/h2-1.3.159-javadoc.jar!/"/>\r
- [H2](http://h2database.com) 1.3.159\r
- [HSQLDB](http://hsqldb.org) 2.2.4\r
- [Derby](http://db.apache.org/derby) 10.7.1.1 & 10.8.1.2\r
+- [MySQL](http://mysql.com) 5.0.51b\r
\r
-Partially Supported Databases (not Unit-Tested)\r
--------\r
-- [MySQL](http://mysql.com)\r
-\r
-Support for others is planned and may only require creating a simple "dialect" class.\r
+Support for others is possible and may only require creating a simple "dialect" class.\r
\r
License\r
-------\r
- [H2](http://h2database.com) 1.3.159\r
- [HSQLDB](http://hsqldb.org) 2.2.4 \r
- [Derby](http://db.apache.org/derby) 10.7.1.1 & 10.8.1.2\r
+- [MySQL](http://mysql.com) 5.0.51b\r
\r
-### Partially Supported Databases (not Unit-Tested)\r
-- [MySQL](http://mysql.com)\r
-\r
-Support for others is planned and may only require creating a simple "dialect" class.\r
+Support for others is possible and may only require creating a simple "dialect" class.\r
\r
### Java Runtime Requirement\r
\r
\r
### iciql+database performance comparison\r
\r
-The following data was generated by running the iciql test suite. The suite is almost completely single-threaded. All databases are run in embedded *memory-only* mode through a JDBC connection. Since the suite is running in memory-only mode, disk IO bottlenecks should be removed from the equation and the results should be measuring raw statement processing.\r
+The following data was generated by running the iciql test suite. The suite is almost completely single-threaded. All Java databases are run in embedded *memory-only* mode through a JDBC connection.\r
\r
<pre>\r
%DBPERFORMANCE%\r
\r
**%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) *released %BUILDDATE%*\r
\r
-- Disabled 2 concurrency unit tests since I believe they are flawed and do not yield reproducible results\r
+- Finished MySQL dialect implementation. MySQL 5.0.51b passes 100% of tests.\r
+- Added Db.dropTable(T) method\r
+\r
+### Older Releases\r
+\r
+**0.6.6** *released 2011-08-15*\r
+\r
+- Disabled two concurrency unit tests since I believe they are flawed and do not yield reproducible results\r
- Added Derby database dialect. Derby 10.7.1.1 and 10.8.1.2 pass 100% of tests.\r
- Implemented HSQL MERGE syntax. HSQL 2.2.4 fails 1 test which is a known [bug in HSQL](https://sourceforge.net/tracker/?func=detail&aid=3390047&group_id=23316&atid=378131)\r
- Updated to H2 1.3.159\r
\r
-### Older Releases\r
-\r
**0.6.5** *released 2011-08-12*\r
\r
- fixed failure of db.delete(PrimitiveModel) and db.update(PrimitiveModel)\r
import com.iciql.Iciql.IQTable;\r
import com.iciql.Iciql.IQVersion;\r
import com.iciql.util.JdbcUtils;\r
+import com.iciql.util.StatementLogger;\r
import com.iciql.util.StringUtils;\r
import com.iciql.util.Utils;\r
import com.iciql.util.WeakIdentityHashMap;\r
\r
public <T> void insert(T t) {\r
Class<?> clazz = t.getClass();\r
- define(clazz).createTableIfRequired(this).insert(this, t, false);\r
+ long rc = define(clazz).createTableIfRequired(this).insert(this, t, false);\r
+ if (rc == 0) {\r
+ throw new IciqlException("Failed to insert {0}. Affected rowcount == 0.", t);\r
+ }\r
}\r
\r
public <T> long insertAndGetKey(T t) {\r
return Query.from(this, alias);\r
}\r
\r
+ @SuppressWarnings("unchecked")\r
+ public <T> int dropTable(Class<? extends T> modelClass) {\r
+ TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
+ SQLStatement stat = new SQLStatement(this);\r
+ getDialect().prepareDropTable(stat, def);\r
+ StatementLogger.drop(stat.getSQL());\r
+ int rc = 0;\r
+ try {\r
+ rc = stat.executeUpdate();\r
+ } catch (IciqlException e) {\r
+ if (e.getIciqlCode() != IciqlException.CODE_SCHEMA_NOT_FOUND\r
+ && e.getIciqlCode() != IciqlException.CODE_TABLE_NOT_FOUND) {\r
+ throw e;\r
+ }\r
+ }\r
+ // remove this model class from the table definition cache\r
+ classMap.remove(modelClass);\r
+ return rc;\r
+ }\r
+\r
@SuppressWarnings("unchecked")\r
public <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {\r
List<T> result = new ArrayList<T>();\r
IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class);\r
if (model.value() > 0) {\r
DbVersion v = new DbVersion();\r
- DbVersion dbVersion =\r
// (SCHEMA="" && TABLE="") == DATABASE\r
- from(v).where(v.schemaName).is("").and(v.tableName).is("").selectFirst();\r
+ DbVersion dbVersion = from(v).where(v.schemaName).is("").and(v.tableName).is("")\r
+ .selectFirst();\r
if (dbVersion == null) {\r
// database has no version registration, but model specifies\r
// version: insert DbVersion entry and return.\r
DbVersion newDb = new DbVersion(model.value());\r
- insert(newDb);\r
+ // database is an older version than the model\r
+ boolean success = dbUpgrader.upgradeDatabase(this, 0, newDb.version);\r
+ if (success) {\r
+ insert(newDb);\r
+ }\r
} else {\r
// database has a version registration:\r
// check to see if upgrade is required.\r
// table is using iciql version tracking.\r
DbVersion v = new DbVersion();\r
String schema = StringUtils.isNullOrEmpty(model.schemaName) ? "" : model.schemaName;\r
- DbVersion dbVersion = from(v).where(v.schemaName).like(schema).and(v.tableName)\r
- .like(model.tableName).selectFirst();\r
+ DbVersion dbVersion = from(v).where(v.schemaName).is(schema).and(v.tableName)\r
+ .is(model.tableName).selectFirst();\r
if (dbVersion == null) {\r
// table has no version registration, but model specifies\r
// version: insert DbVersion entry\r
upgradeChecked.clear();\r
}\r
\r
- SQLDialect getDialect() {\r
+ public SQLDialect getDialect() {\r
return dialect;\r
}\r
\r
\r
public static final int CODE_UNMAPPED_FIELD = 1;\r
public static final int CODE_DUPLICATE_KEY = 2;\r
- public static final int CODE_TABLE_NOT_FOUND = 3;\r
- public static final int CODE_TABLE_ALREADY_EXISTS = 4;\r
- public static final int CODE_INDEX_ALREADY_EXISTS = 5;\r
+ public static final int CODE_SCHEMA_NOT_FOUND = 3;\r
+ public static final int CODE_TABLE_NOT_FOUND = 4;\r
+ public static final int CODE_TABLE_ALREADY_EXISTS = 5;\r
+ public static final int CODE_INDEX_ALREADY_EXISTS = 6;\r
\r
private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?";\r
\r
// http://developer.mimer.com/documentation/html_92/Mimer_SQL_Mobile_DocSet/App_Return_Codes2.html\r
SQLException s = (SQLException) t;\r
String state = s.getSQLState();\r
- if ("23505".equals(state)) {\r
+ if ("23000".equals(state)) {\r
+ // MySQL\r
iciqlCode = CODE_DUPLICATE_KEY;\r
+ } else if ("23505".equals(state)) {\r
+ iciqlCode = CODE_DUPLICATE_KEY;\r
+ } else if ("42000".equals(state)) {\r
+ // MySQL\r
+ iciqlCode = CODE_DUPLICATE_KEY;\r
+ } else if ("42Y07".equals(state)) {\r
+ // Derby\r
+ iciqlCode = CODE_SCHEMA_NOT_FOUND;\r
} else if ("42X05".equals(state)) {\r
// Derby\r
iciqlCode = CODE_TABLE_NOT_FOUND;\r
*/
void configureDialect(String databaseName, DatabaseMetaData data);
+ /**
+ * Allows a dialect to substitute an SQL type.
+ *
+ * @param sqlType
+ * @return the dialect-safe type
+ */
+ String convertSqlType(String sqlType);
+
/**
* Returns a properly formatted table name for the dialect.
*
*/
<T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def);
+ /**
+ * Get the DROP TABLE statement.
+ *
+ * @param stat
+ * @param def
+ */
+ <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def);
+
/**
* Get the CREATE INDEX statement.
*
* @param sqlType\r
* @return the SQL type or a preferred alternative\r
*/\r
- protected String convertSqlType(String sqlType) {\r
+ @Override\r
+ public String convertSqlType(String sqlType) {\r
return sqlType;\r
}\r
\r
return name;\r
}\r
\r
+ @Override\r
+ public <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder("DROP TABLE IF EXISTS "\r
+ + prepareTableName(def.schemaName, def.tableName));\r
+ stat.setSQL(buff.toString());\r
+ return;\r
+ }\r
+\r
@Override\r
public <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def) {\r
StatementBuilder buff;\r
}\r
\r
@Override\r
- protected String convertSqlType(String sqlType) {\r
+ public String convertSqlType(String sqlType) {\r
if ("TINYINT".equals(sqlType)) {\r
// Derby does not have a TINYINT/BYTE type\r
return "SMALLINT";\r
return false;\r
}\r
\r
+ @Override\r
+ public <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder("DROP TABLE "\r
+ + prepareTableName(def.schemaName, def.tableName));\r
+ stat.setSQL(buff.toString());\r
+ return;\r
+ }\r
+\r
@Override\r
public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) {\r
StatementBuilder buff = new StatementBuilder();\r
\r
package com.iciql;\r
\r
+import com.iciql.TableDefinition.FieldDefinition;\r
import com.iciql.TableDefinition.IndexDefinition;\r
import com.iciql.util.StatementBuilder;\r
\r
public class SQLDialectMySQL extends SQLDialectDefault {\r
\r
@Override\r
- protected String convertSqlType(String sqlType) {\r
+ public String convertSqlType(String sqlType) {\r
if (sqlType.equals("CLOB")) {\r
return "TEXT";\r
}\r
buff.append(prepareColumnName(col));\r
}\r
buff.append(") ");\r
+ stat.setSQL(buff.toString().trim());\r
+ }\r
\r
- // USING\r
- switch (index.type) {\r
- case HASH:\r
- buff.append("USING HASH");\r
- break;\r
- case UNIQUE_HASH:\r
- buff.append("USING HASH");\r
- break;\r
+ @Override\r
+ public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,\r
+ TableDefinition<T> def, Object obj) {\r
+ StatementBuilder buff = new StatementBuilder("INSERT INTO ");\r
+ buff.append(prepareTableName(schemaName, tableName)).append(" (");\r
+ buff.resetCount();\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(field.columnName);\r
}\r
- stat.setSQL(buff.toString().trim());\r
+ buff.resetCount();\r
+ buff.append(") VALUES (");\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append('?');\r
+ Object value = def.getValue(obj, field);\r
+ stat.addParameter(value);\r
+ }\r
+ buff.append(") ON DUPLICATE KEY UPDATE ");\r
+ buff.resetCount();\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(field.columnName);\r
+ buff.append("=VALUES(");\r
+ buff.append(field.columnName);\r
+ buff.append(')');\r
+ }\r
+ stat.setSQL(buff.toString());\r
}\r
}
\ No newline at end of file
try {\r
stat.executeUpdate();\r
} catch (IciqlException e) {\r
- if (e.getIciqlCode() != IciqlException.CODE_INDEX_ALREADY_EXISTS) {\r
+ if (e.getIciqlCode() != IciqlException.CODE_INDEX_ALREADY_EXISTS\r
+ && e.getIciqlCode() != IciqlException.CODE_DUPLICATE_KEY) {\r
throw e;\r
}\r
}\r
if (!isNullOrEmpty(schema)) {
if (isNullOrEmpty(def.schemaName)) {
remarks.add(consider(table, "SCHEMA",
- format("@{0}(name={1})", IQSchema.class.getSimpleName(), schema)));
+ format("@{0}(\"{1}\")", IQSchema.class.getSimpleName(), schema)));
} else if (!schema.equalsIgnoreCase(def.schemaName)) {
remarks.add(error(
table,
"SCHEMA",
- format("@{0}(name={1}) != {2}", IQSchema.class.getSimpleName(), def.schemaName,
- schema)).throwError(throwError));
+ format("@{0}(\"{1}\") != {2}", IQSchema.class.getSimpleName(), def.schemaName, schema))
+ .throwError(throwError));
}
}
downloadFromApache(MavenObject.H2, BuildType.COMPILETIME);\r
downloadFromApache(MavenObject.HSQLDB, BuildType.RUNTIME);\r
downloadFromApache(MavenObject.DERBY, BuildType.RUNTIME);\r
+ downloadFromApache(MavenObject.MYSQL, BuildType.RUNTIME);\r
downloadFromApache(MavenObject.JCOMMANDER, BuildType.RUNTIME);\r
downloadFromApache(MavenObject.JCOMMANDER, BuildType.COMPILETIME);\r
downloadFromApache(MavenObject.MARKDOWNPAPERS, BuildType.RUNTIME);\r
"219a3540f3b27d7cc3b1d91d6ea046cd8723290e", "0bb50eec177acf0e94d58e0cf07262fe5164331d",\r
"c7adc475ca40c288c93054e0f4fe58f3a98c0cb5");\r
\r
- public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.159",\r
- "dd89f939661eb5593909584e1c243db0c25de130", "4d953bf765e8a13c7e06ca51165438338966c698",\r
- "4c79ed03f994820a1a873150c8a9f13c667784d3");\r
+ public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.158",\r
+ "4bac13427caeb32ef6e93b70101e61f370c7b5e2", "6bb165156a0831879fa7797df6e18bdcd4421f2d",\r
+ "446d3f58c44992534cb54f67134532d95961904a");\r
\r
public static final MavenObject HSQLDB = new MavenObject("org/hsqldb", "hsqldb", "2.2.4",\r
"6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "", "");\r
public static final MavenObject DERBY = new MavenObject("org/apache/derby", "derby", "10.8.1.2",\r
"2f8717d96eafe3eef3de445ba653f142d54ddab1", "", "");\r
\r
+ public static final MavenObject MYSQL = new MavenObject("mysql", "mysql-connector-java", "5.1.15",\r
+ "0fbc80454d27cc65f3addfa516707e9f8e60c3eb", "", "");\r
+\r
public static final MavenObject JUNIT = new MavenObject("junit", "junit", "4.8.2",\r
"c94f54227b08100974c36170dcb53329435fe5ad", "", "");\r
\r
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
+import com.iciql.IciqlException;
+
/**
* Utility class to optionally log generated statements to StatementListeners.<br>
* Statement logging is disabled by default.
* Enumeration of the different statement types that are logged.
*/
public enum StatementType {
- STAT, TOTAL, CREATE, INSERT, UPDATE, MERGE, DELETE, SELECT;
+ STAT, TOTAL, CREATE, INSERT, UPDATE, MERGE, DELETE, SELECT, DROP;
}
/**
private static final AtomicLong UPDATE_COUNT = new AtomicLong();
private static final AtomicLong MERGE_COUNT = new AtomicLong();
private static final AtomicLong DELETE_COUNT = new AtomicLong();
+ private static final AtomicLong DROP_COUNT = new AtomicLong();
/**
* Activates the Console Logger.
*/
public static void activateConsoleLogger() {
- LISTENERS.add(CONSOLE);
+ registerListener(CONSOLE);
}
/**
* Deactivates the Console Logger.
*/
public static void deactivateConsoleLogger() {
- LISTENERS.remove(CONSOLE);
+ unregisterListener(CONSOLE);
}
/**
* @param listener
*/
public static void unregisterListener(StatementListener listener) {
- LISTENERS.remove(listener);
+ if (!LISTENERS.remove(listener)) {
+ throw new IciqlException("Failed to remove statement listener {0}", listener);
+ }
}
public static void create(String statement) {
logStatement(StatementType.SELECT, statement);
}
+ public static void drop(String statement) {
+ DROP_COUNT.incrementAndGet();
+ logStatement(StatementType.DROP, statement);
+ }
+
private static void logStatement(final StatementType type, final String statement) {
for (final StatementListener listener : LISTENERS) {
EXEC.execute(new Runnable() {
return SELECT_COUNT.longValue();
}
+ public static long getDropCount() {
+ return DROP_COUNT.longValue();
+ }
+
public static long getTotalCount() {
return getCreateCount() + getInsertCount() + getUpdateCount() + getDeleteCount() + getMergeCount()
- + getSelectCount();
+ + getSelectCount() + getDropCount();
}
public static void logStats() {
logStat(StatementType.MERGE, getMergeCount());
logStat(StatementType.DELETE, getDeleteCount());
logStat(StatementType.SELECT, getSelectCount());
+ logStat(StatementType.DROP, getDropCount());
logStatement(StatementType.STAT, "========================");
logStat(StatementType.TOTAL, getTotalCount());
}
// test indexes are created, and columns are in the right order
DatabaseMetaData meta = db.getConnection().getMetaData();
boolean isH2 = meta.getDatabaseProductName().equals("H2");
- boolean isDerby = meta.getDatabaseProductName().equals("Apache Derby");
- ResultSet rs;
- if (isDerby) {
- // Derby defaults to USERNAME schema
- rs = meta.getIndexInfo(null, "SA", "ANNOTATEDPRODUCT", false, true);
- } else {
- // H2, HSQL default to PUBLIC schema
- rs = meta.getIndexInfo(null, "PUBLIC", "ANNOTATEDPRODUCT", false, true);
- }
+ String schema = IciqlSuite.getDefaultSchema(db);
+ ResultSet rs = meta.getIndexInfo(null, schema, "ANNOTATEDPRODUCT", false, true);
+
// first index is primary key index
// H2 gives this a testable name.
assertTrue(rs.next());
if (isH2) {
- assertStartsWith(rs.getString("INDEX_NAME"), "PRIMARY_KEY");
+ assertStartsWith(rs.getString("INDEX_NAME").toUpperCase(), "PRIMARY_KEY");
}
assertTrue(rs.next());
- assertStartsWith(rs.getString("INDEX_NAME"), "ANNOTATEDPRODUCT_0");
- assertStartsWith(rs.getString("COLUMN_NAME"), "NAME");
+ assertStartsWith(rs.getString("INDEX_NAME").toUpperCase(), "ANNOTATEDPRODUCT_0");
+ assertStartsWith(rs.getString("COLUMN_NAME").toUpperCase(), "NAME");
assertTrue(rs.next());
- assertStartsWith(rs.getString("INDEX_NAME"), "ANNOTATEDPRODUCT_0");
- assertStartsWith(rs.getString("COLUMN_NAME"), "CAT");
+ assertStartsWith(rs.getString("INDEX_NAME").toUpperCase(), "ANNOTATEDPRODUCT_0");
+ assertStartsWith(rs.getString("COLUMN_NAME").toUpperCase(), "CAT");
assertTrue(rs.next());
- assertStartsWith(rs.getString("INDEX_NAME"), "NAMEIDX");
- assertStartsWith(rs.getString("COLUMN_NAME"), "NAME");
+ assertStartsWith(rs.getString("INDEX_NAME").toUpperCase(), "NAMEIDX");
+ assertStartsWith(rs.getString("COLUMN_NAME").toUpperCase(), "NAME");
assertFalse(rs.next());
}
db.executeUpdate(MessageFormat.format(create, "VARCHAR(255)"));
db.insertAll(StringRecord.getList());
testSimpleUpdate(db, "VARCHAR fail");
+ db.executeUpdate("DROP TABLE CLOB_TEST");
db.close();
db = IciqlSuite.openNewDb();
- db.executeUpdate(MessageFormat.format(create, "CLOB"));
+ db.executeUpdate(MessageFormat.format(create, db.getDialect().convertSqlType("CLOB")));
db.insertAll(StringRecord.getList());
testSimpleUpdate(db, "CLOB fail because of single quote artifacts");
+ db.executeUpdate("DROP TABLE CLOB_TEST");
db.close();
}
import com.beust.jcommander.Parameters;\r
import com.iciql.Constants;\r
import com.iciql.Db;\r
+import com.iciql.test.models.BooleanModel;\r
+import com.iciql.test.models.ComplexObject;\r
+import com.iciql.test.models.Customer;\r
+import com.iciql.test.models.DefaultValuesModel;\r
+import com.iciql.test.models.EnumModels.EnumIdModel;\r
+import com.iciql.test.models.EnumModels.EnumOrdinalModel;\r
+import com.iciql.test.models.EnumModels.EnumStringModel;\r
+import com.iciql.test.models.Order;\r
+import com.iciql.test.models.PrimitivesModel;\r
+import com.iciql.test.models.Product;\r
+import com.iciql.test.models.ProductAnnotationOnly;\r
+import com.iciql.test.models.ProductInheritedAnnotation;\r
+import com.iciql.test.models.ProductMixedAnnotation;\r
+import com.iciql.test.models.SupportedTypes;\r
import com.iciql.util.StatementLogger;\r
import com.iciql.util.StatementLogger.StatementListener;\r
import com.iciql.util.StatementLogger.StatementType;\r
@RunWith(Suite.class)\r
@SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class,\r
ConcurrencyTest.class, EnumsTest.class, ModelsTest.class, PrimitivesTest.class,\r
- RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UUIDTest.class })\r
+ RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, UUIDTest.class })\r
public class IciqlSuite {\r
\r
- private static final TestDb[] TEST_DBS = { new TestDb("H2", "jdbc:h2:mem:db{0,number,000}"),\r
- new TestDb("HSQL", "jdbc:hsqldb:mem:db{0,number,000}"),\r
- new TestDb("Derby", "jdbc:derby:memory:db{0,number,000};create=true") };\r
+ private static final TestDb[] TEST_DBS = { new TestDb("H2 (embedded)", "jdbc:h2:mem:db{0,number,000}"),\r
+ new TestDb("HSQL (embedded)", "jdbc:hsqldb:mem:db{0,number,000}"),\r
+ new TestDb("Derby (embedded)", "jdbc:derby:memory:db{0,number,000};create=true"),\r
+ new TestDb("MySQL (tcp/myisam)", "jdbc:mysql://localhost:3306/iciql") };\r
\r
private static final TestDb DEFAULT_TEST_DB = TEST_DBS[0];\r
\r
value.startsWith(startsWith));\r
}\r
\r
+ public static boolean equivalentTo(double expected, double actual) {\r
+ if (Double.compare(expected, actual) == 0) {\r
+ return true;\r
+ }\r
+ return Math.abs(expected - actual) <= 0.000001d;\r
+ }\r
+\r
/**\r
* Increment the database counter, open and create a new database.\r
* \r
testUrl = DEFAULT_TEST_DB.url;\r
}\r
testUrl = MessageFormat.format(testUrl, openCount.incrementAndGet());\r
- return Db.open(testUrl, username, password);\r
+ Db db = Db.open(testUrl, username, password);\r
+\r
+ // drop tables\r
+ db.dropTable(BooleanModel.class);\r
+ db.dropTable(ComplexObject.class);\r
+ db.dropTable(Customer.class);\r
+ db.dropTable(DefaultValuesModel.class);\r
+ db.dropTable(EnumIdModel.class);\r
+ db.dropTable(EnumOrdinalModel.class);\r
+ db.dropTable(EnumStringModel.class);\r
+ db.dropTable(Order.class);\r
+ db.dropTable(PrimitivesModel.class);\r
+ db.dropTable(Product.class);\r
+ db.dropTable(ProductAnnotationOnly.class);\r
+ db.dropTable(ProductInheritedAnnotation.class);\r
+ db.dropTable(ProductMixedAnnotation.class);\r
+ db.dropTable(SupportedTypes.class);\r
+\r
+ return db;\r
}\r
\r
/**\r
return Db.open(testUrl, username, password);\r
}\r
\r
+ /**\r
+ * Drops all tables from the current database.\r
+ * \r
+ * @return the current database\r
+ */\r
+ public static void dropAllTables(Db db) {\r
+\r
+ }\r
+\r
/**\r
* Returns the name of the underlying database engine for the Db object.\r
* \r
return IciqlSuite.getDatabaseEngineName(db).equals("H2");\r
}\r
\r
+ /**\r
+ * Returns true if the underlying database engine is MySQL.\r
+ * \r
+ * @param db\r
+ * @return true if underlying database engine is MySQL\r
+ */\r
+ public static boolean isMySQL(Db db) {\r
+ return IciqlSuite.getDatabaseEngineName(db).equals("MySQL");\r
+ }\r
+\r
/**\r
* Gets the default schema of the underlying database engine.\r
* \r
if (isDerby(db)) {\r
// Derby sets default schema name to username\r
return username.toUpperCase();\r
+ } else if (isMySQL(db)) {\r
+ // MySQL does not have schemas\r
+ return null;\r
}\r
+\r
return "PUBLIC";\r
}\r
\r
\r
SuiteClasses suiteClasses = IciqlSuite.class.getAnnotation(SuiteClasses.class);\r
long quickestDatabase = Long.MAX_VALUE;\r
- String dividerMajor = buildDivider('*', 70);\r
- String dividerMinor = buildDivider('-', 70);\r
+ String dividerMajor = buildDivider('*', 79);\r
+ String dividerMinor = buildDivider('-', 79);\r
\r
// Header\r
out.println(dividerMajor);\r
showProperty("available processors", "" + Runtime.getRuntime().availableProcessors());\r
showProperty(\r
"available memory",\r
- MessageFormat.format("{0,number,#.0} GB", ((double) Runtime.getRuntime().maxMemory())\r
+ MessageFormat.format("{0,number,0.0} GB", ((double) Runtime.getRuntime().maxMemory())\r
/ (1024 * 1024)));\r
out.println();\r
\r
statementWriter.append("\n\n");\r
}\r
\r
- System.setProperty("iciql.url", testDb.url);\r
- Result result = JUnitCore.runClasses(suiteClasses.value());\r
- testDb.runtime = result.getRunTime();\r
- if (testDb.runtime < quickestDatabase) {\r
- quickestDatabase = testDb.runtime;\r
- }\r
- testDb.statements = StatementLogger.getTotalCount() - lastCount;\r
- // reset total count for next database\r
- lastCount = StatementLogger.getTotalCount();\r
-\r
- out.println(MessageFormat.format(\r
- "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs",\r
- result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(),\r
- testDb.statements, result.getRunTime() / 1000f));\r
-\r
- if (result.getFailureCount() == 0) {\r
- out.println();\r
+ if (testDb.getVersion().equals("OFFLINE")) {\r
+ // Database not available\r
+ out.println("Skipping. Could not find " + testDb.url);\r
} else {\r
- for (Failure failure : result.getFailures()) {\r
- out.println(MessageFormat.format("\n + {0}\n {1}\n", failure.getTestHeader(),\r
- failure.getMessage()));\r
+ // Test database\r
+ System.setProperty("iciql.url", testDb.url);\r
+ Result result = JUnitCore.runClasses(suiteClasses.value());\r
+ testDb.runtime = result.getRunTime();\r
+ if (testDb.runtime < quickestDatabase) {\r
+ quickestDatabase = testDb.runtime;\r
+ }\r
+ testDb.statements = StatementLogger.getTotalCount() - lastCount;\r
+ // reset total count for next database\r
+ lastCount = StatementLogger.getTotalCount();\r
+\r
+ out.println(MessageFormat.format(\r
+ "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs",\r
+ result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(),\r
+ testDb.statements, result.getRunTime() / 1000f));\r
+\r
+ if (result.getFailureCount() == 0) {\r
+ out.println();\r
+ out.println(" 100% successful test suite run.");\r
+ out.println();\r
+ } else {\r
+ for (Failure failure : result.getFailures()) {\r
+ out.println(MessageFormat.format("\n + {0}\n {1}", failure.getTestHeader(),\r
+ failure.getMessage()));\r
+ }\r
+ out.println();\r
}\r
}\r
}\r
\r
// Display runtime results sorted by performance leader\r
+ out.println();\r
out.println(dividerMajor);\r
out.println(MessageFormat.format("{0} {1} ({2}) test suite performance results", Constants.NAME,\r
Constants.VERSION, Constants.VERSION_DATE));\r
\r
@Override\r
public int compare(TestDb o1, TestDb o2) {\r
+ if (o1.runtime == 0) {\r
+ return 1;\r
+ }\r
+ if (o2.runtime == 0) {\r
+ return -1;\r
+ }\r
if (o1.runtime == o2.runtime) {\r
return 0;\r
}\r
});\r
for (TestDb testDb : dbs) {\r
out.println(MessageFormat.format(\r
- "{0} {1} {2,number,0.0} stats/sec {3,number,0.000} secs ({4,number,#.0}x)",\r
- StringUtils.pad(testDb.name, 6, " ", true),\r
- StringUtils.pad(testDb.getVersion(), 22, " ", true),\r
- ((double) testDb.statements)/ (testDb.runtime/1000d), testDb.runtime / 1000f,\r
- ((double) testDb.runtime) / quickestDatabase));\r
+ "{0} {1} {2,number,0} stats/sec {3,number,0.000} secs ({4,number,0.0}x)",\r
+ StringUtils.pad(testDb.name, 20, " ", true),\r
+ StringUtils.pad(testDb.getVersion(), 22, " ", true), ((double) testDb.statements)\r
+ / (testDb.runtime / 1000d), testDb.runtime / 1000f, ((double) testDb.runtime)\r
+ / quickestDatabase));\r
}\r
\r
// close PrintStream and restore System.err\r
version = db.getConnection().getMetaData().getDatabaseProductVersion();\r
db.close();\r
return version;\r
- } catch (SQLException s) {\r
- version = "";\r
+ } catch (Throwable t) {\r
+ version = "OFFLINE";\r
}\r
}\r
return version;\r
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import org.junit.After;
import org.junit.Before;
import com.iciql.Db;
import com.iciql.DbInspector;
-import com.iciql.DbUpgrader;
-import com.iciql.DbVersion;
-import com.iciql.Iciql.IQVersion;
import com.iciql.ValidationRemark;
import com.iciql.test.models.Product;
import com.iciql.test.models.ProductAnnotationOnly;
import com.iciql.test.models.ProductMixedAnnotation;
import com.iciql.test.models.SupportedTypes;
-import com.iciql.test.models.SupportedTypes.SupportedTypes2;
+import com.iciql.util.StringUtils;
/**
* Test that the mapping between classes and tables is done correctly.
@Test
public void testValidateModels() {
- boolean isH2 = IciqlSuite.isH2(db);
- boolean isDerby = IciqlSuite.isDerby(db);
String schemaName = IciqlSuite.getDefaultSchema(db);
-
DbInspector inspector = new DbInspector(db);
- validateModel(inspector, schemaName, new Product(), 3);
- validateModel(inspector, schemaName, new ProductAnnotationOnly(), (isH2 || isDerby) ? 2 : 3);
+ validateModel(inspector, schemaName, new ProductAnnotationOnly(), 2);
validateModel(inspector, schemaName, new ProductMixedAnnotation(), 4);
}
errorCollector.addError(new SQLException(remark.toString()));
}
}
- assertTrue(remarks.get(0).message.equals(MessageFormat.format("@IQSchema(name={0})", schemaName)));
- assertEquals(sb.toString(), expected, remarks.size());
+
+ if (StringUtils.isNullOrEmpty(schemaName)) {
+ // no schema expected
+ assertEquals(sb.toString(), expected - 1, remarks.size());
+ } else {
+ assertEquals(sb.toString(), expected, remarks.size());
+ assertEquals(MessageFormat.format("@IQSchema(\"{0}\")", schemaName), remarks.get(0).message);
+ }
}
@Test
assertEquals(1489, models.get(0).length());
}
}
-
- @Test
- public void testDatabaseUpgrade() {
- // insert a database version record
- db.insert(new DbVersion(1));
-
- TestDbUpgrader dbUpgrader = new TestDbUpgrader();
- db.setDbUpgrader(dbUpgrader);
-
- List<SupportedTypes> original = SupportedTypes.createList();
- db.insertAll(original);
-
- assertEquals(1, dbUpgrader.oldVersion.get());
- assertEquals(2, dbUpgrader.newVersion.get());
- }
-
- @Test
- public void testTableUpgrade() {
- Db db = IciqlSuite.openNewDb();
-
- // insert first, this will create version record automatically
- List<SupportedTypes> original = SupportedTypes.createList();
- db.insertAll(original);
-
- // reset the dbUpgrader (clears the update check cache)
- TestDbUpgrader dbUpgrader = new TestDbUpgrader();
- db.setDbUpgrader(dbUpgrader);
-
- SupportedTypes2 s2 = new SupportedTypes2();
-
- List<SupportedTypes2> types = db.from(s2).select();
- assertEquals(10, types.size());
- assertEquals(1, dbUpgrader.oldVersion.get());
- assertEquals(2, dbUpgrader.newVersion.get());
- db.close();
- }
-
- /**
- * A sample database upgrader class.
- */
- @IQVersion(2)
- class TestDbUpgrader implements DbUpgrader {
- final AtomicInteger oldVersion = new AtomicInteger(0);
- final AtomicInteger newVersion = new AtomicInteger(0);
-
- public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) {
- // just claims success on upgrade request
- oldVersion.set(fromVersion);
- newVersion.set(toVersion);
- return true;
- }
-
- public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) {
- // just claims success on upgrade request
- oldVersion.set(fromVersion);
- newVersion.set(toVersion);
- return true;
- }
-
- }
-
}
TestReverse check = db.from(new TestReverse()).selectFirst();\r
assertEquals(t.name, check.name);\r
assertEquals(t.id, check.id);\r
+ db.executeUpdate("DROP TABLE testreverse");\r
}\r
\r
@Test\r
public void testIsNull() {\r
Product p = new Product();\r
String sql = db.from(p).whereTrue(isNull(p.productName)).getSQL();\r
- assertEquals("SELECT * FROM Product WHERE (productName IS NULL)", sql);\r
+ assertEquals("SELECT * FROM Product WHERE (" + db.getDialect().prepareColumnName("productName")\r
+ + " IS NULL)", sql);\r
}\r
\r
@Test\r
public void testOrAndNot() {\r
Product p = new Product();\r
String sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL();\r
- assertEquals("SELECT * FROM Product WHERE (NOT productName IS NULL)", sql);\r
+ String productName = db.getDialect().prepareColumnName("productName");\r
+ assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql);\r
sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL();\r
- assertEquals("SELECT * FROM Product WHERE (NOT productName IS NULL)", sql);\r
+ assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql);\r
sql = db.from(p).whereTrue(db.test(p.productId).is(1)).getSQL();\r
- assertEquals("SELECT * FROM Product WHERE ((productId = ?))", sql);\r
+ String productId = db.getDialect().prepareColumnName("productId");\r
+ assertEquals("SELECT * FROM Product WHERE ((" + productId + " = ?))", sql);\r
}\r
\r
@Test\r
.lessThan(java.sql.Timestamp.valueOf("2005-05-05 05:05:05")).and(co.name).is("hello")\r
.and(co.time).lessThan(java.sql.Time.valueOf("23:23:23")).and(co.value)\r
.is(new BigDecimal("1")).getSQL();\r
- assertEquals("SELECT * FROM ComplexObject WHERE id = ? AND amount = ? "\r
- + "AND birthday < ? AND created < ? AND name = ? AND time < ? AND value = ?", sql);\r
+\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("SELECT * FROM ComplexObject WHERE ");\r
+ sb.append(db.getDialect().prepareColumnName("id"));\r
+ sb.append(" = ? AND ");\r
+ sb.append(db.getDialect().prepareColumnName("amount"));\r
+ sb.append(" = ? AND ");\r
+ sb.append(db.getDialect().prepareColumnName("birthday"));\r
+ sb.append(" < ? AND ");\r
+ sb.append(db.getDialect().prepareColumnName("created"));\r
+ sb.append(" < ? AND ");\r
+ sb.append(db.getDialect().prepareColumnName("name"));\r
+ sb.append(" = ? AND ");\r
+ sb.append(db.getDialect().prepareColumnName("time"));\r
+ sb.append(" < ? AND ");\r
+ sb.append(db.getDialect().prepareColumnName("value"));\r
+ sb.append(" = ?");\r
+ assertEquals(sb.toString(), sql);\r
\r
long count = db.from(co).where(co.id).is(1).and(co.amount).is(1L).and(co.birthday)\r
.lessThan(new java.util.Date()).and(co.created)\r
return co.id == x && co.name.equals(name) && co.name.equals("hello");\r
}\r
}).getSQL();\r
- assertEquals("SELECT * FROM ComplexObject WHERE id=? AND ?=name AND 'hello'=name", sql);\r
+ StringBuilder sb = new StringBuilder();\r
+ sb.append("SELECT * FROM ComplexObject WHERE ");\r
+ sb.append(db.getDialect().prepareColumnName("id"));\r
+ sb.append("=? AND ?=");\r
+ sb.append(db.getDialect().prepareColumnName("name"));\r
+ sb.append(" AND 'hello'=");\r
+ sb.append(db.getDialect().prepareColumnName("name"));\r
+ assertEquals(sb.toString(), sql);\r
\r
long count = db.from(co).where(new Filter() {\r
public boolean where() {\r
import java.util.UUID;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@Test
public void testUUIDs() throws Exception {
- if (!IciqlSuite.isH2(db)) {
- // do not test non-H2 databases
- return;
- }
+ // do not test non-H2 databases
+ Assume.assumeTrue(IciqlSuite.isH2(db));
+
List<UUIDRecord> originals = UUIDRecord.getList();
db.insertAll(originals);
UUIDRecord u = new UUIDRecord();
--- /dev/null
+/*\r
+ * Copyright 2011 James Moger.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.iciql.test;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+\r
+import java.util.List;\r
+import java.util.concurrent.atomic.AtomicInteger;\r
+\r
+import org.junit.Test;\r
+\r
+import com.iciql.Db;\r
+import com.iciql.DbUpgrader;\r
+import com.iciql.Iciql.IQVersion;\r
+import com.iciql.test.models.Product;\r
+import com.iciql.test.models.SupportedTypes;\r
+import com.iciql.test.models.SupportedTypes.SupportedTypes2;\r
+\r
+/**\r
+ * Tests the database and table upgrade functions.\r
+ * \r
+ */\r
+public class UpgradesTest {\r
+\r
+ @Test\r
+ public void testDatabaseUpgrade() {\r
+ Db db = IciqlSuite.openNewDb();\r
+\r
+ List<Product> products = Product.getList();\r
+\r
+ // set the v1 upgrader and insert a record.\r
+ // this will trigger the upgrade.\r
+ V1DbUpgrader v1 = new V1DbUpgrader();\r
+ db.setDbUpgrader(v1);\r
+ db.insert(products.get(0));\r
+\r
+ // confirm that upgrade occurred\r
+ assertEquals(0, v1.oldVersion.get());\r
+ assertEquals(1, v1.newVersion.get());\r
+\r
+ // open a second connection to the database\r
+ // and then apply the v2 upgrade.\r
+ // For H2 its important to keep the first connection\r
+ // alive so that the database is not destroyed.\r
+ Db db2 = IciqlSuite.openCurrentDb();\r
+\r
+ // set the v2 upgrader and insert a record.\r
+ // this will trigger the upgrade.\r
+ V2DbUpgrader v2 = new V2DbUpgrader();\r
+ db2.setDbUpgrader(v2);\r
+ db2.insert(products.get(1));\r
+\r
+ // confirm that upgrade occurred\r
+ assertEquals(1, v2.oldVersion.get());\r
+ assertEquals(2, v2.newVersion.get());\r
+\r
+ db.executeUpdate("DROP TABLE iq_versions");\r
+ db.close();\r
+ db2.close();\r
+ }\r
+\r
+ @Test\r
+ public void testTableUpgrade() {\r
+ Db db = IciqlSuite.openNewDb();\r
+\r
+ // insert first, this will create version record automatically\r
+ List<SupportedTypes> original = SupportedTypes.createList();\r
+ db.insertAll(original);\r
+\r
+ // reset the dbUpgrader (clears the update check cache)\r
+ V2DbUpgrader dbUpgrader = new V2DbUpgrader();\r
+ db.setDbUpgrader(dbUpgrader);\r
+\r
+ SupportedTypes2 s2 = new SupportedTypes2();\r
+\r
+ List<SupportedTypes2> types = db.from(s2).select();\r
+ assertEquals(10, types.size());\r
+ assertEquals(1, dbUpgrader.oldVersion.get());\r
+ assertEquals(2, dbUpgrader.newVersion.get());\r
+ db.executeUpdate("DROP TABLE iq_versions");\r
+ db.close();\r
+ }\r
+\r
+ /**\r
+ * A sample database upgrader class.\r
+ */\r
+ class BaseDbUpgrader implements DbUpgrader {\r
+ final AtomicInteger oldVersion = new AtomicInteger(0);\r
+ final AtomicInteger newVersion = new AtomicInteger(0);\r
+\r
+ public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) {\r
+ // just claims success on upgrade request\r
+ oldVersion.set(fromVersion);\r
+ newVersion.set(toVersion);\r
+ return true;\r
+ }\r
+\r
+ public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) {\r
+ // just claims success on upgrade request\r
+ oldVersion.set(fromVersion);\r
+ newVersion.set(toVersion);\r
+ return true;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * A sample V1 database upgrader class.\r
+ */\r
+ @IQVersion(1)\r
+ class V1DbUpgrader extends BaseDbUpgrader {\r
+ }\r
+\r
+ /**\r
+ * A sample V2 database upgrader class.\r
+ */\r
+ @IQVersion(2)\r
+ class V2DbUpgrader extends BaseDbUpgrader {\r
+ }\r
+\r
+}\r
\r
import com.iciql.Iciql.IQColumn;\r
import com.iciql.Iciql.IQTable;\r
+import com.iciql.test.IciqlSuite;\r
\r
/**\r
* Primitive types model.\r
same &= myShort == p.myShort;\r
same &= myByte == p.myByte;\r
same &= myBoolean == p.myBoolean;\r
- same &= myDouble == p.myDouble;\r
- same &= myFloat == p.myFloat;\r
+ same &= IciqlSuite.equivalentTo(myDouble, p.myDouble);\r
+ same &= IciqlSuite.equivalentTo(myFloat, p.myFloat);\r
return same;\r
}\r
\r
@IQIndexes({ @IQIndex({ "name", "cat" }), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name") })
public class ProductAnnotationOnly {
- @IQColumn(autoIncrement = true)
- public Integer autoIncrement;
-
public String unmappedField;
- @IQColumn(name = "id")
+ @IQColumn(name = "id", autoIncrement = true)
public Integer productId;
@IQColumn(name = "cat", length = 15, trim = true)
import com.iciql.Iciql.IQTable;
import com.iciql.Iciql.IQVersion;
import com.iciql.Iciql.IndexType;
+import com.iciql.test.IciqlSuite;
import com.iciql.test.models.EnumModels.Tree;
import com.iciql.util.Utils;
same &= myShort.equals(s.myShort);
same &= myInteger.equals(s.myInteger);
same &= myLong.equals(s.myLong);
- same &= myFloat.equals(s.myFloat);
- same &= myDouble.equals(s.myDouble);
+ same &= IciqlSuite.equivalentTo(myFloat, s.myFloat);
+ same &= IciqlSuite.equivalentTo(myDouble, s.myDouble);
same &= myBigDecimal.compareTo(s.myBigDecimal) == 0;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
same &= df.format(myUtilDate).equals(df.format(s.myUtilDate));