From d95e16170d063c0b74d04e6636e8fddbfa120689 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 16 Aug 2011 15:32:56 -0400 Subject: [PATCH] Finished MySQL dialect; v5.0.51b 100% tested. Added Db.dropTable(T) --- .classpath | 1 + README.markdown | 7 +- docs/00_index.mkd | 6 +- docs/03_performance.mkd | 2 +- docs/05_releases.mkd | 11 +- src/com/iciql/Db.java | 42 ++++- src/com/iciql/IciqlException.java | 18 ++- src/com/iciql/SQLDialect.java | 16 ++ src/com/iciql/SQLDialectDefault.java | 11 +- src/com/iciql/SQLDialectDerby.java | 10 +- src/com/iciql/SQLDialectMySQL.java | 41 +++-- src/com/iciql/TableDefinition.java | 3 +- src/com/iciql/TableInspector.java | 6 +- src/com/iciql/build/Build.java | 10 +- src/com/iciql/util/StatementLogger.java | 25 ++- tests/com/iciql/test/AnnotationsTest.java | 26 ++- tests/com/iciql/test/ClobTest.java | 4 +- tests/com/iciql/test/IciqlSuite.java | 149 ++++++++++++++---- tests/com/iciql/test/ModelsTest.java | 83 ++-------- tests/com/iciql/test/SamplesTest.java | 41 ++++- tests/com/iciql/test/UUIDTest.java | 8 +- tests/com/iciql/test/UpgradesTest.java | 134 ++++++++++++++++ .../iciql/test/models/PrimitivesModel.java | 5 +- .../test/models/ProductAnnotationOnly.java | 5 +- .../com/iciql/test/models/SupportedTypes.java | 5 +- 25 files changed, 477 insertions(+), 192 deletions(-) create mode 100644 tests/com/iciql/test/UpgradesTest.java diff --git a/.classpath b/.classpath index 1d41ba7..136c201 100644 --- a/.classpath +++ b/.classpath @@ -26,6 +26,7 @@ + diff --git a/README.markdown b/README.markdown index 6c4f5e2..28aa424 100644 --- a/README.markdown +++ b/README.markdown @@ -20,12 +20,9 @@ Supported Databases (Unit-Tested) - [H2](http://h2database.com) 1.3.159 - [HSQLDB](http://hsqldb.org) 2.2.4 - [Derby](http://db.apache.org/derby) 10.7.1.1 & 10.8.1.2 +- [MySQL](http://mysql.com) 5.0.51b -Partially Supported Databases (not Unit-Tested) -------- -- [MySQL](http://mysql.com) - -Support for others is planned and may only require creating a simple "dialect" class. +Support for others is possible and may only require creating a simple "dialect" class. License ------- diff --git a/docs/00_index.mkd b/docs/00_index.mkd index b3e8cbf..daf9c42 100644 --- a/docs/00_index.mkd +++ b/docs/00_index.mkd @@ -39,11 +39,9 @@ select * from products - [H2](http://h2database.com) 1.3.159 - [HSQLDB](http://hsqldb.org) 2.2.4 - [Derby](http://db.apache.org/derby) 10.7.1.1 & 10.8.1.2 +- [MySQL](http://mysql.com) 5.0.51b -### Partially Supported Databases (not Unit-Tested) -- [MySQL](http://mysql.com) - -Support for others is planned and may only require creating a simple "dialect" class. +Support for others is possible and may only require creating a simple "dialect" class. ### Java Runtime Requirement diff --git a/docs/03_performance.mkd b/docs/03_performance.mkd index a8922b7..7df050a 100644 --- a/docs/03_performance.mkd +++ b/docs/03_performance.mkd @@ -9,7 +9,7 @@ Performance of iciql statement generation is not currently benchmarked. ### iciql+database performance comparison -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. +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.
 %DBPERFORMANCE%
diff --git a/docs/05_releases.mkd b/docs/05_releases.mkd
index 91ef18f..8e0dfd6 100644
--- a/docs/05_releases.mkd
+++ b/docs/05_releases.mkd
@@ -6,13 +6,18 @@
 
 **%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%*
 
-- Disabled 2 concurrency unit tests since I believe they are flawed and do not yield reproducible results
+- Finished MySQL dialect implementation.  MySQL 5.0.51b passes 100% of tests.
+- Added Db.dropTable(T) method
+
+### Older Releases
+
+**0.6.6**   *released 2011-08-15*
+
+- Disabled two concurrency unit tests since I believe they are flawed and do not yield reproducible results
 - Added Derby database dialect.  Derby 10.7.1.1 and 10.8.1.2 pass 100% of tests.
 - 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)
 - Updated to H2 1.3.159
 
-### Older Releases
-
 **0.6.5**   *released 2011-08-12*
 
 - fixed failure of db.delete(PrimitiveModel) and db.update(PrimitiveModel)
diff --git a/src/com/iciql/Db.java b/src/com/iciql/Db.java
index bb087e4..09c26a1 100644
--- a/src/com/iciql/Db.java
+++ b/src/com/iciql/Db.java
@@ -38,6 +38,7 @@ import com.iciql.DbUpgrader.DefaultDbUpgrader;
 import com.iciql.Iciql.IQTable;
 import com.iciql.Iciql.IQVersion;
 import com.iciql.util.JdbcUtils;
