* Improved DEFAULT value specifications.
* Fixed bug in buildObjects where the ResultSet could be closed by the
automatic create table attempt.
* DbInspector now uses the dialect's reported DATETIME class preference.
* Improved IciqlException SQLState code checks.
* Integrated LIMIT and OFFSET expression appending in dialects.
* Updated to H2 1.3.159
* Allow reopening of a memory database in the test suite.
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/markdownpapers-core-1.1.0-javadoc.jar!/"/>\r
</attributes>\r
</classpathentry>\r
- <classpathentry kind="lib" path="ext/h2-1.3.158.jar" sourcepath="ext/h2-1.3.158-sources.jar">\r
- <attributes>\r
- <attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/h2-1.3.158-javadoc.jar!/"/>\r
- </attributes>\r
- </classpathentry>\r
<classpathentry kind="lib" path="ext/doclava-1.0.3.jar" sourcepath="ext/doclava-1.0.3-sources.jar">\r
<attributes>\r
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/doclava-1.0.3-javadoc.jar!/"/>\r
</attributes>\r
</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/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
+ </attributes>\r
+ </classpathentry>\r
<classpathentry kind="output" path="bin"/>\r
</classpath>\r
/*.zip
/*.jar
/build.properties
+/derby.log
+/performance.txt
+/performance_db.txt
\r
http://hsqldb.org \r
\r
+---------------------------------------------------------------------------\r
+Derby Database\r
+---------------------------------------------------------------------------\r
+ Derby, released under the\r
+ Apache Software License, Version 2.0.\r
+ \r
+ http://db.apache.org/derby \r
+\r
---------------------------------------------------------------------------\r
MarkdownPapers\r
---------------------------------------------------------------------------\r
- a model-based, database access wrapper for JDBC\r
- for modest database schemas and basic statement generation\r
- for those who want to write code, instead of SQL, using IDE completion and compile-time type-safety\r
-- small (100KB) with no runtime dependencies\r
+- small (125KB) with no runtime dependencies\r
- pronounced *icicle* (although it could be French: *ici ql* - here query language)\r
- a friendly fork of the H2 [JaQu](http://h2database.com/html/jaqu.html) project\r
\r
- designed to compete with more powerful database query tools like [jOOQ](http://jooq.sourceforge.net) or [Querydsl](http://source.mysema.com/display/querydsl/Querydsl)\r
- designed to compete with enterprise ORM tools like [Hibernate](http://www.hibernate.org) or [mybatis](http://www.mybatis.org)\r
\r
-Supported Databases\r
+Supported Databases (Unit-Tested)\r
-------\r
-- [H2 1.3](http://h2database.com)\r
-- [HSQLDB 2.2](http://hsqldb.org)\r
-- Support for others is planned and may only require creating a simple "dialect" class.\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
+\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
\r
License\r
-------\r
</path>\r
<javac destdir="${project.build.dir}" failonerror="false">\r
<src path="${basedir}/src" />\r
+ <src path="${basedir}/tests" />\r
<classpath refid="master-classpath" />\r
</javac>\r
<copy todir="${project.build.dir}">\r
<fileset dir="${basedir}/src" excludes="**/*.java,**/thumbs.db" />\r
</copy>\r
\r
+ <!-- Execute the test suite -->\r
+ <echo>Executing iciql ${iq.version} test suite</echo>\r
+ <java classpath="${project.build.dir}" classname="com.iciql.test.IciqlSuite">\r
+ <classpath refid="master-classpath" />\r
+ <arg value="--outputFile" />\r
+ <arg value="${basedir}/performance_db.txt" />\r
+ </java>\r
+ \r
<!-- Build Standard Javadoc -->\r
<delete dir="${basedir}/javadoc" />\r
<javadoc destdir="${basedir}/javadoc" nonavbar="true" stylesheetfile="${basedir}/docs/resources/javadoc.css">\r
<fileset dir="${project.build.dir}">\r
<include name="**/*" />\r
<exclude name="com/iciql/build/" />\r
+ <exclude name="com/iciql/tests/" />\r
<exclude name="**/*.html" />\r
</fileset>\r
</jar>\r
<fileset dir="${basedir}/src">\r
<include name="**/*" />\r
<exclude name="com/iciql/build/" />\r
+ <exclude name="com/iciql/tests/" />\r
<exclude name="**/*.html" />\r
</fileset>\r
</jar>\r
<include name="**/*" />\r
</fileset>\r
</copy>\r
-\r
+ \r
<!-- Build site pages -->\r
<java classpath="${project.build.dir}" classname="com.iciql.build.BuildSite">\r
<classpath refid="master-classpath" />\r
<arg value="--regex" />\r
<arg value=""\b(issue)(\s*[#]?|-){0,1}(\d+)\b!!!<a href='http://code.google.com/p/iciql/issues/detail?id=$3'>issue $3</a>"" />\r
\r
+ <arg value="--load" />\r
+ <arg value="%DBPERFORMANCE%=${basedir}/performance_db.txt" />\r
+\r
</java> \r
</target>\r
\r
- a model-based, database access wrapper for JDBC\r
- for modest database schemas and basic statement generation\r
- for those who want to write code, instead of SQL, using IDE completion and compile-time type-safety\r
-- small (120KB) with no runtime dependencies\r
+- small (125KB) with no runtime dependencies\r
- pronounced *icicle* (although it could be French: *ici ql* - here query language)\r
- a friendly fork of the H2 [JaQu][jaqu] project\r
\r
</tr>\r
</table>\r
\r
-### Supported Databases\r
-- [H2 1.3](http://h2database.com)\r
-- [HSQLDB 2.2](http://hsqldb.org)\r
+### Supported Databases (Unit-Tested)\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
+\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
\r
JdbcUtils.closeSilently(rs, true);\r
%ENDCODE% \r
\r
+### Natural Syntax\r
+\r
+**work-in-progress**\r
+\r
+The original JaQu source offers partial support for Java expressions in *where* clauses.\r
+\r
+This works by decompiling a Java expression, at runtime, to an SQL condition. The expression is written as an anonymous inner class implementation of the `com.iciql.Filter` interface.\r
+A proof-of-concept decompiler is included, but is incomplete.\r
+\r
+The proposed syntax is:\r
+%BEGINCODE%\r
+long count = db.from(co).\r
+ where(new Filter() { public boolean where() {\r
+ return co.id == x\r
+ && co.name.equals(name)\r
+ && co.value == new BigDecimal("1")\r
+ && co.amount == 1L\r
+ && co.birthday.before(new java.util.Date())\r
+ && co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))\r
+ && co.time.before(java.sql.Time.valueOf("23:23:23"));\r
+ }\r
+ }).selectCount();\r
+%ENDCODE%\r
+\r
### JDBC Statements, ResultSets, and Exception Handling\r
\r
Iciql opens and closes all JDBC objects automatically. SQLExceptions thrown during execution of a statement (except for *close()* calls), will be caught, wrapped, and rethrown as an `IciqlException`, which is a RuntimeException.\r
+++ /dev/null
-## Natural Syntax\r
-\r
-**work-in-progress**\r
-\r
-The original JaQu source offers partial support for Java expressions in *where* clauses.\r
-\r
-This works by decompiling a Java expression, at runtime, to an SQL condition. The expression is written as an anonymous inner class implementation of the `com.iciql.Filter` interface.\r
-A proof-of-concept decompiler is included, but is incomplete.\r
-\r
-The proposed syntax is:\r
-%BEGINCODE%\r
-long count = db.from(co).\r
- where(new Filter() { public boolean where() {\r
- return co.id == x\r
- && co.name.equals(name)\r
- && co.value == new BigDecimal("1")\r
- && co.amount == 1L\r
- && co.birthday.before(new java.util.Date())\r
- && co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))\r
- && co.time.before(java.sql.Time.valueOf("23:23:23"));\r
- }\r
- }).selectCount();\r
-%ENDCODE%
\ No newline at end of file
--- /dev/null
+\r
+## Performance\r
+\r
+The information provided here may be based on flawed test procedures. You have to be the judge of what is performant and non-performant.\r
+\r
+### iciql statement generation\r
+\r
+Performance of iciql statement generation is not currently benchmarked but that is planned.\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
+\r
+<pre>\r
+%DBPERFORMANCE%\r
+</pre>
\ No newline at end of file
### Build Dependencies (downloaded during build)\r
- [H2 Database](http://h2database.com) (Eclipse Public License 1.0)\r
- [HSQL Database Engine](http://hsqldb.org) (BSD)\r
+- [Apache Derby Database](http://db.apache.org/derby) (Apache 2.0)\r
- [JUnit](http://junit.org) (Common Public License)\r
- [commons-net](http://commons.apache.org/net) (Apache 2.0)\r
- [ant-googlecode](http://code.google.com/p/ant-googlecode) (New BSD)\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
-- fixed failure of db.delete(PrimitiveModel) and db.update(PrimitiveModel)\r
+- Disabled 2 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
+\r
**0.6.4** *released 2011-08-12*\r
\r
- api change release (API v4)\r
- added HSQL dialect. HSQL fails 4 out of 50 unit tests.\r
- 2 failures are unimplemented merge\r
- 1 has been filed and accepted as a [bug in HSQL](https://sourceforge.net/tracker/?func=detail&aid=3390047&group_id=23316&atid=378131)\r
- - 1 is a concurrency issue, but the test may not be flawed\r
+ - 1 is a concurrency issue, but the test may be flawed\r
- added untested MySQL dialect\r
- renamed <b>_ iq_versions</b> table to *iq_versions* since leading _ character is troublesome for some databases.\r
- @IQColumn(allowNull=true) -> @IQColumn(nullable=true)\r
\r
// The build script extracts this exact line so be careful editing it\r
// and only use A-Z a-z 0-9 .-_ in the string.\r
- public static final String VERSION = "0.6.5";\r
+ public static final String VERSION = "0.6.6-SNAPSHOT";\r
\r
// The build script extracts this exact line so be careful editing it\r
// and only use A-Z a-z 0-9 .-_ in the string.\r
- public static final String VERSION_DATE = "2011-08-12";\r
+ public static final String VERSION_DATE = "PENDING";\r
\r
// The build script extracts this exact line so be careful editing it\r
// and only use A-Z a-z 0-9 .-_ in the string.\r
DIALECTS.put("H2", SQLDialectH2.class);\r
DIALECTS.put("MySQL", SQLDialectMySQL.class);\r
DIALECTS.put("HSQL Database Engine", SQLDialectHSQL.class);\r
+ DIALECTS.put("Apache Derby", SQLDialectDerby.class);\r
}\r
\r
private Db(Connection conn) {\r
* does exist. Not all databases support MERGE and the syntax varies with\r
* the database.\r
* \r
+ * If the database does not support a MERGE syntax the dialect can try to\r
+ * simulate a merge by implementing:\r
+ * <p>\r
+ * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)\r
+ * <p>\r
+ * iciql will check the affected row count returned by the internal merge\r
+ * method and if the affected row count = 0, it will issue an update.\r
+ * <p>\r
+ * See the Derby dialect for an implementation of this technique.\r
+ * <p>\r
* If the dialect does not support merge an IciqlException will be thrown.\r
* \r
* @param t\r
*/\r
public <T> void merge(T t) {\r
Class<?> clazz = t.getClass();\r
- define(clazz).createTableIfRequired(this).merge(this, t);\r
+ TableDefinition<?> def = define(clazz).createTableIfRequired(this);\r
+ int rc = def.merge(this, t);\r
+ if (rc == 0) {\r
+ rc = def.update(this, t);\r
+ }\r
+ if (rc == 0) {\r
+ throw new IciqlException("merge failed");\r
+ }\r
}\r
\r
public <T> int update(T t) {\r
@SuppressWarnings("unchecked")\r
public <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {\r
List<T> result = new ArrayList<T>();\r
- TableDefinition<T> def = (TableDefinition<T>) define(modelClass).createTableIfRequired(this);\r
+ TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
try {\r
while (rs.next()) {\r
T item = Utils.newObject(modelClass);\r
public DbInspector(Db db) {
this.db = db;
+ setPreferredDateTimeClass(db.getDialect().getDateTimeClass());
}
/**
* (trims strings to maxLength of column)
* @return a list of complete model classes as strings, each element a class
*/
- public List<String> generateModel(String schema, String table, String packageName, boolean annotateSchema,
- boolean trimStrings) {
+ public List<String> generateModel(String schema, String table, String packageName,
+ boolean annotateSchema, boolean trimStrings) {
try {
List<String> models = Utils.newArrayList();
List<TableInspector> tables = getTables(schema, table);
Class<T> clazz = (Class<T>) model.getClass();
TableDefinition<T> def = db.define(clazz);
boolean forceUpperCase = getMetaData().storesUpperCaseIdentifiers();
- String schema = (forceUpperCase && def.schemaName != null) ? def.schemaName.toUpperCase() : def.schemaName;
+ String schema = (forceUpperCase && def.schemaName != null) ? def.schemaName.toUpperCase()
+ : def.schemaName;
String table = forceUpperCase ? def.tableName.toUpperCase() : def.tableName;
List<TableInspector> tables = getTables(schema, table);
return tables.get(0);
while (rs.next()) {
String t = rs.getString("TABLE_NAME");
if (!t.equalsIgnoreCase(iciqlTables)) {
- tables.add(new TableInspector(s, t, getMetaData().storesUpperCaseIdentifiers(), dateTimeClass));
+ tables.add(new TableInspector(s, t, getMetaData().storesUpperCaseIdentifiers(),
+ dateTimeClass));
}
}
}
* </tr>\r
* <tr>\r
* <td>java.math.BigDecimal</td>\r
- * <td>DECIMAL (length == 0)<br/>DECIMAL(length, scale) (length > 0)</td>\r
+ * <td>DECIMAL (length == 0)<br/>\r
+ * DECIMAL(length, scale) (length > 0)</td>\r
* </tr>\r
* <tr>\r
* <td>java.sql.Date</td>\r
* Scale is used during the CREATE TABLE phase to define the scale of a\r
* DECIMAL(precision, scale) expression.\r
* <p>\r
- * Any scale set in define() may override this annotation setting if\r
- * the model class is not annotated with IQTable. Default: 0.\r
+ * Any scale set in define() may override this annotation setting if the\r
+ * model class is not annotated with IQTable. Default: 0.\r
*/\r
int scale() default 0;\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_INDEX_ALREADY_EXISTS = 4;\r
+ public static final int CODE_TABLE_ALREADY_EXISTS = 4;\r
+ public static final int CODE_INDEX_ALREADY_EXISTS = 5;\r
\r
- private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?"; \r
+ private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?";\r
\r
private static final long serialVersionUID = 1L;\r
\r
super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message, t);\r
configureCode(t);\r
}\r
- \r
+\r
public static void checkUnmappedField(String sql) {\r
if (Pattern.compile(IciqlException.TOKEN_UNMAPPED_FIELD).matcher(sql).find()) {\r
IciqlException e = new IciqlException("unmapped field in statement!");\r
throw e;\r
}\r
}\r
- \r
+\r
public static IciqlException fromSQL(String sql, Throwable t) {\r
if (Pattern.compile(TOKEN_UNMAPPED_FIELD).matcher(sql).find()) {\r
IciqlException e = new IciqlException(t, "unmapped field in statement!");\r
return e;\r
}\r
}\r
- \r
+\r
public void setSQL(String sql) {\r
this.sql = sql;\r
}\r
public int getIciqlCode() {\r
return iciqlCode;\r
}\r
- \r
+\r
private void configureCode(Throwable t) {\r
if (t == null) {\r
return;\r
String state = s.getSQLState();\r
if ("23505".equals(state)) {\r
iciqlCode = CODE_DUPLICATE_KEY;\r
- } else if ("42501".equals(state)) {\r
+ } else if ("42X05".equals(state)) {\r
+ // Derby\r
iciqlCode = CODE_TABLE_NOT_FOUND;\r
} else if ("42S02".equals(state)) {\r
+ // H2\r
iciqlCode = CODE_TABLE_NOT_FOUND;\r
- } else if ("42504".equals(state)) {\r
- iciqlCode = CODE_INDEX_ALREADY_EXISTS;\r
+ } else if ("42501".equals(state)) {\r
+ // HSQL\r
+ iciqlCode = CODE_TABLE_NOT_FOUND;\r
+ } else if ("X0Y32".equals(state)) {\r
+ // Derby\r
+ iciqlCode = CODE_TABLE_ALREADY_EXISTS;\r
} else if ("42S11".equals(state)) {\r
+ // H2\r
+ iciqlCode = CODE_INDEX_ALREADY_EXISTS;\r
+ } else if ("42504".equals(state)) {\r
+ // HSQL\r
iciqlCode = CODE_INDEX_ALREADY_EXISTS;\r
}\r
}\r
}\r
- \r
+\r
@Override\r
- public String toString() {\r
+ public String toString() {\r
StringBuilder sb = new StringBuilder();\r
- sb.append(getClass().getName()); \r
- String message = getLocalizedMessage();\r
- if (message != null) {\r
- sb.append(": ").append(message);\r
- }\r
- if (sql != null) {\r
- sb.append('\n').append(sql);\r
- }\r
- return sb.toString();\r
- }\r
+ sb.append(getClass().getName());\r
+ String message = getLocalizedMessage();\r
+ if (message != null) {\r
+ sb.append(": ").append(message);\r
+ }\r
+ if (sql != null) {\r
+ sb.append('\n').append(sql);\r
+ }\r
+ return sb.toString();\r
+ }\r
}\r
String value = null;
if (Number.class.isAssignableFrom(objectClass)) {
// NUMBER
- value = ((Number) o).toString();
+ return ((Number) o).toString();
+ } else if (Boolean.class.isAssignableFrom(objectClass)) {
+ // BOOLEAN
+ return o.toString();
} else if (java.sql.Date.class.isAssignableFrom(objectClass)) {
// DATE
value = new SimpleDateFormat("yyyy-MM-dd").format((Date) o);
} else if (String.class.isAssignableFrom(objectClass)) {
// STRING
value = o.toString();
- } else if (Boolean.class.isAssignableFrom(objectClass)) {
- // BOOLEAN
- value = o.toString();
}
if (value == null) {
return "''";
return true;
}
- // TODO H2 single-quotes literal values, which is useful.
- // MySQL does not single-quote literal values so its hard to
- // differentiate a FUNCTION/VARIABLE from a literal value.
-
// function / variable
Pattern functionDefault = Pattern.compile("[^'].*[^']");
if (functionDefault.matcher(defaultValue).matches()) {
public UpdateColumnIncrement<T, Byte> increment(byte field) {\r
return incrementPrimitive(field);\r
}\r
- \r
+\r
public UpdateColumnIncrement<T, Short> increment(short field) {\r
return incrementPrimitive(field);\r
}\r
}\r
return orderBy(alias);\r
}\r
- \r
+\r
public Query<T> orderBy(Object expr) {\r
OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);\r
addOrderBy(e);\r
return this;\r
}\r
- \r
+\r
/**\r
* Order by a number of columns.\r
* \r
}\r
return groupBy(alias);\r
}\r
- \r
+\r
public Query<T> groupBy(Object expr) {\r
groupByExpressions.add(expr);\r
return this;\r
}\r
- \r
+\r
public Query<T> groupBy(Object... groupBy) {\r
this.groupByExpressions.addAll(Arrays.asList(groupBy));\r
return this;\r
stat.appendSQL(" ");\r
}\r
}\r
- if (limit > 0) {\r
- db.getDialect().appendLimit(stat, limit);\r
- }\r
- if (offset > 0) {\r
- db.getDialect().appendOffset(stat, offset);\r
- }\r
+ db.getDialect().appendLimitOffset(stat, limit, offset);\r
StatementLogger.select(stat.getSQL());\r
}\r
\r
\r
/**\r
* This class represents a "between y and z" condition.\r
+ * \r
* @param <T>\r
* the return type of the query\r
* @param <A>\r
query.orderBy(field);\r
return this;\r
}\r
+\r
/**\r
* Order by a number of Object columns.\r
* \r
* @param def
*/
<T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def);
-
+
/**
* Get the CREATE INDEX statement.
*
Object obj);
/**
- * Append "LIMIT limit" to the SQL statement.
+ * Append "LIMIT limit OFFSET offset" to the SQL statement.
*
* @param stat
* the statement
* @param limit
* the limit
- */
- void appendLimit(SQLStatement stat, long limit);
-
- /**
- * Append "OFFSET offset" to the SQL statement.
- *
- * @param stat
- * the statement
* @param offset
* the offset
*/
- void appendOffset(SQLStatement stat, long offset);
+ void appendLimitOffset(SQLStatement stat, long limit, long offset);
/**
* Whether memory tables are supported.
* @return true if they are
*/
boolean supportsMemoryTables();
-
+
+ /**
+ * Whether IF NOT EXISTS notation is supported.
+ *
+ * @return true if they are
+ */
+ boolean supportsIfNotExists();
+
/**
* Whether LIMIT/OFFSET notation is supported.
*
*/
boolean supportsLimitOffset();
+ /**
+ * Returns the preferred DATETIME class for the database.
+ * <p>
+ * Either java.util.Date or java.sql.Timestamp
+ *
+ * @return preferred DATETIME class
+ */
+ Class<? extends java.util.Date> getDateTimeClass();
}
return sqlType;\r
}\r
\r
+ @Override\r
+ public Class<? extends java.util.Date> getDateTimeClass() {\r
+ return java.util.Date.class;\r
+ }\r
+\r
@Override\r
public boolean supportsMemoryTables() {\r
return false;\r
}\r
\r
+ @Override\r
+ public boolean supportsIfNotExists() {\r
+ return true;\r
+ }\r
+\r
@Override\r
public boolean supportsLimitOffset() {\r
return true;\r
public <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def) {\r
StatementBuilder buff;\r
if (def.memoryTable && supportsMemoryTables()) {\r
- buff = new StatementBuilder("CREATE MEMORY TABLE IF NOT EXISTS ");\r
+ buff = new StatementBuilder("CREATE MEMORY TABLE ");\r
} else {\r
- buff = new StatementBuilder("CREATE TABLE IF NOT EXISTS ");\r
+ buff = new StatementBuilder("CREATE TABLE ");\r
+ }\r
+\r
+ if (supportsIfNotExists()) {\r
+ buff.append("IF NOT EXISTS ");\r
}\r
\r
buff.append(prepareTableName(def.schemaName, def.tableName)).append('(');\r
}\r
\r
@Override\r
- public void appendLimit(SQLStatement stat, long limit) {\r
- stat.appendSQL(" LIMIT " + limit);\r
- }\r
-\r
- @Override\r
- public void appendOffset(SQLStatement stat, long offset) {\r
- stat.appendSQL(" OFFSET " + offset);\r
+ public void appendLimitOffset(SQLStatement stat, long limit, long offset) {\r
+ if (limit > 0) {\r
+ stat.appendSQL(" LIMIT " + limit);\r
+ }\r
+ if (offset > 0) {\r
+ stat.appendSQL(" OFFSET " + offset);\r
+ }\r
}\r
}
\ No newline at end of file
--- /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;\r
+\r
+import java.text.MessageFormat;\r
+\r
+import com.iciql.TableDefinition.FieldDefinition;\r
+import com.iciql.TableDefinition.IndexDefinition;\r
+import com.iciql.util.StatementBuilder;\r
+\r
+/**\r
+ * Derby database dialect.\r
+ */\r
+public class SQLDialectDerby extends SQLDialectDefault {\r
+\r
+ @Override\r
+ public Class<? extends java.util.Date> getDateTimeClass() {\r
+ return java.sql.Timestamp.class;\r
+ }\r
+\r
+ @Override\r
+ protected String convertSqlType(String sqlType) {\r
+ if ("TINYINT".equals(sqlType)) {\r
+ // Derby does not have a TINYINT/BYTE type\r
+ return "SMALLINT";\r
+ }\r
+ return sqlType;\r
+ }\r
+\r
+ @Override\r
+ public boolean supportsMemoryTables() {\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public boolean supportsIfNotExists() {\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public boolean supportsLimitOffset() {\r
+ // FETCH/OFFSET added in 10.5\r
+ return databaseVersion >= 10.5f;\r
+ }\r
+\r
+ @Override\r
+ public void appendLimitOffset(SQLStatement stat, long limit, long offset) {\r
+ if (offset > 0) {\r
+ stat.appendSQL(" OFFSET " + offset + (offset == 1 ? " ROW" : " ROWS"));\r
+ }\r
+ if (limit > 0) {\r
+ stat.appendSQL(" FETCH NEXT " + limit + (limit == 1 ? " ROW" : " ROWS") + " ONLY");\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public String prepareColumnName(String name) {\r
+ return name;\r
+ }\r
+\r
+ @Override\r
+ protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement,\r
+ boolean isPrimaryKey) {\r
+ if (isAutoIncrement) {\r
+ buff.append(" GENERATED BY DEFAULT AS IDENTITY");\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @Override\r
+ public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) {\r
+ StatementBuilder buff = new StatementBuilder();\r
+ buff.append("CREATE ");\r
+ switch (index.type) {\r
+ case UNIQUE:\r
+ buff.append("UNIQUE ");\r
+ break;\r
+ case UNIQUE_HASH:\r
+ buff.append("UNIQUE ");\r
+ break;\r
+ }\r
+ buff.append("INDEX ");\r
+ buff.append(index.indexName);\r
+ buff.append(" ON ");\r
+ buff.append(table);\r
+ buff.append("(");\r
+ for (String col : index.columnNames) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(col));\r
+ }\r
+ buff.append(") ");\r
+\r
+ stat.setSQL(buff.toString().trim());\r
+ }\r
+\r
+ /**\r
+ * Derby does not support the SQL2003 MERGE syntax, but we can use a trick\r
+ * to insert a row if it does not exist and call update() in Db.merge() if\r
+ * the affected row count is 0;\r
+ * \r
+ * http://stackoverflow.com/questions/407688\r
+ */\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));\r
+ buff.append(" (");\r
+ buff.resetCount();\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(field.columnName));\r
+ }\r
+ buff.append(") (SELECT ");\r
+ buff.resetCount();\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(" FROM ");\r
+ buff.append(prepareTableName(schemaName, tableName));\r
+ buff.append(" WHERE ");\r
+ buff.resetCount();\r
+ for (FieldDefinition field : def.fields) {\r
+ if (field.isPrimaryKey) {\r
+ buff.appendExceptFirst(" AND ");\r
+ buff.append(MessageFormat.format("{0} = ?", prepareColumnName(field.columnName)));\r
+ Object value = def.getValue(obj, field);\r
+ stat.addParameter(value);\r
+ }\r
+ }\r
+ buff.append(" HAVING count(*)=0)");\r
+ stat.setSQL(buff.toString());\r
+ }\r
+}
\ No newline at end of file
buff.append(col);\r
}\r
buff.append(")");\r
- stat.setSQL(buff.toString()); \r
+ stat.setSQL(buff.toString());\r
}\r
- \r
+\r
@Override\r
- public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition<T> def, Object obj) {\r
+ public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,\r
+ TableDefinition<T> def, Object obj) {\r
StatementBuilder buff = new StatementBuilder("MERGE INTO ");\r
buff.append(prepareTableName(schemaName, tableName)).append(" (");\r
buff.resetCount();\r
\r
package com.iciql;\r
\r
+import java.text.MessageFormat;\r
+\r
+import com.iciql.TableDefinition.FieldDefinition;\r
import com.iciql.TableDefinition.IndexDefinition;\r
import com.iciql.util.StatementBuilder;\r
\r
* HyperSQL database dialect.\r
*/\r
public class SQLDialectHSQL extends SQLDialectDefault {\r
- \r
+\r
@Override\r
public boolean supportsMemoryTables() {\r
return true;\r
}\r
- \r
+\r
@Override\r
- protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement, boolean isPrimaryKey) {\r
+ protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement,\r
+ boolean isPrimaryKey) {\r
boolean isIdentity = false;\r
if (isAutoIncrement && isPrimaryKey) {\r
buff.append(" IDENTITY");\r
StatementBuilder buff = new StatementBuilder();\r
buff.append("CREATE ");\r
switch (index.type) {\r
- case STANDARD:\r
- break;\r
case UNIQUE:\r
buff.append("UNIQUE ");\r
break;\r
buff.append(")");\r
stat.setSQL(buff.toString());\r
}\r
+\r
+ @Override\r
+ public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,\r
+ TableDefinition<T> def, Object obj) {\r
+ final String valuePrefix = "v";\r
+ StatementBuilder buff = new StatementBuilder("MERGE INTO ");\r
+ buff.append(prepareTableName(schemaName, tableName));\r
+ // a, b, c....\r
+ buff.append(" USING (VALUES(");\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append("CAST(? AS ");\r
+ String dataType = convertSqlType(field.dataType);\r
+ buff.append(dataType);\r
+ if ("VARCHAR".equals(dataType)) {\r
+ if (field.length > 0) {\r
+ // VARCHAR(x)\r
+ buff.append(MessageFormat.format("({0})", field.length));\r
+ }\r
+ } else if ("DECIMAL".equals(dataType)) {\r
+ if (field.length > 0) {\r
+ if (field.scale > 0) {\r
+ // DECIMAL(x,y)\r
+ buff.append(MessageFormat.format("({0},{1})", field.length, field.scale));\r
+ } else {\r
+ // DECIMAL(x)\r
+ buff.append(MessageFormat.format("({0})", field.length));\r
+ }\r
+ }\r
+ }\r
+ buff.append(')');\r
+ Object value = def.getValue(obj, field);\r
+ stat.addParameter(value);\r
+ }\r
+\r
+ // map to temporary table\r
+ buff.resetCount();\r
+ buff.append(")) AS vals (");\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(valuePrefix + field.columnName));\r
+ }\r
+\r
+ buff.append(") ON ");\r
+\r
+ // create the ON condition\r
+ // (va, vb) = (va,vb)\r
+ String[] prefixes = { "", valuePrefix };\r
+ for (int i = 0; i < prefixes.length; i++) {\r
+ String prefix = prefixes[i];\r
+ buff.resetCount();\r
+ buff.append('(');\r
+ for (FieldDefinition field : def.fields) {\r
+ if (field.isPrimaryKey) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(prefix + field.columnName));\r
+ }\r
+ }\r
+ buff.append(")");\r
+ if (i == 0) {\r
+ buff.append('=');\r
+ }\r
+ }\r
+\r
+ // UPDATE\r
+ // set a=va\r
+ buff.append(" WHEN MATCHED THEN UPDATE SET ");\r
+ buff.resetCount();\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(field.columnName));\r
+ buff.append('=');\r
+ buff.append(prepareColumnName(valuePrefix + field.columnName));\r
+ }\r
+\r
+ // INSERT\r
+ // insert va, vb, vc....\r
+ buff.append(" WHEN NOT MATCHED THEN INSERT ");\r
+ buff.resetCount();\r
+ buff.append(" VALUES (");\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(valuePrefix + field.columnName));\r
+ }\r
+ buff.append(')');\r
+ stat.setSQL(buff.toString());\r
+ }\r
}
\ No newline at end of file
}\r
return sqlType;\r
}\r
- \r
+\r
@Override\r
public boolean supportsMemoryTables() {\r
return false;\r
public String prepareColumnName(String name) {\r
return "`" + name + "`";\r
}\r
- \r
+\r
@Override\r
- protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement, boolean isPrimaryKey) {\r
+ protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement,\r
+ boolean isPrimaryKey) {\r
if (isAutoIncrement) {\r
buff.append(" AUTO_INCREMENT");\r
}\r
return false;\r
}\r
- \r
+\r
@Override\r
public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) {\r
StatementBuilder buff = new StatementBuilder();\r
buff.append(prepareColumnName(col));\r
}\r
buff.append(") ");\r
- \r
+\r
// USING\r
switch (index.type) {\r
case HASH:\r
}\r
\r
public SQLStatement addParameter(Object o) {\r
+ // Automatically convert java.util.Date to java.sql.Timestamp\r
+ // if the dialect requires java.sql.Timestamp objects (e.g. Derby)\r
+ if (o != null && o.getClass().equals(java.util.Date.class)\r
+ && db.getDialect().getDateTimeClass().equals(java.sql.Timestamp.class)) {\r
+ o = new java.sql.Timestamp(((java.util.Date) o).getTime());\r
+ }\r
params.add(o);\r
return this;\r
}\r
try {\r
prep.setObject(parameterIndex, x);\r
} catch (SQLException e) {\r
- IciqlException ix = new IciqlException(e, "error setting parameter {0} as {1}", parameterIndex, x.getClass().getSimpleName());\r
+ IciqlException ix = new IciqlException(e, "error setting parameter {0} as {1}", parameterIndex, x\r
+ .getClass().getSimpleName());\r
ix.setSQL(getSQL());\r
throw ix;\r
}\r
}\r
\r
private boolean skipInsertField(FieldDefinition field, Object obj) {\r
- // skip uninitialized primitive autoincrement values\r
- if (field.isAutoIncrement && field.isPrimitive) {\r
+ if (field.isAutoIncrement) {\r
Object value = getValue(obj, field);\r
- if (value.toString().equals("0")) {\r
+ if (field.isPrimitive) {\r
+ // skip uninitialized primitive autoincrement values\r
+ if (value.toString().equals("0")) {\r
+ return true;\r
+ }\r
+ } else if (value == null) {\r
+ // skip null object autoincrement values\r
return true;\r
}\r
}\r
return false;\r
}\r
\r
- void merge(Db db, Object obj) {\r
+ int merge(Db db, Object obj) {\r
if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {\r
- throw new IllegalStateException("No primary key columns defined " + "for table " + obj.getClass()\r
+ throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()\r
+ " - no update possible");\r
}\r
SQLStatement stat = new SQLStatement(db);\r
db.getDialect().prepareMerge(stat, schemaName, tableName, this, obj);\r
StatementLogger.merge(stat.getSQL());\r
- stat.executeUpdate();\r
+ return stat.executeUpdate();\r
}\r
\r
int update(Db db, Object obj) {\r
if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {\r
- throw new IllegalStateException("No primary key columns defined " + "for table " + obj.getClass()\r
+ throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()\r
+ " - no update possible");\r
}\r
SQLStatement stat = new SQLStatement(db);\r
SQLStatement stat = new SQLStatement(db);\r
db.getDialect().prepareCreateTable(stat, this);\r
StatementLogger.create(stat.getSQL());\r
- stat.executeUpdate();\r
+ try {\r
+ stat.executeUpdate();\r
+ } catch (IciqlException e) {\r
+ if (e.getIciqlCode() != IciqlException.CODE_TABLE_ALREADY_EXISTS) {\r
+ throw e;\r
+ }\r
+ }\r
\r
// create indexes\r
for (IndexDefinition index : indexes) {\r
IndexInspector info = new IndexInspector(rs);
if (info.type.equals(IndexType.UNIQUE)) {
String name = info.name.toLowerCase();
- if (name.startsWith("primary") || name.startsWith("sys_idx_sys_pk")) {
+ if (name.startsWith("primary") || name.startsWith("sys_idx_sys_pk")
+ || name.startsWith("sql")) {
// skip primary key indexes
continue;
}
package com.iciql;
-
/**
* This class represents "SET column = value" in an UPDATE statement.
*
downloadFromApache(MavenObject.H2, BuildType.RUNTIME);\r
downloadFromApache(MavenObject.H2, BuildType.COMPILETIME);\r
downloadFromApache(MavenObject.HSQLDB, BuildType.RUNTIME);\r
+ downloadFromApache(MavenObject.DERBY, 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.158",\r
- "4bac13427caeb32ef6e93b70101e61f370c7b5e2", "6bb165156a0831879fa7797df6e18bdcd4421f2d",\r
- "446d3f58c44992534cb54f67134532d95961904a");\r
+ public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.159",\r
+ "dd89f939661eb5593909584e1c243db0c25de130", "4d953bf765e8a13c7e06ca51165438338966c698",\r
+ "4c79ed03f994820a1a873150c8a9f13c667784d3");\r
\r
public static final MavenObject HSQLDB = new MavenObject("org/hsqldb", "hsqldb", "2.2.4",\r
- "6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "",\r
- "");\r
+ "6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "", "");\r
+\r
+ public static final MavenObject DERBY = new MavenObject("org/apache/derby", "derby", "10.8.1.2",\r
+ "2f8717d96eafe3eef3de445ba653f142d54ddab1", "", "");\r
\r
public static final MavenObject JUNIT = new MavenObject("junit", "junit", "4.8.2",\r
"c94f54227b08100974c36170dcb53329435fe5ad", "", "");\r
aliasMap.put(values[0], values[1]);\r
}\r
\r
- System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ", markdownFiles.length,\r
- sourceFolder.getAbsolutePath()));\r
+ System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ",\r
+ markdownFiles.length, sourceFolder.getAbsolutePath()));\r
String linkPattern = "<a href=''{0}''>{1}</a>";\r
StringBuilder sb = new StringBuilder();\r
for (File file : markdownFiles) {\r
\r
// get remainder of text\r
if (endCode < markdownContent.length()) {\r
- strippedContent.append(markdownContent.substring(endCode, markdownContent.length()));\r
+ strippedContent.append(markdownContent.substring(endCode,\r
+ markdownContent.length()));\r
}\r
markdownContent = strippedContent.toString();\r
nmd++;\r
String[] kv = token.split("!!!", 2);\r
content = content.replaceAll(kv[0], kv[1]);\r
}\r
+ for (String alias : params.loads) {\r
+ String[] kv = alias.split("=", 2);\r
+ String loadedContent = StringUtils.readContent(new File(kv[1]), "\n");\r
+ loadedContent = StringUtils.escapeForHtml(loadedContent, false);\r
+ loadedContent = StringUtils.breakLinesForHtml(loadedContent);\r
+ content = content.replace(kv[0], loadedContent);\r
+ }\r
\r
- OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(destinationFolder,\r
- fileName)), Charset.forName("UTF-8"));\r
+ OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(\r
+ destinationFolder, fileName)), Charset.forName("UTF-8"));\r
writer.write(header);\r
if (!StringUtils.isNullOrEmpty(htmlAdSnippet)) {\r
writer.write(htmlAdSnippet);\r
@Parameter(names = { "--substitute" }, description = "%TOKEN%=value", required = false)\r
public List<String> substitutions = new ArrayList<String>();\r
\r
+ @Parameter(names = { "--load" }, description = "%TOKEN%=filename", required = false)\r
+ public List<String> loads = new ArrayList<String>();\r
+\r
@Parameter(names = { "--nomarkdown" }, description = "%STARTTOKEN%:%ENDTOKEN%", required = false)\r
public List<String> nomarkdown = new ArrayList<String>();\r
- \r
+\r
@Parameter(names = { "--regex" }, description = "searchPattern!!!replacePattern", required = false)\r
public List<String> regex = new ArrayList<String>();\r
\r
debug("class name " + className);
ByteArrayOutputStream buff = new ByteArrayOutputStream();
try {
- InputStream in = clazz.getClassLoader().getResource(className.replace('.', '/') + ".class").openStream();
+ InputStream in = clazz.getClassLoader().getResource(className.replace('.', '/') + ".class")
+ .openStream();
while (true) {
int x = in.read();
if (x < 0) {
}
case 6: {
long x = readLong();
- constantPool[i] = ConstantNumber.get("" + Double.longBitsToDouble(x), x, Constant.Type.DOUBLE);
+ constantPool[i] = ConstantNumber
+ .get("" + Double.longBitsToDouble(x), x, Constant.Type.DOUBLE);
i++;
break;
}
int field = constantPool[fieldRef].intValue();
int classIndex = field >>> 16;
int nameAndType = constantPool[field & 0xffff].intValue();
- String className = constantPool[constantPool[classIndex].intValue()] + "." + constantPool[nameAndType >>> 16]
- + " " + constantPool[nameAndType & 0xffff];
+ String className = constantPool[constantPool[classIndex].intValue()] + "."
+ + constantPool[nameAndType >>> 16] + " " + constantPool[nameAndType & 0xffff];
return className;
}
int method = constantPool[methodRef].intValue();
int classIndex = method >>> 16;
int nameAndType = constantPool[method & 0xffff].intValue();
- String className = constantPool[constantPool[classIndex].intValue()] + "." + constantPool[nameAndType >>> 16]
- + " " + constantPool[nameAndType & 0xffff];
+ String className = constantPool[constantPool[classIndex].intValue()] + "."
+ + constantPool[nameAndType >>> 16] + " " + constantPool[nameAndType & 0xffff];
return className;
}
private int readInt() {
byte[] buff = data;
- return (buff[pos++] << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8) + (buff[pos++] & 0xff);
+ return (buff[pos++] << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8)
+ + (buff[pos++] & 0xff);
}
private long readLong() {
* automatically trim strings that exceed maxLength
*/
public static void execute(String url, String user, String password, String schema, String table,
- String packageName, String folder, boolean annotateSchema, boolean trimStrings) throws SQLException {
+ String packageName, String folder, boolean annotateSchema, boolean trimStrings)
+ throws SQLException {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
Db db = Db.open(url, user, password.toCharArray());
DbInspector inspector = new DbInspector(db);
- List<String> models = inspector.generateModel(schema, table, packageName, annotateSchema, trimStrings);
+ List<String> models = inspector.generateModel(schema, table, packageName, annotateSchema,
+ trimStrings);
File parentFile;
if (StringUtils.isNullOrEmpty(folder)) {
parentFile = new File(System.getProperty("user.dir"));
return ds.getConnection(user, password);
} catch (SQLException e) {
throw e;
- } catch (Exception e) {
+ } catch (Exception e) {
throw new SQLException("Failed to get connection for " + url, e);
}
} else {
\r
/**\r
* Sets the logging level for a particular statement type.\r
- * \r
+ * \r
* @param type\r
* @param level\r
*/\r
case DEBUG:\r
logger.debug(statement);\r
break;\r
- case TRACE: \r
+ case TRACE:\r
logger.trace(statement);\r
break;\r
- case OFF: \r
+ case OFF:\r
break;\r
}\r
}\r
builder.append(x);
return this;
}
-
+
/**
* Returns the current value of the loop counter.
+ *
* @return the loop counter
*/
public int getCount() {
package com.iciql.util;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
}
return count;
}
+
+ /**
+ * Prepare text for html presentation. Replace sensitive characters with
+ * html entities.
+ *
+ * @param inStr
+ * @param changeSpace
+ * @return plain text escaped for html
+ */
+ public static String escapeForHtml(String inStr, boolean changeSpace) {
+ StringBuffer retStr = new StringBuffer();
+ int i = 0;
+ while (i < inStr.length()) {
+ if (inStr.charAt(i) == '&') {
+ retStr.append("&");
+ } else if (inStr.charAt(i) == '<') {
+ retStr.append("<");
+ } else if (inStr.charAt(i) == '>') {
+ retStr.append(">");
+ } else if (inStr.charAt(i) == '\"') {
+ retStr.append(""");
+ } else if (changeSpace && inStr.charAt(i) == ' ') {
+ retStr.append(" ");
+ } else if (changeSpace && inStr.charAt(i) == '\t') {
+ retStr.append(" ");
+ } else {
+ retStr.append(inStr.charAt(i));
+ }
+ i++;
+ }
+ return retStr.toString();
+ }
+
+ /**
+ * Replaces carriage returns and line feeds with html line breaks.
+ *
+ * @param string
+ * @return plain text with html line breaks
+ */
+ public static String breakLinesForHtml(String string) {
+ return string.replace("\r\n", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
+ }
+
+ /**
+ * Returns the string content of the specified file.
+ *
+ * @param file
+ * @param lineEnding
+ * @return the string content of the file
+ */
+ public static String readContent(File file, String lineEnding) {
+ StringBuilder sb = new StringBuilder();
+ try {
+ InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
+ BufferedReader reader = new BufferedReader(is);
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ if (lineEnding != null) {
+ sb.append(lineEnding);
+ }
+ }
+ reader.close();
+ } catch (Throwable t) {
+ System.err.println("Failed to read content of " + file.getAbsolutePath());
+ t.printStackTrace();
+ }
+ return sb.toString();
+ }
}
}\r
}\r
}\r
- throw new IciqlException(e, "Missing default constructor? Exception trying to instantiate {0}: {1}", clazz.getName(), e.getMessage());\r
+ throw new IciqlException(e,\r
+ "Missing default constructor? Exception trying to instantiate {0}: {1}",\r
+ clazz.getName(), e.getMessage());\r
}\r
}\r
};\r
}\r
}\r
}\r
- throw new IciqlException(e, "Missing default constructor?! Exception trying to instantiate {0}: {1}", clazz.getName(), e.getMessage());\r
+ throw new IciqlException(e,\r
+ "Missing default constructor?! Exception trying to instantiate {0}: {1}",\r
+ clazz.getName(), e.getMessage());\r
}\r
}\r
\r
float f = 0f;\r
try {\r
f = Float.parseFloat(s);\r
- } catch (Exception e) { \r
+ } catch (Exception e) {\r
}\r
return f > 0 || s.equals("true") || s.equals("yes");\r
}\r
*/
@Test
public void testObjectAliasMapping() throws Exception {
- Db db = IciqlSuite.openDb();
+ Db db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
// baseline count is the next id value
*/
@Test
public void testPrimitiveAliasMapping() throws Exception {
- Db db = IciqlSuite.openDb();
+ Db db = IciqlSuite.openNewDb();
PrimitivesModel model = new PrimitivesModel();
model.myLong = 100L;
db.insert(model);
@Before
public void setUp() {
- db = IciqlSuite.openDb();
+ db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(ProductAnnotationOnly.getList());
db.insertAll(ProductMixedAnnotation.getList());
// test indexes are created, and columns are in the right order
DatabaseMetaData meta = db.getConnection().getMetaData();
boolean isH2 = meta.getDatabaseProductName().equals("H2");
- ResultSet rs = meta.getIndexInfo(null, "PUBLIC", "ANNOTATEDPRODUCT", false, true);
+ 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);
+ }
// first index is primary key index
// H2 gives this a testable name.
assertTrue(rs.next());
} catch (IciqlException e) {
assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode());
}
-
+
// 10 objects, 10 autoIncremented unique values
assertEquals(10, db.from(p).selectDistinct(p.productName).size());
\r
@Test\r
public void testBooleanColumn() {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
db.insertAll(BooleanModel.getList());\r
BooleanAsIntModel b = new BooleanAsIntModel();\r
List<BooleanAsIntModel> models = db.from(b).select();\r
\r
@Test\r
public void testIntColumn() {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
// insert INT column\r
db.insertAll(BooleanAsIntModel.getList());\r
\r
@Test
public void testClob() throws Exception {
String create = "CREATE TABLE CLOB_TEST(ID INT PRIMARY KEY, WORDS {0})";
- Db db = IciqlSuite.openDb();
+ Db db = IciqlSuite.openNewDb();
db.executeUpdate(MessageFormat.format(create, "VARCHAR(255)"));
db.insertAll(StringRecord.getList());
testSimpleUpdate(db, "VARCHAR fail");
db.close();
- db = IciqlSuite.openDb();
+ db = IciqlSuite.openNewDb();
db.executeUpdate(MessageFormat.format(create, "CLOB"));
db.insertAll(StringRecord.getList());
testSimpleUpdate(db, "CLOB fail because of single quote artifacts");
}
public static List<StringRecord> getList() {
- StringRecord[] list = { create(1, "Once upon a midnight dreary, while I pondered weak and weary,"),
+ StringRecord[] list = {
+ create(1, "Once upon a midnight dreary, while I pondered weak and weary,"),
create(2, "Over many a quaint and curious volume of forgotten lore,"),
create(3, "While I nodded, nearly napping, suddenly there came a tapping,"),
create(4, "As of some one gently rapping, rapping at my chamber door."),
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import com.iciql.Db;
*/
public class ConcurrencyTest {
- private Db db;
- private int numberOfTests = 200;
+ private int numberOfTests = 800;
@Before
public void setUp() {
- db = IciqlSuite.openDb();
+ Db db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
}
- @After
- public void tearDown() {
- db.close();
- }
-
@Test
public void testAliasSharing() throws Exception {
- // Single-threaded example of why aliases can NOT be shared.
- Product p = new Product();
- Query<Product> query1 = db.from(p);
- Query<Product> query2 = db.from(p);
-
- // if you could share alias instances both counts should be equal
- long count1 = 0;
+ Db db = IciqlSuite.openCurrentDb();
try {
- count1 = query1.where(p.category).is("Beverages").selectCount();
- } catch (IciqlException e) {
- assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode());
+ // Single-threaded example of why aliases can NOT be shared.
+ Product p = new Product();
+ Query<Product> query1 = db.from(p);
+ Query<Product> query2 = db.from(p);
+
+ // if you could share alias instances both counts should be equal
+ long count1 = 0;
+ try {
+ count1 = query1.where(p.category).is("Beverages").selectCount();
+ } catch (IciqlException e) {
+ assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode());
+ }
+ long count2 = query2.where(p.category).is("Beverages").selectCount();
+
+ // but they aren't
+ assertEquals(0, count1);
+ assertEquals(2, count2);
+ assertTrue(count1 != count2);
+ } finally {
+ db.close();
}
- long count2 = query2.where(p.category).is("Beverages").selectCount();
-
- // but they aren't
- assertEquals(0, count1);
- assertEquals(2, count2);
- assertTrue(count1 != count2);
}
@Test
+ @Ignore
public void testConcurrencyFinal() throws Exception {
// Multi-threaded example of why aliases can NOT be shared.
//
}
@Test
+ @Ignore
public void testConcurrencyThreadLocal() throws Exception {
List<Thread> threads = Utils.newArrayList();
final AtomicInteger failures = new AtomicInteger(0);
}
private void test(int testCase, Product p) throws AssertionError {
- List<Product> list;
- switch (testCase) {
- case 0:
- list = db.from(p).where(p.productName).is("Chai").select();
- assertEquals(1, list.size());
- assertEquals("Chai", list.get(0).productName);
- break;
- case 1:
- list = db.from(p).where(p.category).is("Condiments").select();
- assertEquals(5, list.size());
- break;
- case 3:
- list = db.from(p).where(p.productName).is("Aniseed Syrup").select();
- assertEquals(1, list.size());
- assertEquals("Aniseed Syrup", list.get(0).productName);
- break;
- case 4:
- list = db.from(p).where(p.productName).like("Chef%").select();
- assertEquals(2, list.size());
- assertTrue(list.get(0).productName.startsWith("Chef"));
- assertTrue(list.get(1).productName.startsWith("Chef"));
- break;
- case 6:
- list = db.from(p).where(p.unitsInStock).exceeds(0).select();
- assertEquals(9, list.size());
- break;
- case 7:
- list = db.from(p).where(p.unitsInStock).is(0).select();
- assertEquals(1, list.size());
- assertEquals("Chef Anton's Gumbo Mix", list.get(0).productName);
- break;
- case 9:
- list = db.from(p).where(p.productId).is(7).select();
- assertEquals(1, list.size());
- assertTrue(7 == list.get(0).productId);
- break;
- default:
- list = db.from(p).select();
- assertEquals(10, list.size());
+ Db db = IciqlSuite.openCurrentDb();
+ try {
+ List<Product> list;
+ switch (testCase) {
+ case 0:
+ list = db.from(p).where(p.productName).is("Chai").select();
+ assertEquals(1, list.size());
+ assertEquals("Chai", list.get(0).productName);
+ break;
+ case 1:
+ list = db.from(p).where(p.category).is("Condiments").select();
+ assertEquals(5, list.size());
+ break;
+ case 3:
+ list = db.from(p).where(p.productName).is("Aniseed Syrup").select();
+ assertEquals(1, list.size());
+ assertEquals("Aniseed Syrup", list.get(0).productName);
+ break;
+ case 4:
+ list = db.from(p).where(p.productName).like("Chef%").select();
+ assertEquals(2, list.size());
+ assertTrue(list.get(0).productName.startsWith("Chef"));
+ assertTrue(list.get(1).productName.startsWith("Chef"));
+ break;
+ case 6:
+ list = db.from(p).where(p.unitsInStock).exceeds(0).select();
+ assertEquals(9, list.size());
+ break;
+ case 7:
+ list = db.from(p).where(p.unitsInStock).is(0).select();
+ assertEquals(1, list.size());
+ assertEquals("Chef Anton's Gumbo Mix", list.get(0).productName);
+ break;
+ case 9:
+ list = db.from(p).where(p.productId).is(7).select();
+ assertEquals(1, list.size());
+ assertTrue(7 == list.get(0).productId);
+ break;
+ default:
+ list = db.from(p).select();
+ assertEquals(10, list.size());
+ }
+ } finally {
+ db.close();
}
}
}
\r
@Test\r
public void testDefaultObjectValues() {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
\r
// insert random model\r
DefaultValuesModel model = new DefaultValuesModel();\r
\r
@Before\r
public void setUp() {\r
- db = IciqlSuite.openDb();\r
+ db = IciqlSuite.openNewDb();\r
db.insertAll(EnumIdModel.createList());\r
db.insertAll(EnumOrdinalModel.createList());\r
db.insertAll(EnumStringModel.createList());\r
*/\r
package com.iciql.test;\r
\r
+import java.io.PrintStream;\r
import java.sql.SQLException;\r
import java.text.MessageFormat;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.List;\r
import java.util.concurrent.atomic.AtomicInteger;\r
\r
import org.junit.Assert;\r
import org.junit.runners.Suite;\r
import org.junit.runners.Suite.SuiteClasses;\r
\r
+import com.beust.jcommander.JCommander;\r
+import com.beust.jcommander.Parameter;\r
+import com.beust.jcommander.ParameterException;\r
+import com.beust.jcommander.Parameters;\r
+import com.iciql.Constants;\r
import com.iciql.Db;\r
+import com.iciql.util.StringUtils;\r
\r
/**\r
* JUnit 4 iciql test suite.\r
RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UUIDTest.class })\r
public class IciqlSuite {\r
\r
- private static final TestDb[] TEST_DBS = { \r
- new TestDb("H2", "jdbc:h2:mem:"),\r
- new TestDb("HSQL", "jdbc:hsqldb:mem:db{0,number,000}") };\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
\r
private static final TestDb DEFAULT_TEST_DB = TEST_DBS[0];\r
\r
+ private static final PrintStream ERR = System.err;\r
+\r
private static AtomicInteger openCount = new AtomicInteger(0);\r
\r
+ private static String username = "sa";\r
+\r
+ private static String password = "sa";\r
+\r
+ private static PrintStream out = System.out;\r
+\r
public static void assertStartsWith(String value, String startsWith) {\r
Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", startsWith, value),\r
value.startsWith(startsWith));\r
}\r
\r
- public static Db openDb() {\r
+ /**\r
+ * Increment the database counter, open and create a new database.\r
+ * \r
+ * @return a fresh database\r
+ */\r
+ public static Db openNewDb() {\r
String testUrl = System.getProperty("iciql.url");\r
if (testUrl == null) {\r
testUrl = DEFAULT_TEST_DB.url;\r
}\r
testUrl = MessageFormat.format(testUrl, openCount.incrementAndGet());\r
- return Db.open(testUrl, "sa", "sa");\r
+ return Db.open(testUrl, username, password);\r
}\r
\r
- public static String getDatabaseName(Db db) {\r
+ /**\r
+ * Open the current database.\r
+ * \r
+ * @return the current database\r
+ */\r
+ public static Db openCurrentDb() {\r
+ String testUrl = System.getProperty("iciql.url");\r
+ if (testUrl == null) {\r
+ testUrl = DEFAULT_TEST_DB.url;\r
+ }\r
+ testUrl = MessageFormat.format(testUrl, openCount.get());\r
+ return Db.open(testUrl, username, password);\r
+ }\r
+\r
+ /**\r
+ * Returns the name of the underlying database engine for the Db object.\r
+ * \r
+ * @param db\r
+ * @return the database engine name\r
+ */\r
+ public static String getDatabaseEngineName(Db db) {\r
String database = "";\r
try {\r
database = db.getConnection().getMetaData().getDatabaseProductName();\r
return database;\r
}\r
\r
- public static void main(String... args) {\r
+ /**\r
+ * Returns true if the underlying database engine is Derby.\r
+ * \r
+ * @param db\r
+ * @return true if underlying database engine is Derby\r
+ */\r
+ public static boolean isDerby(Db db) {\r
+ return IciqlSuite.getDatabaseEngineName(db).equals("Apache Derby");\r
+ }\r
+\r
+ /**\r
+ * Returns true if the underlying database engine is H2.\r
+ * \r
+ * @param db\r
+ * @return true if underlying database engine is H2\r
+ */\r
+ public static boolean isH2(Db db) {\r
+ return IciqlSuite.getDatabaseEngineName(db).equals("H2");\r
+ }\r
+\r
+ /**\r
+ * Gets the default schema of the underlying database engine.\r
+ * \r
+ * @param db\r
+ * @return the default schema\r
+ */\r
+ public static String getDefaultSchema(Db db) {\r
+ if (isDerby(db)) {\r
+ // Derby sets default schema name to username\r
+ return username.toUpperCase();\r
+ }\r
+ return "PUBLIC";\r
+ }\r
+\r
+ /**\r
+ * Main entry point for the test suite. Executing this method will run the\r
+ * test suite on all registered databases.\r
+ * \r
+ * @param args\r
+ * @throws Exception\r
+ */\r
+ public static void main(String... args) throws Exception {\r
+ Params params = new Params();\r
+ JCommander jc = new JCommander(params);\r
+ try {\r
+ jc.parse(args);\r
+ } catch (ParameterException t) {\r
+ usage(jc, t);\r
+ }\r
+\r
+ // Replace System.out with a file\r
+ if (!StringUtils.isNullOrEmpty(params.outputFile)) {\r
+ out = new PrintStream(params.outputFile);\r
+ System.setErr(out);\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
+\r
+ // Header\r
+ out.println(dividerMajor);\r
+ out.println(MessageFormat.format("{0} {1} ({2}) testing {3} databases", Constants.NAME,\r
+ Constants.VERSION, Constants.VERSION_DATE, TEST_DBS.length));\r
+ out.println(dividerMajor);\r
+ out.println();\r
+\r
+ showProperty("java.vendor");\r
+ showProperty("java.runtime.version");\r
+ showProperty("java.vm.name");\r
+ showProperty("os.name");\r
+ showProperty("os.version");\r
+ showProperty("os.arch");\r
+ out.println();\r
+\r
+ // Test a database\r
for (TestDb testDb : TEST_DBS) {\r
- System.out.println("*********************************************");\r
- System.out.println("Testing " + testDb.name + " " + testDb.getVersion());\r
- System.out.println("*********************************************");\r
+ out.println(dividerMinor);\r
+ out.println("Testing " + testDb.name + " " + testDb.getVersion());\r
+ out.println(dividerMinor);\r
System.setProperty("iciql.url", testDb.url);\r
Result result = JUnitCore.runClasses(suiteClasses.value());\r
- System.out.println(MessageFormat.format("{0} runs, {1} failures, {2} ignores in {3} msecs",\r
+ testDb.runtime = result.getRunTime();\r
+ if (testDb.runtime < quickestDatabase) {\r
+ quickestDatabase = testDb.runtime;\r
+ }\r
+ out.println(MessageFormat.format("{0} tests: {1} failures, {2} ignores in {3,number,0.000} secs",\r
result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(),\r
- result.getRunTime()));\r
- for (Failure failure : result.getFailures()) {\r
- System.out.println(MessageFormat.format("{0}: {1}", failure.getTestHeader(),\r
- failure.getMessage()));\r
+ result.getRunTime() / 1000f));\r
+ if (result.getFailureCount() == 0) {\r
+ out.println();\r
+ } else {\r
+ for (Failure failure : result.getFailures()) {\r
+ out.println(MessageFormat.format("\n + {0}\n {1}\n", failure.getTestHeader(),\r
+ failure.getMessage()));\r
+ }\r
}\r
+ }\r
+\r
+ // Display runtime results sorted by performance leader\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
+ out.println(dividerMajor);\r
+ List<TestDb> dbs = Arrays.asList(TEST_DBS);\r
+ Collections.sort(dbs, new Comparator<TestDb>() {\r
+\r
+ @Override\r
+ public int compare(TestDb o1, TestDb o2) {\r
+ if (o1.runtime == o2.runtime) {\r
+ return 0;\r
+ }\r
+ if (o1.runtime > o2.runtime) {\r
+ return 1;\r
+ }\r
+ return -1;\r
+ }\r
+ });\r
+ for (TestDb testDb : dbs) {\r
+ out.println(MessageFormat.format("{0} {1} {2,number,0.000} secs ({3,number,#.0}x)",\r
+ StringUtils.pad(testDb.name, 6, " ", true),\r
+ StringUtils.pad(testDb.getVersion(), 22, " ", true), testDb.runtime / 1000f,\r
+ ((double) testDb.runtime) / quickestDatabase));\r
+ }\r
+\r
+ // close PrintStream and restore System.err\r
+ out.close();\r
+ System.setErr(ERR);\r
+ }\r
+\r
+ private static void showProperty(String name) {\r
+ out.print(StringUtils.pad(name, 25, " ", true));\r
+ out.println(System.getProperty(name));\r
+ }\r
+\r
+ private static void usage(JCommander jc, ParameterException t) {\r
+ System.out.println(Constants.NAME + " test suite v" + Constants.VERSION);\r
+ System.out.println();\r
+ if (t != null) {\r
+ System.out.println(t.getMessage());\r
System.out.println();\r
}\r
+ if (jc != null) {\r
+ jc.usage();\r
+ }\r
+ System.exit(0);\r
+ }\r
+\r
+ private static String buildDivider(char c, int length) {\r
+ StringBuilder sb = new StringBuilder();\r
+ for (int i = 0; i < length; i++) {\r
+ sb.append(c);\r
+ }\r
+ return sb.toString();\r
}\r
\r
/**\r
private static class TestDb {\r
final String name;\r
final String url;\r
+ String version;\r
+ long runtime;\r
\r
TestDb(String name, String url) {\r
this.name = name;\r
}\r
\r
String getVersion() {\r
- try {\r
- Db db = Db.open(url, "sa", "sa");\r
- String version = db.getConnection().getMetaData().getDatabaseProductVersion();\r
- db.close();\r
- return version;\r
- } catch (SQLException s) {\r
+ if (version == null) {\r
+ try {\r
+ Db db = Db.open(url, username, password);\r
+ version = db.getConnection().getMetaData().getDatabaseProductVersion();\r
+ db.close();\r
+ return version;\r
+ } catch (SQLException s) {\r
+ version = "";\r
+ }\r
}\r
- return "";\r
+ return version;\r
}\r
}\r
-}\r
+\r
+ /**\r
+ * Command-line parameters for TestSuite.\r
+ */\r
+ @Parameters(separators = " ")\r
+ private static class Params {\r
+\r
+ @Parameter(names = { "--outputFile" }, description = "Results text file", required = false)\r
+ public String outputFile;\r
+ }\r
+}
\ No newline at end of file
import static org.junit.Assert.assertTrue;
import java.sql.SQLException;
+import java.text.MessageFormat;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Before
public void setUp() {
- db = IciqlSuite.openDb();
+ db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(ProductAnnotationOnly.getList());
db.insertAll(ProductMixedAnnotation.getList());
@Test
public void testValidateModels() {
- boolean isH2 = IciqlSuite.getDatabaseName(db).equals("H2");
+ boolean isH2 = IciqlSuite.isH2(db);
+ boolean isDerby = IciqlSuite.isDerby(db);
+ String schemaName = IciqlSuite.getDefaultSchema(db);
+
DbInspector inspector = new DbInspector(db);
- validateModel(inspector, new Product(), 3);
- validateModel(inspector, new ProductAnnotationOnly(), isH2 ? 2 : 3);
- validateModel(inspector, new ProductMixedAnnotation(), isH2 ? 3 : 4);
+ validateModel(inspector, schemaName, new Product(), 3);
+ validateModel(inspector, schemaName, new ProductAnnotationOnly(), (isH2 || isDerby) ? 2 : 3);
+ validateModel(inspector, schemaName, new ProductMixedAnnotation(), 4);
}
- private void validateModel(DbInspector inspector, Object o, int expected) {
+ private void validateModel(DbInspector inspector, String schemaName, Object o, int expected) {
List<ValidationRemark> remarks = inspector.validateModel(o, false);
assertTrue("validation remarks are null for " + o.getClass().getName(), remarks != null);
StringBuilder sb = new StringBuilder();
errorCollector.addError(new SQLException(remark.toString()));
}
}
- assertTrue(remarks.get(0).message.equals("@IQSchema(name=PUBLIC)"));
+ assertTrue(remarks.get(0).message.equals(MessageFormat.format("@IQSchema(name={0})", schemaName)));
assertEquals(sb.toString(), expected, remarks.size());
}
true);
assertEquals(1, models.size());
// a poor test, but a start
- String dbName = IciqlSuite.getDatabaseName(db);
+ String dbName = IciqlSuite.getDatabaseEngineName(db);
if (dbName.equals("H2")) {
assertEquals(1478, models.get(0).length());
} else if (dbName.startsWith("HSQL")) {
// HSQL uses Double instead of Float
assertEquals(1479, models.get(0).length());
+ } else if (dbName.equals("Apache Derby")) {
+ // Derby uses java.sql.Timestamp not java.util.Date
+ // Derby uses username as schema name
+ assertEquals(1489, models.get(0).length());
}
}
@Test
public void testTableUpgrade() {
- Db db = IciqlSuite.openDb();
+ Db db = IciqlSuite.openNewDb();
// insert first, this will create version record automatically
List<SupportedTypes> original = SupportedTypes.createList();
\r
@Test\r
public void testPrimitives() {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
\r
// insert random models in reverse order\r
List<PrimitivesModel> models = PrimitivesModel.getList();\r
List<PrimitivesModel> list = db.from(p).orderBy(p.myLong).select();\r
assertEquals(models.size(), list.size());\r
assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString());\r
- \r
+\r
// test model update\r
retrievedModel.myInteger = 1337;\r
assertEquals(1, db.update(retrievedModel));\r
- assertEquals(1, db.delete(retrievedModel)); \r
+ assertEquals(1, db.delete(retrievedModel));\r
\r
db.close();\r
}\r
\r
@Test\r
public void testRuntimeQuery() {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
db.insertAll(Product.getList());\r
\r
Product p = new Product();\r
\r
@Test\r
public void testExecuteQuery() throws SQLException {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
db.insertAll(Product.getList());\r
\r
// test plain statement\r
- List<Product> products = db.executeQuery(Product.class, "select * from product where unitsInStock=120");\r
+ List<Product> products = db.executeQuery(Product.class,\r
+ "select * from product where unitsInStock=120");\r
assertEquals(1, products.size());\r
assertEquals("Condiments", products.get(0).category);\r
- \r
+\r
// test prepared statement\r
products = db.executeQuery(Product.class, "select * from product where unitsInStock=?", 120);\r
assertEquals(1, products.size());\r
\r
db.close();\r
}\r
- \r
+\r
@Test\r
public void testBuildObjects() throws SQLException {\r
- Db db = IciqlSuite.openDb();\r
+ Db db = IciqlSuite.openNewDb();\r
db.insertAll(Product.getList());\r
\r
// test plain statement\r
\r
assertEquals(1, products.size());\r
assertEquals("Condiments", products.get(0).category);\r
- \r
+\r
// test prepared statement\r
rs = db.executeQuery("select * from product where unitsInStock=?", 120);\r
products = db.buildObjects(Product.class, rs);\r
\r
@Before\r
public void setUp() {\r
- db = IciqlSuite.openDb();\r
+ db = IciqlSuite.openNewDb();\r
db.insertAll(Product.getList());\r
db.insertAll(Customer.getList());\r
db.insertAll(Order.getList());\r
@Test\r
public void testSum() {\r
Product p = new Product();\r
- Long sum = db.from(p).selectFirst(sum(p.unitsInStock));\r
+ Number sum = db.from(p).selectFirst(sum(p.unitsInStock));\r
assertEquals(323, sum.intValue());\r
Double sumPrice = db.from(p).selectFirst(sum(p.unitPrice));\r
assertEquals(313.35, sumPrice.doubleValue(), 0.001);\r
@Before
public void setUp() throws Exception {
- db = IciqlSuite.openDb();
+ db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(Customer.getList());
db.insertAll(Order.getList());
package com.iciql.test.models;
+import static com.iciql.Define.length;
import static com.iciql.Define.primaryKey;
import java.math.BigDecimal;
public void defineIQ() {
primaryKey(id);
+ length(name, 25);
}
public static List<ComplexObject> getList() {
\r
@IQColumn(length = 25)\r
public String customerId;\r
- \r
+\r
@IQColumn(length = 2)\r
public String region;\r
\r
\r
@IQColumn(primaryKey = true, autoIncrement = true)\r
public Long myLong;\r
- \r
+\r
@SuppressWarnings("deprecation")\r
@IQColumn\r
public Date myDate = new Date(100, 7, 1);\r
- \r
+\r
@IQColumn\r
public Integer myInteger = 12345;\r
- \r
+\r
@IQColumn\r
public Tree myEnumIdTree = Tree.WALNUT;\r
\r
public class Order implements Iciql {\r
public String customerId;\r
public Integer orderId;\r
- public Date orderDate; \r
+ public Date orderDate;\r
public BigDecimal total;\r
\r
public Order(String customerId, Integer orderId, String total, String orderDate) {\r
\r
public static List<Order> getList() {\r
Order[] list = { new Order("ALFKI", 10702, "330.00", "2007-01-02"),\r
- new Order("ALFKI", 10952, "471.20", "2007-02-03"), new Order("ANATR", 10308, "88.80", "2007-01-03"),\r
- new Order("ANATR", 10625, "479.75", "2007-03-03"), new Order("ANATR", 10759, "320.00", "2007-04-01"),\r
- new Order("ANTON", 10365, "403.20", "2007-02-13"), new Order("ANTON", 10682, "375.50", "2007-03-13"),\r
+ new Order("ALFKI", 10952, "471.20", "2007-02-03"),\r
+ new Order("ANATR", 10308, "88.80", "2007-01-03"),\r
+ new Order("ANATR", 10625, "479.75", "2007-03-03"),\r
+ new Order("ANATR", 10759, "320.00", "2007-04-01"),\r
+ new Order("ANTON", 10365, "403.20", "2007-02-13"),\r
+ new Order("ANTON", 10682, "375.50", "2007-03-13"),\r
new Order("ANTON", 10355, "480.00", "2007-04-11") };\r
return Arrays.asList(list);\r
}\r
}\r
return list;\r
}\r
- \r
+\r
@Override\r
public String toString() {\r
return String.valueOf(myLong);\r
index(productName, category);\r
}\r
\r
- private static Product create(int productId, String productName, String category, double unitPrice, int unitsInStock) {\r
+ private static Product create(int productId, String productName, String category, double unitPrice,\r
+ int unitsInStock) {\r
return new Product(productId, productName, category, unitPrice, unitsInStock);\r
}\r
\r
create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120),\r
create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15),\r
create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6),\r
- create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29), create(10, "Ikura", "Seafood", 31.0, 31), };\r
+ create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29),\r
+ create(10, "Ikura", "Seafood", 31.0, 31), };\r
\r
return Arrays.asList(list);\r
}\r
private static ProductInheritedAnnotation create(int productId, String productName, String category,
double unitPrice, int unitsInStock, String mappedField) {
- return new ProductInheritedAnnotation(productId, productName, category, unitPrice, unitsInStock, mappedField);
+ return new ProductInheritedAnnotation(productId, productName, category, unitPrice, unitsInStock,
+ mappedField);
}
public static List<ProductInheritedAnnotation> getData() {
import java.util.Arrays;
import java.util.List;
-import com.iciql.Iciql.IQColumn;
+import com.iciql.Define;
+import com.iciql.Iciql;
import com.iciql.Iciql.IQIndex;
import com.iciql.Iciql.IQTable;
*/
@IQTable(annotationsOnly = false)
-@IQIndex({"name", "cat" })
-public class ProductMixedAnnotation {
+@IQIndex({ "name", "cat" })
+public class ProductMixedAnnotation implements Iciql {
public Double unitPrice;
public Integer unitsInStock;
this.mappedField = mappedField;
}
- private static ProductMixedAnnotation create(int productId, String productName, String category, double unitPrice,
- int unitsInStock, String mappedField) {
- return new ProductMixedAnnotation(productId, productName, category, unitPrice, unitsInStock, mappedField);
+ private static ProductMixedAnnotation create(int productId, String productName, String category,
+ double unitPrice, int unitsInStock, String mappedField) {
+ return new ProductMixedAnnotation(productId, productName, category, unitPrice, unitsInStock,
+ mappedField);
}
public static List<ProductMixedAnnotation> getList() {
return productName;
}
+ @Override
+ public void defineIQ() {
+ Define.length(mappedField, 25);
+ }
}
public static List<SupportedTypes> createList() {
List<SupportedTypes> list = Utils.newArrayList();
+ long now = System.currentTimeMillis();
+ long oneday = 24 * 60 * 60 * 1000L;
for (int i = 0; i < 10; i++) {
- list.add(randomValue());
+ list.add(randomValue(now - (i * oneday)));
}
return list;
}
- static SupportedTypes randomValue() {
+ static SupportedTypes randomValue(long time) {
Random rand = new Random();
SupportedTypes s = new SupportedTypes();
s.myBool = new Boolean(rand.nextBoolean());
// scale must match annotation
s.myBigDecimal = s.myBigDecimal.setScale(5, RoundingMode.UP);
s.myString = Long.toHexString(rand.nextLong());
- s.myUtilDate = new java.util.Date(rand.nextLong());
- s.mySqlDate = new java.sql.Date(rand.nextLong());
- s.mySqlTime = new java.sql.Time(rand.nextLong());
- s.mySqlTimestamp = new java.sql.Timestamp(rand.nextLong());
+ s.myUtilDate = new java.util.Date(time);
+ s.mySqlDate = new java.sql.Date(time);
+ s.mySqlTime = new java.sql.Time(time);
+ s.mySqlTimestamp = new java.sql.Timestamp(time);
s.myBlob = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
s.myDefaultFlower = Flower.DAFFODIL;
s.myFavoriteFlower = Flower.MUM;
same &= myOtherFavoriteTree.equals(s.myOtherFavoriteTree);
return same;
}
-
+
/**
* This class demonstrates the table upgrade.
*/