+import com.iciql.util.StatementLogger;
 import com.iciql.util.StringUtils;
 import com.iciql.util.Utils;
 import com.iciql.util.WeakIdentityHashMap;
@@ -180,7 +181,10 @@ public class Db {
 
 	public  void insert(T t) {
 		Class clazz = t.getClass();
-		define(clazz).createTableIfRequired(this).insert(this, t, false);
+		long rc = define(clazz).createTableIfRequired(this).insert(this, t, false);
+		if (rc == 0) {
+			throw new IciqlException("Failed to insert {0}.  Affected rowcount == 0.", t);
+		}
 	}
 
 	public  long insertAndGetKey(T t) {
@@ -235,6 +239,26 @@ public class Db {
 		return Query.from(this, alias);
 	}
 
+	@SuppressWarnings("unchecked")
+	public  int dropTable(Class modelClass) {
+		TableDefinition def = (TableDefinition) define(modelClass);
+		SQLStatement stat = new SQLStatement(this);
+		getDialect().prepareDropTable(stat, def);
+		StatementLogger.drop(stat.getSQL());
+		int rc = 0;
+		try {
+			rc = stat.executeUpdate();
+		} catch (IciqlException e) {
+			if (e.getIciqlCode() != IciqlException.CODE_SCHEMA_NOT_FOUND
+					&& e.getIciqlCode() != IciqlException.CODE_TABLE_NOT_FOUND) {
+				throw e;
+			}
+		}
+		// remove this model class from the table definition cache
+		classMap.remove(modelClass);
+		return rc;
+	}
+
 	@SuppressWarnings("unchecked")
 	public  List buildObjects(Class modelClass, ResultSet rs) {
 		List result = new ArrayList();
@@ -259,14 +283,18 @@ public class Db {
 			IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class);
 			if (model.value() > 0) {
 				DbVersion v = new DbVersion();
-				DbVersion dbVersion =
 				// (SCHEMA="" && TABLE="") == DATABASE
-				from(v).where(v.schemaName).is("").and(v.tableName).is("").selectFirst();
+				DbVersion dbVersion = from(v).where(v.schemaName).is("").and(v.tableName).is("")
+						.selectFirst();
 				if (dbVersion == null) {
 					// database has no version registration, but model specifies
 					// version: insert DbVersion entry and return.
 					DbVersion newDb = new DbVersion(model.value());
-					insert(newDb);
+					// database is an older version than the model
+					boolean success = dbUpgrader.upgradeDatabase(this, 0, newDb.version);
+					if (success) {
+						insert(newDb);
+					}
 				} else {
 					// database has a version registration:
 					// check to see if upgrade is required.
@@ -293,8 +321,8 @@ public class Db {
 				// table is using iciql version tracking.
 				DbVersion v = new DbVersion();
 				String schema = StringUtils.isNullOrEmpty(model.schemaName) ? "" : model.schemaName;
-				DbVersion dbVersion = from(v).where(v.schemaName).like(schema).and(v.tableName)
-						.like(model.tableName).selectFirst();
+				DbVersion dbVersion = from(v).where(v.schemaName).is(schema).and(v.tableName)
+						.is(model.tableName).selectFirst();
 				if (dbVersion == null) {
 					// table has no version registration, but model specifies
 					// version: insert DbVersion entry
@@ -348,7 +376,7 @@ public class Db {
 		upgradeChecked.clear();
 	}
 
-	SQLDialect getDialect() {
+	public SQLDialect getDialect() {
 		return dialect;
 	}
 
diff --git a/src/com/iciql/IciqlException.java b/src/com/iciql/IciqlException.java
index 7d6f152..524d184 100644
--- a/src/com/iciql/IciqlException.java
+++ b/src/com/iciql/IciqlException.java
@@ -27,9 +27,10 @@ public class IciqlException extends RuntimeException {
 
 	public static final int CODE_UNMAPPED_FIELD = 1;
 	public static final int CODE_DUPLICATE_KEY = 2;
-	public static final int CODE_TABLE_NOT_FOUND = 3;
-	public static final int CODE_TABLE_ALREADY_EXISTS = 4;
-	public static final int CODE_INDEX_ALREADY_EXISTS = 5;
+	public static final int CODE_SCHEMA_NOT_FOUND = 3;
+	public static final int CODE_TABLE_NOT_FOUND = 4;
+	public static final int CODE_TABLE_ALREADY_EXISTS = 5;
+	public static final int CODE_INDEX_ALREADY_EXISTS = 6;
 
 	private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?";
 
@@ -95,8 +96,17 @@ public class IciqlException extends RuntimeException {
 			// http://developer.mimer.com/documentation/html_92/Mimer_SQL_Mobile_DocSet/App_Return_Codes2.html
 			SQLException s = (SQLException) t;
 			String state = s.getSQLState();
-			if ("23505".equals(state)) {
+			if ("23000".equals(state)) {
+				// MySQL
 				iciqlCode = CODE_DUPLICATE_KEY;
+			} else if ("23505".equals(state)) {
+				iciqlCode = CODE_DUPLICATE_KEY;
+			} else if ("42000".equals(state)) {
+				// MySQL
+				iciqlCode = CODE_DUPLICATE_KEY;
+			} else if ("42Y07".equals(state)) {
+				// Derby
+				iciqlCode = CODE_SCHEMA_NOT_FOUND;
 			} else if ("42X05".equals(state)) {
 				// Derby
 				iciqlCode = CODE_TABLE_NOT_FOUND;
diff --git a/src/com/iciql/SQLDialect.java b/src/com/iciql/SQLDialect.java
index 97d9fe9..6e6be25 100644
--- a/src/com/iciql/SQLDialect.java
+++ b/src/com/iciql/SQLDialect.java
@@ -35,6 +35,14 @@ public interface SQLDialect {
 	 */
 	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.
 	 * 
@@ -63,6 +71,14 @@ public interface SQLDialect {
 	 */
 	 void prepareCreateTable(SQLStatement stat, TableDefinition def);
 
+	/**
+	 * Get the DROP TABLE statement.
+	 * 
+	 * @param stat
+	 * @param def
+	 */
+	 void prepareDropTable(SQLStatement stat, TableDefinition def);
+
 	/**
 	 * Get the CREATE INDEX statement.
 	 * 
diff --git a/src/com/iciql/SQLDialectDefault.java b/src/com/iciql/SQLDialectDefault.java
index 69502c3..0b611e0 100644
--- a/src/com/iciql/SQLDialectDefault.java
+++ b/src/com/iciql/SQLDialectDefault.java
@@ -57,7 +57,8 @@ public class SQLDialectDefault implements SQLDialect {
 	 * @param sqlType
 	 * @return the SQL type or a preferred alternative
 	 */
-	protected String convertSqlType(String sqlType) {
+	@Override
+	public String convertSqlType(String sqlType) {
 		return sqlType;
 	}
 
@@ -94,6 +95,14 @@ public class SQLDialectDefault implements SQLDialect {
 		return name;
 	}
 
+	@Override
+	public  void prepareDropTable(SQLStatement stat, TableDefinition def) {
+		StatementBuilder buff = new StatementBuilder("DROP TABLE IF EXISTS "
+				+ prepareTableName(def.schemaName, def.tableName));
+		stat.setSQL(buff.toString());
+		return;
+	}
+
 	@Override
 	public  void prepareCreateTable(SQLStatement stat, TableDefinition def) {
 		StatementBuilder buff;
diff --git a/src/com/iciql/SQLDialectDerby.java b/src/com/iciql/SQLDialectDerby.java
index 42f8cfd..019c285 100644
--- a/src/com/iciql/SQLDialectDerby.java
+++ b/src/com/iciql/SQLDialectDerby.java
@@ -33,7 +33,7 @@ public class SQLDialectDerby extends SQLDialectDefault {
 	}
 
 	@Override
-	protected String convertSqlType(String sqlType) {
+	public String convertSqlType(String sqlType) {
 		if ("TINYINT".equals(sqlType)) {
 			// Derby does not have a TINYINT/BYTE type
 			return "SMALLINT";
@@ -81,6 +81,14 @@ public class SQLDialectDerby extends SQLDialectDefault {
 		return false;
 	}
 
+	@Override
+	public  void prepareDropTable(SQLStatement stat, TableDefinition def) {
+		StatementBuilder buff = new StatementBuilder("DROP TABLE "
+				+ prepareTableName(def.schemaName, def.tableName));
+		stat.setSQL(buff.toString());
+		return;
+	}
+
 	@Override
 	public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) {
 		StatementBuilder buff = new StatementBuilder();
diff --git a/src/com/iciql/SQLDialectMySQL.java b/src/com/iciql/SQLDialectMySQL.java
index a6c1218..1c1674a 100644
--- a/src/com/iciql/SQLDialectMySQL.java
+++ b/src/com/iciql/SQLDialectMySQL.java
@@ -16,6 +16,7 @@
 
 package com.iciql;
 
+import com.iciql.TableDefinition.FieldDefinition;
 import com.iciql.TableDefinition.IndexDefinition;
 import com.iciql.util.StatementBuilder;
 
@@ -25,7 +26,7 @@ import com.iciql.util.StatementBuilder;
 public class SQLDialectMySQL extends SQLDialectDefault {
 
 	@Override
-	protected String convertSqlType(String sqlType) {
+	public String convertSqlType(String sqlType) {
 		if (sqlType.equals("CLOB")) {
 			return "TEXT";
 		}
@@ -75,16 +76,36 @@ public class SQLDialectMySQL extends SQLDialectDefault {
 			buff.append(prepareColumnName(col));
 		}
 		buff.append(") ");
+		stat.setSQL(buff.toString().trim());
+	}
 
-		// USING
-		switch (index.type) {
-		case HASH:
-			buff.append("USING HASH");
-			break;
-		case UNIQUE_HASH:
-			buff.append("USING HASH");
-			break;
+	@Override
+	public  void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+			TableDefinition def, Object obj) {
+		StatementBuilder buff = new StatementBuilder("INSERT INTO ");
+		buff.append(prepareTableName(schemaName, tableName)).append(" (");
+		buff.resetCount();
+		for (FieldDefinition field : def.fields) {
+			buff.appendExceptFirst(", ");
+			buff.append(field.columnName);
 		}
-		stat.setSQL(buff.toString().trim());
+		buff.resetCount();
+		buff.append(") VALUES (");
+		for (FieldDefinition field : def.fields) {
+			buff.appendExceptFirst(", ");
+			buff.append('?');
+			Object value = def.getValue(obj, field);
+			stat.addParameter(value);
+		}
+		buff.append(") ON DUPLICATE KEY UPDATE ");
+		buff.resetCount();
+		for (FieldDefinition field : def.fields) {
+			buff.appendExceptFirst(", ");
+			buff.append(field.columnName);
+			buff.append("=VALUES(");
+			buff.append(field.columnName);
+			buff.append(')');
+		}
+		stat.setSQL(buff.toString());
 	}
 }
\ No newline at end of file
diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java
index 4d9fec2..251f098 100644
--- a/src/com/iciql/TableDefinition.java
+++ b/src/com/iciql/TableDefinition.java
@@ -573,7 +573,8 @@ public class TableDefinition {
 			try {
 				stat.executeUpdate();
 			} catch (IciqlException e) {
-				if (e.getIciqlCode() != IciqlException.CODE_INDEX_ALREADY_EXISTS) {
+				if (e.getIciqlCode() != IciqlException.CODE_INDEX_ALREADY_EXISTS
+						&& e.getIciqlCode() != IciqlException.CODE_DUPLICATE_KEY) {
 					throw e;
 				}
 			}
diff --git a/src/com/iciql/TableInspector.java b/src/com/iciql/TableInspector.java
index dc28956..093055c 100644
--- a/src/com/iciql/TableInspector.java
+++ b/src/com/iciql/TableInspector.java
@@ -433,13 +433,13 @@ public class TableInspector {
 		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));
 			}
 		}
 
diff --git a/src/com/iciql/build/Build.java b/src/com/iciql/build/Build.java
index 0102739..8dfd7df 100644
--- a/src/com/iciql/build/Build.java
+++ b/src/com/iciql/build/Build.java
@@ -58,6 +58,7 @@ public class Build {
 		downloadFromApache(MavenObject.H2, BuildType.COMPILETIME);
 		downloadFromApache(MavenObject.HSQLDB, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.DERBY, BuildType.RUNTIME);
+		downloadFromApache(MavenObject.MYSQL, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.JCOMMANDER, BuildType.RUNTIME);
 		downloadFromApache(MavenObject.JCOMMANDER, BuildType.COMPILETIME);
 		downloadFromApache(MavenObject.MARKDOWNPAPERS, BuildType.RUNTIME);
@@ -170,9 +171,9 @@ public class Build {
 				"219a3540f3b27d7cc3b1d91d6ea046cd8723290e", "0bb50eec177acf0e94d58e0cf07262fe5164331d",
 				"c7adc475ca40c288c93054e0f4fe58f3a98c0cb5");
 
-		public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.159",
-				"dd89f939661eb5593909584e1c243db0c25de130", "4d953bf765e8a13c7e06ca51165438338966c698",
-				"4c79ed03f994820a1a873150c8a9f13c667784d3");
+		public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.158",
+				"4bac13427caeb32ef6e93b70101e61f370c7b5e2", "6bb165156a0831879fa7797df6e18bdcd4421f2d",
+				"446d3f58c44992534cb54f67134532d95961904a");
 
 		public static final MavenObject HSQLDB = new MavenObject("org/hsqldb", "hsqldb", "2.2.4",
 				"6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "", "");
@@ -180,6 +181,9 @@ public class Build {
 		public static final MavenObject DERBY = new MavenObject("org/apache/derby", "derby", "10.8.1.2",
 				"2f8717d96eafe3eef3de445ba653f142d54ddab1", "", "");
 
+		public static final MavenObject MYSQL = new MavenObject("mysql", "mysql-connector-java", "5.1.15",
+				"0fbc80454d27cc65f3addfa516707e9f8e60c3eb", "", "");
+
 		public static final MavenObject JUNIT = new MavenObject("junit", "junit", "4.8.2",
 				"c94f54227b08100974c36170dcb53329435fe5ad", "", "");
 
diff --git a/src/com/iciql/util/StatementLogger.java b/src/com/iciql/util/StatementLogger.java
index 0504c98..b11a5f9 100644
--- a/src/com/iciql/util/StatementLogger.java
+++ b/src/com/iciql/util/StatementLogger.java
@@ -22,6 +22,8 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicLong;
 
+import com.iciql.IciqlException;
+
 /**
  * Utility class to optionally log generated statements to StatementListeners.
* Statement logging is disabled by default. @@ -35,7 +37,7 @@ public class StatementLogger { * 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; } /** @@ -61,19 +63,20 @@ public class StatementLogger { 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); } /** @@ -91,7 +94,9 @@ public class StatementLogger { * @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) { @@ -124,6 +129,11 @@ public class StatementLogger { 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() { @@ -158,9 +168,13 @@ public class StatementLogger { 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() { @@ -172,6 +186,7 @@ public class StatementLogger { logStat(StatementType.MERGE, getMergeCount()); logStat(StatementType.DELETE, getDeleteCount()); logStat(StatementType.SELECT, getSelectCount()); + logStat(StatementType.DROP, getDropCount()); logStatement(StatementType.STAT, "========================"); logStat(StatementType.TOTAL, getTotalCount()); } diff --git a/tests/com/iciql/test/AnnotationsTest.java b/tests/com/iciql/test/AnnotationsTest.java index 7e4e918..4f50d61 100644 --- a/tests/com/iciql/test/AnnotationsTest.java +++ b/tests/com/iciql/test/AnnotationsTest.java @@ -69,30 +69,24 @@ public class AnnotationsTest { // 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()); } diff --git a/tests/com/iciql/test/ClobTest.java b/tests/com/iciql/test/ClobTest.java index 3b32dcb..49cee72 100644 --- a/tests/com/iciql/test/ClobTest.java +++ b/tests/com/iciql/test/ClobTest.java @@ -42,12 +42,14 @@ public class ClobTest { 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(); } diff --git a/tests/com/iciql/test/IciqlSuite.java b/tests/com/iciql/test/IciqlSuite.java index b91f3a4..0389d80 100644 --- a/tests/com/iciql/test/IciqlSuite.java +++ b/tests/com/iciql/test/IciqlSuite.java @@ -40,6 +40,20 @@ import com.beust.jcommander.ParameterException; import com.beust.jcommander.Parameters; import com.iciql.Constants; import com.iciql.Db; +import com.iciql.test.models.BooleanModel; +import com.iciql.test.models.ComplexObject; +import com.iciql.test.models.Customer; +import com.iciql.test.models.DefaultValuesModel; +import com.iciql.test.models.EnumModels.EnumIdModel; +import com.iciql.test.models.EnumModels.EnumOrdinalModel; +import com.iciql.test.models.EnumModels.EnumStringModel; +import com.iciql.test.models.Order; +import com.iciql.test.models.PrimitivesModel; +import com.iciql.test.models.Product; +import com.iciql.test.models.ProductAnnotationOnly; +import com.iciql.test.models.ProductInheritedAnnotation; +import com.iciql.test.models.ProductMixedAnnotation; +import com.iciql.test.models.SupportedTypes; import com.iciql.util.StatementLogger; import com.iciql.util.StatementLogger.StatementListener; import com.iciql.util.StatementLogger.StatementType; @@ -58,12 +72,13 @@ import com.iciql.util.StringUtils; @RunWith(Suite.class) @SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class, ConcurrencyTest.class, EnumsTest.class, ModelsTest.class, PrimitivesTest.class, - RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UUIDTest.class }) + RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, UUIDTest.class }) public class IciqlSuite { - private static final TestDb[] TEST_DBS = { new TestDb("H2", "jdbc:h2:mem:db{0,number,000}"), - new TestDb("HSQL", "jdbc:hsqldb:mem:db{0,number,000}"), - new TestDb("Derby", "jdbc:derby:memory:db{0,number,000};create=true") }; + private static final TestDb[] TEST_DBS = { new TestDb("H2 (embedded)", "jdbc:h2:mem:db{0,number,000}"), + new TestDb("HSQL (embedded)", "jdbc:hsqldb:mem:db{0,number,000}"), + new TestDb("Derby (embedded)", "jdbc:derby:memory:db{0,number,000};create=true"), + new TestDb("MySQL (tcp/myisam)", "jdbc:mysql://localhost:3306/iciql") }; private static final TestDb DEFAULT_TEST_DB = TEST_DBS[0]; @@ -82,6 +97,13 @@ public class IciqlSuite { value.startsWith(startsWith)); } + public static boolean equivalentTo(double expected, double actual) { + if (Double.compare(expected, actual) == 0) { + return true; + } + return Math.abs(expected - actual) <= 0.000001d; + } + /** * Increment the database counter, open and create a new database. * @@ -93,7 +115,25 @@ public class IciqlSuite { testUrl = DEFAULT_TEST_DB.url; } testUrl = MessageFormat.format(testUrl, openCount.incrementAndGet()); - return Db.open(testUrl, username, password); + Db db = Db.open(testUrl, username, password); + + // drop tables + db.dropTable(BooleanModel.class); + db.dropTable(ComplexObject.class); + db.dropTable(Customer.class); + db.dropTable(DefaultValuesModel.class); + db.dropTable(EnumIdModel.class); + db.dropTable(EnumOrdinalModel.class); + db.dropTable(EnumStringModel.class); + db.dropTable(Order.class); + db.dropTable(PrimitivesModel.class); + db.dropTable(Product.class); + db.dropTable(ProductAnnotationOnly.class); + db.dropTable(ProductInheritedAnnotation.class); + db.dropTable(ProductMixedAnnotation.class); + db.dropTable(SupportedTypes.class); + + return db; } /** @@ -110,6 +150,15 @@ public class IciqlSuite { return Db.open(testUrl, username, password); } + /** + * Drops all tables from the current database. + * + * @return the current database + */ + public static void dropAllTables(Db db) { + + } + /** * Returns the name of the underlying database engine for the Db object. * @@ -145,6 +194,16 @@ public class IciqlSuite { return IciqlSuite.getDatabaseEngineName(db).equals("H2"); } + /** + * Returns true if the underlying database engine is MySQL. + * + * @param db + * @return true if underlying database engine is MySQL + */ + public static boolean isMySQL(Db db) { + return IciqlSuite.getDatabaseEngineName(db).equals("MySQL"); + } + /** * Gets the default schema of the underlying database engine. * @@ -155,7 +214,11 @@ public class IciqlSuite { if (isDerby(db)) { // Derby sets default schema name to username return username.toUpperCase(); + } else if (isMySQL(db)) { + // MySQL does not have schemas + return null; } + return "PUBLIC"; } @@ -206,8 +269,8 @@ public class IciqlSuite { SuiteClasses suiteClasses = IciqlSuite.class.getAnnotation(SuiteClasses.class); long quickestDatabase = Long.MAX_VALUE; - String dividerMajor = buildDivider('*', 70); - String dividerMinor = buildDivider('-', 70); + String dividerMajor = buildDivider('*', 79); + String dividerMinor = buildDivider('-', 79); // Header out.println(dividerMajor); @@ -225,7 +288,7 @@ public class IciqlSuite { showProperty("available processors", "" + Runtime.getRuntime().availableProcessors()); showProperty( "available memory", - MessageFormat.format("{0,number,#.0} GB", ((double) Runtime.getRuntime().maxMemory()) + MessageFormat.format("{0,number,0.0} GB", ((double) Runtime.getRuntime().maxMemory()) / (1024 * 1024))); out.println(); @@ -246,32 +309,42 @@ public class IciqlSuite { statementWriter.append("\n\n"); } - System.setProperty("iciql.url", testDb.url); - Result result = JUnitCore.runClasses(suiteClasses.value()); - testDb.runtime = result.getRunTime(); - if (testDb.runtime < quickestDatabase) { - quickestDatabase = testDb.runtime; - } - testDb.statements = StatementLogger.getTotalCount() - lastCount; - // reset total count for next database - lastCount = StatementLogger.getTotalCount(); - - out.println(MessageFormat.format( - "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs", - result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(), - testDb.statements, result.getRunTime() / 1000f)); - - if (result.getFailureCount() == 0) { - out.println(); + if (testDb.getVersion().equals("OFFLINE")) { + // Database not available + out.println("Skipping. Could not find " + testDb.url); } else { - for (Failure failure : result.getFailures()) { - out.println(MessageFormat.format("\n + {0}\n {1}\n", failure.getTestHeader(), - failure.getMessage())); + // Test database + System.setProperty("iciql.url", testDb.url); + Result result = JUnitCore.runClasses(suiteClasses.value()); + testDb.runtime = result.getRunTime(); + if (testDb.runtime < quickestDatabase) { + quickestDatabase = testDb.runtime; + } + testDb.statements = StatementLogger.getTotalCount() - lastCount; + // reset total count for next database + lastCount = StatementLogger.getTotalCount(); + + out.println(MessageFormat.format( + "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs", + result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(), + testDb.statements, result.getRunTime() / 1000f)); + + if (result.getFailureCount() == 0) { + out.println(); + out.println(" 100% successful test suite run."); + out.println(); + } else { + for (Failure failure : result.getFailures()) { + out.println(MessageFormat.format("\n + {0}\n {1}", failure.getTestHeader(), + failure.getMessage())); + } + out.println(); } } } // Display runtime results sorted by performance leader + out.println(); out.println(dividerMajor); out.println(MessageFormat.format("{0} {1} ({2}) test suite performance results", Constants.NAME, Constants.VERSION, Constants.VERSION_DATE)); @@ -281,6 +354,12 @@ public class IciqlSuite { @Override public int compare(TestDb o1, TestDb o2) { + if (o1.runtime == 0) { + return 1; + } + if (o2.runtime == 0) { + return -1; + } if (o1.runtime == o2.runtime) { return 0; } @@ -292,11 +371,11 @@ public class IciqlSuite { }); for (TestDb testDb : dbs) { out.println(MessageFormat.format( - "{0} {1} {2,number,0.0} stats/sec {3,number,0.000} secs ({4,number,#.0}x)", - StringUtils.pad(testDb.name, 6, " ", true), - StringUtils.pad(testDb.getVersion(), 22, " ", true), - ((double) testDb.statements)/ (testDb.runtime/1000d), testDb.runtime / 1000f, - ((double) testDb.runtime) / quickestDatabase)); + "{0} {1} {2,number,0} stats/sec {3,number,0.000} secs ({4,number,0.0}x)", + StringUtils.pad(testDb.name, 20, " ", true), + StringUtils.pad(testDb.getVersion(), 22, " ", true), ((double) testDb.statements) + / (testDb.runtime / 1000d), testDb.runtime / 1000f, ((double) testDb.runtime) + / quickestDatabase)); } // close PrintStream and restore System.err @@ -362,8 +441,8 @@ public class IciqlSuite { version = db.getConnection().getMetaData().getDatabaseProductVersion(); db.close(); return version; - } catch (SQLException s) { - version = ""; + } catch (Throwable t) { + version = "OFFLINE"; } } return version; diff --git a/tests/com/iciql/test/ModelsTest.java b/tests/com/iciql/test/ModelsTest.java index 9bbf450..d2e02fa 100644 --- a/tests/com/iciql/test/ModelsTest.java +++ b/tests/com/iciql/test/ModelsTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue; 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; @@ -33,15 +32,12 @@ import org.junit.rules.ErrorCollector; 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. @@ -72,13 +68,9 @@ public class ModelsTest { @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); } @@ -95,8 +87,14 @@ public class ModelsTest { 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 @@ -133,65 +131,4 @@ public class ModelsTest { 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 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 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 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; - } - - } - } diff --git a/tests/com/iciql/test/SamplesTest.java b/tests/com/iciql/test/SamplesTest.java index f16672a..17c2151 100644 --- a/tests/com/iciql/test/SamplesTest.java +++ b/tests/com/iciql/test/SamplesTest.java @@ -91,6 +91,7 @@ public class SamplesTest { TestReverse check = db.from(new TestReverse()).selectFirst(); assertEquals(t.name, check.name); assertEquals(t.id, check.id); + db.executeUpdate("DROP TABLE testreverse"); } @Test @@ -238,7 +239,8 @@ public class SamplesTest { public void testIsNull() { Product p = new Product(); String sql = db.from(p).whereTrue(isNull(p.productName)).getSQL(); - assertEquals("SELECT * FROM Product WHERE (productName IS NULL)", sql); + assertEquals("SELECT * FROM Product WHERE (" + db.getDialect().prepareColumnName("productName") + + " IS NULL)", sql); } @Test @@ -258,11 +260,13 @@ public class SamplesTest { public void testOrAndNot() { Product p = new Product(); String sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL(); - assertEquals("SELECT * FROM Product WHERE (NOT productName IS NULL)", sql); + String productName = db.getDialect().prepareColumnName("productName"); + assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql); sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL(); - assertEquals("SELECT * FROM Product WHERE (NOT productName IS NULL)", sql); + assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql); sql = db.from(p).whereTrue(db.test(p.productId).is(1)).getSQL(); - assertEquals("SELECT * FROM Product WHERE ((productId = ?))", sql); + String productId = db.getDialect().prepareColumnName("productId"); + assertEquals("SELECT * FROM Product WHERE ((" + productId + " = ?))", sql); } @Test @@ -316,8 +320,24 @@ public class SamplesTest { .lessThan(java.sql.Timestamp.valueOf("2005-05-05 05:05:05")).and(co.name).is("hello") .and(co.time).lessThan(java.sql.Time.valueOf("23:23:23")).and(co.value) .is(new BigDecimal("1")).getSQL(); - assertEquals("SELECT * FROM ComplexObject WHERE id = ? AND amount = ? " - + "AND birthday < ? AND created < ? AND name = ? AND time < ? AND value = ?", sql); + + StringBuilder sb = new StringBuilder(); + sb.append("SELECT * FROM ComplexObject WHERE "); + sb.append(db.getDialect().prepareColumnName("id")); + sb.append(" = ? AND "); + sb.append(db.getDialect().prepareColumnName("amount")); + sb.append(" = ? AND "); + sb.append(db.getDialect().prepareColumnName("birthday")); + sb.append(" < ? AND "); + sb.append(db.getDialect().prepareColumnName("created")); + sb.append(" < ? AND "); + sb.append(db.getDialect().prepareColumnName("name")); + sb.append(" = ? AND "); + sb.append(db.getDialect().prepareColumnName("time")); + sb.append(" < ? AND "); + sb.append(db.getDialect().prepareColumnName("value")); + sb.append(" = ?"); + assertEquals(sb.toString(), sql); long count = db.from(co).where(co.id).is(1).and(co.amount).is(1L).and(co.birthday) .lessThan(new java.util.Date()).and(co.created) @@ -340,7 +360,14 @@ public class SamplesTest { return co.id == x && co.name.equals(name) && co.name.equals("hello"); } }).getSQL(); - assertEquals("SELECT * FROM ComplexObject WHERE id=? AND ?=name AND 'hello'=name", sql); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT * FROM ComplexObject WHERE "); + sb.append(db.getDialect().prepareColumnName("id")); + sb.append("=? AND ?="); + sb.append(db.getDialect().prepareColumnName("name")); + sb.append(" AND 'hello'="); + sb.append(db.getDialect().prepareColumnName("name")); + assertEquals(sb.toString(), sql); long count = db.from(co).where(new Filter() { public boolean where() { diff --git a/tests/com/iciql/test/UUIDTest.java b/tests/com/iciql/test/UUIDTest.java index bc3bd12..9b88cb8 100644 --- a/tests/com/iciql/test/UUIDTest.java +++ b/tests/com/iciql/test/UUIDTest.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.UUID; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -52,10 +53,9 @@ public class UUIDTest { @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 originals = UUIDRecord.getList(); db.insertAll(originals); UUIDRecord u = new UUIDRecord(); diff --git a/tests/com/iciql/test/UpgradesTest.java b/tests/com/iciql/test/UpgradesTest.java new file mode 100644 index 0000000..d4c15e1 --- /dev/null +++ b/tests/com/iciql/test/UpgradesTest.java @@ -0,0 +1,134 @@ +/* + * Copyright 2011 James Moger. + * + * 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.iciql.test; + +import static org.junit.Assert.assertEquals; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +import com.iciql.Db; +import com.iciql.DbUpgrader; +import com.iciql.Iciql.IQVersion; +import com.iciql.test.models.Product; +import com.iciql.test.models.SupportedTypes; +import com.iciql.test.models.SupportedTypes.SupportedTypes2; + +/** + * Tests the database and table upgrade functions. + * + */ +public class UpgradesTest { + + @Test + public void testDatabaseUpgrade() { + Db db = IciqlSuite.openNewDb(); + + List products = Product.getList(); + + // set the v1 upgrader and insert a record. + // this will trigger the upgrade. + V1DbUpgrader v1 = new V1DbUpgrader(); + db.setDbUpgrader(v1); + db.insert(products.get(0)); + + // confirm that upgrade occurred + assertEquals(0, v1.oldVersion.get()); + assertEquals(1, v1.newVersion.get()); + + // open a second connection to the database + // and then apply the v2 upgrade. + // For H2 its important to keep the first connection + // alive so that the database is not destroyed. + Db db2 = IciqlSuite.openCurrentDb(); + + // set the v2 upgrader and insert a record. + // this will trigger the upgrade. + V2DbUpgrader v2 = new V2DbUpgrader(); + db2.setDbUpgrader(v2); + db2.insert(products.get(1)); + + // confirm that upgrade occurred + assertEquals(1, v2.oldVersion.get()); + assertEquals(2, v2.newVersion.get()); + + db.executeUpdate("DROP TABLE iq_versions"); + db.close(); + db2.close(); + } + + @Test + public void testTableUpgrade() { + Db db = IciqlSuite.openNewDb(); + + // insert first, this will create version record automatically + List original = SupportedTypes.createList(); + db.insertAll(original); + + // reset the dbUpgrader (clears the update check cache) + V2DbUpgrader dbUpgrader = new V2DbUpgrader(); + db.setDbUpgrader(dbUpgrader); + + SupportedTypes2 s2 = new SupportedTypes2(); + + List types = db.from(s2).select(); + assertEquals(10, types.size()); + assertEquals(1, dbUpgrader.oldVersion.get()); + assertEquals(2, dbUpgrader.newVersion.get()); + db.executeUpdate("DROP TABLE iq_versions"); + db.close(); + } + + /** + * A sample database upgrader class. + */ + class BaseDbUpgrader 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; + } + } + + /** + * A sample V1 database upgrader class. + */ + @IQVersion(1) + class V1DbUpgrader extends BaseDbUpgrader { + } + + /** + * A sample V2 database upgrader class. + */ + @IQVersion(2) + class V2DbUpgrader extends BaseDbUpgrader { + } + +} diff --git a/tests/com/iciql/test/models/PrimitivesModel.java b/tests/com/iciql/test/models/PrimitivesModel.java index 0affd28..44e8b9b 100644 --- a/tests/com/iciql/test/models/PrimitivesModel.java +++ b/tests/com/iciql/test/models/PrimitivesModel.java @@ -21,6 +21,7 @@ import java.util.Random; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; +import com.iciql.test.IciqlSuite; /** * Primitive types model. @@ -67,8 +68,8 @@ public class PrimitivesModel { same &= myShort == p.myShort; same &= myByte == p.myByte; same &= myBoolean == p.myBoolean; - same &= myDouble == p.myDouble; - same &= myFloat == p.myFloat; + same &= IciqlSuite.equivalentTo(myDouble, p.myDouble); + same &= IciqlSuite.equivalentTo(myFloat, p.myFloat); return same; } diff --git a/tests/com/iciql/test/models/ProductAnnotationOnly.java b/tests/com/iciql/test/models/ProductAnnotationOnly.java index 80bf0a1..e4de22d 100644 --- a/tests/com/iciql/test/models/ProductAnnotationOnly.java +++ b/tests/com/iciql/test/models/ProductAnnotationOnly.java @@ -34,12 +34,9 @@ import com.iciql.Iciql.IndexType; @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) diff --git a/tests/com/iciql/test/models/SupportedTypes.java b/tests/com/iciql/test/models/SupportedTypes.java index 596af74..1aaa833 100644 --- a/tests/com/iciql/test/models/SupportedTypes.java +++ b/tests/com/iciql/test/models/SupportedTypes.java @@ -32,6 +32,7 @@ import com.iciql.Iciql.IQIndexes; 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; @@ -166,8 +167,8 @@ public class SupportedTypes { 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)); -- 2.39.5