Browse Source

Added Derby dialect. Finished HSQL dialect. Documentation.

* 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.
tags/v0.6.6
James Moger 12 years ago
parent
commit
66e810aaf2
61 changed files with 1013 additions and 310 deletions
  1. 6
    5
      .classpath
  2. 3
    0
      .gitignore
  3. 8
    0
      NOTICE
  4. 11
    5
      README.markdown
  5. 15
    1
      build.xml
  6. 8
    4
      docs/00_index.mkd
  7. 24
    0
      docs/02_usage.mkd
  8. 0
    23
      docs/03_natural_syntax.mkd
  9. 16
    0
      docs/03_performance.mkd
  10. 1
    0
      docs/05_building.mkd
  11. 9
    2
      docs/05_releases.mkd
  12. 2
    2
      src/com/iciql/Constants.java
  13. 20
    2
      src/com/iciql/Db.java
  14. 7
    4
      src/com/iciql/DbInspector.java
  15. 4
    3
      src/com/iciql/Iciql.java
  16. 32
    21
      src/com/iciql/IciqlException.java
  17. 4
    8
      src/com/iciql/ModelUtils.java
  18. 6
    11
      src/com/iciql/Query.java
  19. 1
    0
      src/com/iciql/QueryBetween.java
  20. 1
    0
      src/com/iciql/QueryWhere.java
  21. 19
    12
      src/com/iciql/SQLDialect.java
  22. 23
    9
      src/com/iciql/SQLDialectDefault.java
  23. 151
    0
      src/com/iciql/SQLDialectDerby.java
  24. 4
    3
      src/com/iciql/SQLDialectH2.java
  25. 94
    5
      src/com/iciql/SQLDialectHSQL.java
  26. 6
    5
      src/com/iciql/SQLDialectMySQL.java
  27. 8
    1
      src/com/iciql/SQLStatement.java
  28. 19
    8
      src/com/iciql/TableDefinition.java
  29. 2
    1
      src/com/iciql/TableInspector.java
  30. 0
    1
      src/com/iciql/UpdateColumnSet.java
  31. 8
    5
      src/com/iciql/build/Build.java
  32. 17
    6
      src/com/iciql/build/BuildSite.java
  33. 10
    7
      src/com/iciql/bytecode/ClassReader.java
  34. 4
    2
      src/com/iciql/util/GenerateModels.java
  35. 1
    1
      src/com/iciql/util/JdbcUtils.java
  36. 3
    3
      src/com/iciql/util/Slf4jStatementListener.java
  37. 2
    1
      src/com/iciql/util/StatementBuilder.java
  38. 74
    0
      src/com/iciql/util/StringUtils.java
  39. 7
    3
      src/com/iciql/util/Utils.java
  40. 2
    2
      tests/com/iciql/test/AliasMapTest.java
  41. 11
    3
      tests/com/iciql/test/AnnotationsTest.java
  42. 2
    2
      tests/com/iciql/test/BooleanModelTest.java
  43. 4
    3
      tests/com/iciql/test/ClobTest.java
  44. 70
    64
      tests/com/iciql/test/ConcurrencyTest.java
  45. 1
    1
      tests/com/iciql/test/DefaultValuesTest.java
  46. 1
    1
      tests/com/iciql/test/EnumsTest.java
  47. 220
    23
      tests/com/iciql/test/IciqlSuite.java
  48. 17
    9
      tests/com/iciql/test/ModelsTest.java
  49. 3
    3
      tests/com/iciql/test/PrimitivesTest.java
  50. 8
    7
      tests/com/iciql/test/RuntimeQueryTest.java
  51. 2
    2
      tests/com/iciql/test/SamplesTest.java
  52. 1
    1
      tests/com/iciql/test/UpdateTest.java
  53. 2
    0
      tests/com/iciql/test/models/ComplexObject.java
  54. 1
    1
      tests/com/iciql/test/models/Customer.java
  55. 3
    3
      tests/com/iciql/test/models/DefaultValuesModel.java
  56. 7
    4
      tests/com/iciql/test/models/Order.java
  57. 1
    1
      tests/com/iciql/test/models/PrimitivesModel.java
  58. 4
    2
      tests/com/iciql/test/models/Product.java
  59. 2
    1
      tests/com/iciql/test/models/ProductInheritedAnnotation.java
  60. 12
    6
      tests/com/iciql/test/models/ProductMixedAnnotation.java
  61. 9
    7
      tests/com/iciql/test/models/SupportedTypes.java

+ 6
- 5
.classpath View File

@@ -14,11 +14,6 @@
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/markdownpapers-core-1.1.0-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="ext/h2-1.3.158.jar" sourcepath="ext/h2-1.3.158-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/h2-1.3.158-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="ext/doclava-1.0.3.jar" sourcepath="ext/doclava-1.0.3-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/doclava-1.0.3-javadoc.jar!/"/>
@@ -30,5 +25,11 @@
</attributes>
</classpathentry>
<classpathentry kind="lib" path="ext/hsqldb-2.2.4.jar"/>
<classpathentry kind="lib" path="ext/derby-10.8.1.2.jar"/>
<classpathentry kind="lib" path="ext/h2-1.3.159.jar" sourcepath="ext/h2-1.3.159-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/iciql/ext/h2-1.3.159-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>

+ 3
- 0
.gitignore View File

@@ -6,3 +6,6 @@
/*.zip
/*.jar
/build.properties
/derby.log
/performance.txt
/performance_db.txt

+ 8
- 0
NOTICE View File

@@ -45,6 +45,14 @@ HSQL Database
http://hsqldb.org
---------------------------------------------------------------------------
Derby Database
---------------------------------------------------------------------------
Derby, released under the
Apache Software License, Version 2.0.
http://db.apache.org/derby
---------------------------------------------------------------------------
MarkdownPapers
---------------------------------------------------------------------------

+ 11
- 5
README.markdown View File

@@ -5,7 +5,7 @@ iciql **is**...
- a model-based, database access wrapper for JDBC
- for modest database schemas and basic statement generation
- for those who want to write code, instead of SQL, using IDE completion and compile-time type-safety
- small (100KB) with no runtime dependencies
- small (125KB) with no runtime dependencies
- pronounced *icicle* (although it could be French: *ici ql* - here query language)
- a friendly fork of the H2 [JaQu](http://h2database.com/html/jaqu.html) project
@@ -15,11 +15,17 @@ iciql **is not**...
- designed to compete with more powerful database query tools like [jOOQ](http://jooq.sourceforge.net) or [Querydsl](http://source.mysema.com/display/querydsl/Querydsl)
- designed to compete with enterprise ORM tools like [Hibernate](http://www.hibernate.org) or [mybatis](http://www.mybatis.org)
Supported Databases
Supported Databases (Unit-Tested)
-------
- [H2 1.3](http://h2database.com)
- [HSQLDB 2.2](http://hsqldb.org)
- Support for others is planned and may only require creating a simple "dialect" class.
- [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
Partially Supported Databases (not Unit-Tested)
-------
- [MySQL](http://mysql.com)
Support for others is planned and may only require creating a simple "dialect" class.
License
-------

+ 15
- 1
build.xml View File

@@ -99,12 +99,21 @@
</path>
<javac destdir="${project.build.dir}" failonerror="false">
<src path="${basedir}/src" />
<src path="${basedir}/tests" />
<classpath refid="master-classpath" />
</javac>
<copy todir="${project.build.dir}">
<fileset dir="${basedir}/src" excludes="**/*.java,**/thumbs.db" />
</copy>
<!-- Execute the test suite -->
<echo>Executing iciql ${iq.version} test suite</echo>
<java classpath="${project.build.dir}" classname="com.iciql.test.IciqlSuite">
<classpath refid="master-classpath" />
<arg value="--outputFile" />
<arg value="${basedir}/performance_db.txt" />
</java>
<!-- Build Standard Javadoc -->
<delete dir="${basedir}/javadoc" />
<javadoc destdir="${basedir}/javadoc" nonavbar="true" stylesheetfile="${basedir}/docs/resources/javadoc.css">
@@ -170,6 +179,7 @@
<fileset dir="${project.build.dir}">
<include name="**/*" />
<exclude name="com/iciql/build/" />
<exclude name="com/iciql/tests/" />
<exclude name="**/*.html" />
</fileset>
</jar>
@@ -186,6 +196,7 @@
<fileset dir="${basedir}/src">
<include name="**/*" />
<exclude name="com/iciql/build/" />
<exclude name="com/iciql/tests/" />
<exclude name="**/*.html" />
</fileset>
</jar>
@@ -229,7 +240,7 @@
<include name="**/*" />
</fileset>
</copy>
<!-- Build site pages -->
<java classpath="${project.build.dir}" classname="com.iciql.build.BuildSite">
<classpath refid="master-classpath" />
@@ -281,6 +292,9 @@
<arg value="--regex" />
<arg value="&quot;\b(issue)(\s*[#]?|-){0,1}(\d+)\b!!!&lt;a href='http://code.google.com/p/iciql/issues/detail?id=$3'&gt;issue $3&lt;/a&gt;&quot;" />
<arg value="--load" />
<arg value="%DBPERFORMANCE%=${basedir}/performance_db.txt" />
</java>
</target>

+ 8
- 4
docs/00_index.mkd View File

@@ -5,7 +5,7 @@ iciql **is**...
- a model-based, database access wrapper for JDBC
- for modest database schemas and basic statement generation
- for those who want to write code, instead of SQL, using IDE completion and compile-time type-safety
- small (120KB) with no runtime dependencies
- small (125KB) with no runtime dependencies
- pronounced *icicle* (although it could be French: *ici ql* - here query language)
- a friendly fork of the H2 [JaQu][jaqu] project
@@ -35,9 +35,13 @@ select * from products
</tr>
</table>
### Supported Databases
- [H2 1.3](http://h2database.com)
- [HSQLDB 2.2](http://hsqldb.org)
### 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
### Partially Supported Databases (not Unit-Tested)
- [MySQL](http://mysql.com)
Support for others is planned and may only require creating a simple "dialect" class.

+ 24
- 0
docs/02_usage.mkd View File

@@ -65,6 +65,30 @@ List&lt;Product&gt; allProducts = db.buildObjects(Product.class, rs);
JdbcUtils.closeSilently(rs, true);
%ENDCODE%
### Natural Syntax
**work-in-progress**
The original JaQu source offers partial support for Java expressions in *where* clauses.
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.
A proof-of-concept decompiler is included, but is incomplete.
The proposed syntax is:
%BEGINCODE%
long count = db.from(co).
where(new Filter() { public boolean where() {
return co.id == x
&amp;&amp; co.name.equals(name)
&amp;&amp; co.value == new BigDecimal("1")
&amp;&amp; co.amount == 1L
&amp;&amp; co.birthday.before(new java.util.Date())
&amp;&amp; co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))
&amp;&amp; co.time.before(java.sql.Time.valueOf("23:23:23"));
}
}).selectCount();
%ENDCODE%
### JDBC Statements, ResultSets, and Exception Handling
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.

+ 0
- 23
docs/03_natural_syntax.mkd View File

@@ -1,23 +0,0 @@
## Natural Syntax
**work-in-progress**
The original JaQu source offers partial support for Java expressions in *where* clauses.
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.
A proof-of-concept decompiler is included, but is incomplete.
The proposed syntax is:
%BEGINCODE%
long count = db.from(co).
where(new Filter() { public boolean where() {
return co.id == x
&amp;&amp; co.name.equals(name)
&amp;&amp; co.value == new BigDecimal("1")
&amp;&amp; co.amount == 1L
&amp;&amp; co.birthday.before(new java.util.Date())
&amp;&amp; co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))
&amp;&amp; co.time.before(java.sql.Time.valueOf("23:23:23"));
}
}).selectCount();
%ENDCODE%

+ 16
- 0
docs/03_performance.mkd View File

@@ -0,0 +1,16 @@
## Performance
The information provided here may be based on flawed test procedures. You have to be the judge of what is performant and non-performant.
### iciql statement generation
Performance of iciql statement generation is not currently benchmarked but that is planned.
### 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.
<pre>
%DBPERFORMANCE%
</pre>

+ 1
- 0
docs/05_building.mkd View File

@@ -10,6 +10,7 @@ Additionally, [eclipse-cs](http://eclipse-cs.sourceforge.net), [FindBugs](http:/
### Build Dependencies (downloaded during build)
- [H2 Database](http://h2database.com) (Eclipse Public License 1.0)
- [HSQL Database Engine](http://hsqldb.org) (BSD)
- [Apache Derby Database](http://db.apache.org/derby) (Apache 2.0)
- [JUnit](http://junit.org) (Common Public License)
- [commons-net](http://commons.apache.org/net) (Apache 2.0)
- [ant-googlecode](http://code.google.com/p/ant-googlecode) (New BSD)

+ 9
- 2
docs/05_releases.mkd View File

@@ -6,10 +6,17 @@
**%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) &nbsp; *released %BUILDDATE%*
- fixed failure of db.delete(PrimitiveModel) and db.update(PrimitiveModel)
- Disabled 2 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** &nbsp; *released 2011-08-12*
- fixed failure of db.delete(PrimitiveModel) and db.update(PrimitiveModel)
**0.6.4** &nbsp; *released 2011-08-12*
- api change release (API v4)
@@ -28,7 +35,7 @@
- added HSQL dialect. HSQL fails 4 out of 50 unit tests.
- 2 failures are unimplemented merge
- 1 has been filed and accepted as a [bug in HSQL](https://sourceforge.net/tracker/?func=detail&aid=3390047&group_id=23316&atid=378131)
- 1 is a concurrency issue, but the test may not be flawed
- 1 is a concurrency issue, but the test may be flawed
- added untested MySQL dialect
- renamed <b>_ iq_versions</b> table to *iq_versions* since leading _ character is troublesome for some databases.
- @IQColumn(allowNull=true) -> @IQColumn(nullable=true)

+ 2
- 2
src/com/iciql/Constants.java View File

@@ -25,11 +25,11 @@ public class Constants {
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
public static final String VERSION = "0.6.5";
public static final String VERSION = "0.6.6-SNAPSHOT";
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
public static final String VERSION_DATE = "2011-08-12";
public static final String VERSION_DATE = "PENDING";
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.

+ 20
- 2
src/com/iciql/Db.java View File

@@ -74,6 +74,7 @@ public class Db {
DIALECTS.put("H2", SQLDialectH2.class);
DIALECTS.put("MySQL", SQLDialectMySQL.class);
DIALECTS.put("HSQL Database Engine", SQLDialectHSQL.class);
DIALECTS.put("Apache Derby", SQLDialectDerby.class);
}
private Db(Connection conn) {
@@ -192,13 +193,30 @@ public class Db {
* does exist. Not all databases support MERGE and the syntax varies with
* the database.
*
* If the database does not support a MERGE syntax the dialect can try to
* simulate a merge by implementing:
* <p>
* INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
* <p>
* iciql will check the affected row count returned by the internal merge
* method and if the affected row count = 0, it will issue an update.
* <p>
* See the Derby dialect for an implementation of this technique.
* <p>
* If the dialect does not support merge an IciqlException will be thrown.
*
* @param t
*/
public <T> void merge(T t) {
Class<?> clazz = t.getClass();
define(clazz).createTableIfRequired(this).merge(this, t);
TableDefinition<?> def = define(clazz).createTableIfRequired(this);
int rc = def.merge(this, t);
if (rc == 0) {
rc = def.update(this, t);
}
if (rc == 0) {
throw new IciqlException("merge failed");
}
}
public <T> int update(T t) {
@@ -220,7 +238,7 @@ public class Db {
@SuppressWarnings("unchecked")
public <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {
List<T> result = new ArrayList<T>();
TableDefinition<T> def = (TableDefinition<T>) define(modelClass).createTableIfRequired(this);
TableDefinition<T> def = (TableDefinition<T>) define(modelClass);
try {
while (rs.next()) {
T item = Utils.newObject(modelClass);

+ 7
- 4
src/com/iciql/DbInspector.java View File

@@ -42,6 +42,7 @@ public class DbInspector {

public DbInspector(Db db) {
this.db = db;
setPreferredDateTimeClass(db.getDialect().getDateTimeClass());
}

/**
@@ -73,8 +74,8 @@ public class DbInspector {
* (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);
@@ -130,7 +131,8 @@ public class DbInspector {
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);
@@ -167,7 +169,8 @@ public class DbInspector {
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));
}
}
}

+ 4
- 3
src/com/iciql/Iciql.java View File

@@ -72,7 +72,8 @@ import java.lang.annotation.Target;
* </tr>
* <tr>
* <td>java.math.BigDecimal</td>
* <td>DECIMAL (length == 0)<br/>DECIMAL(length, scale) (length > 0)</td>
* <td>DECIMAL (length == 0)<br/>
* DECIMAL(length, scale) (length > 0)</td>
* </tr>
* <tr>
* <td>java.sql.Date</td>
@@ -400,8 +401,8 @@ public interface Iciql {
* Scale is used during the CREATE TABLE phase to define the scale of a
* DECIMAL(precision, scale) expression.
* <p>
* Any scale set in define() may override this annotation setting if
* the model class is not annotated with IQTable. Default: 0.
* Any scale set in define() may override this annotation setting if the
* model class is not annotated with IQTable. Default: 0.
*/
int scale() default 0;

+ 32
- 21
src/com/iciql/IciqlException.java View File

@@ -28,9 +28,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_INDEX_ALREADY_EXISTS = 4;
public static final int CODE_TABLE_ALREADY_EXISTS = 4;
public static final int CODE_INDEX_ALREADY_EXISTS = 5;
private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?";
private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?";
private static final long serialVersionUID = 1L;
@@ -51,7 +52,7 @@ public class IciqlException extends RuntimeException {
super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message, t);
configureCode(t);
}
public static void checkUnmappedField(String sql) {
if (Pattern.compile(IciqlException.TOKEN_UNMAPPED_FIELD).matcher(sql).find()) {
IciqlException e = new IciqlException("unmapped field in statement!");
@@ -60,7 +61,7 @@ public class IciqlException extends RuntimeException {
throw e;
}
}
public static IciqlException fromSQL(String sql, Throwable t) {
if (Pattern.compile(TOKEN_UNMAPPED_FIELD).matcher(sql).find()) {
IciqlException e = new IciqlException(t, "unmapped field in statement!");
@@ -73,7 +74,7 @@ public class IciqlException extends RuntimeException {
return e;
}
}
public void setSQL(String sql) {
this.sql = sql;
}
@@ -85,7 +86,7 @@ public class IciqlException extends RuntimeException {
public int getIciqlCode() {
return iciqlCode;
}
private void configureCode(Throwable t) {
if (t == null) {
return;
@@ -96,29 +97,39 @@ public class IciqlException extends RuntimeException {
String state = s.getSQLState();
if ("23505".equals(state)) {
iciqlCode = CODE_DUPLICATE_KEY;
} else if ("42501".equals(state)) {
} else if ("42X05".equals(state)) {
// Derby
iciqlCode = CODE_TABLE_NOT_FOUND;
} else if ("42S02".equals(state)) {
// H2
iciqlCode = CODE_TABLE_NOT_FOUND;
} else if ("42504".equals(state)) {
iciqlCode = CODE_INDEX_ALREADY_EXISTS;
} else if ("42501".equals(state)) {
// HSQL
iciqlCode = CODE_TABLE_NOT_FOUND;
} else if ("X0Y32".equals(state)) {
// Derby
iciqlCode = CODE_TABLE_ALREADY_EXISTS;
} else if ("42S11".equals(state)) {
// H2
iciqlCode = CODE_INDEX_ALREADY_EXISTS;
} else if ("42504".equals(state)) {
// HSQL
iciqlCode = CODE_INDEX_ALREADY_EXISTS;
}
}
}
@Override
public String toString() {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName());
String message = getLocalizedMessage();
if (message != null) {
sb.append(": ").append(message);
}
if (sql != null) {
sb.append('\n').append(sql);
}
return sb.toString();
}
sb.append(getClass().getName());
String message = getLocalizedMessage();
if (message != null) {
sb.append(": ").append(message);
}
if (sql != null) {
sb.append('\n').append(sql);
}
return sb.toString();
}
}

+ 4
- 8
src/com/iciql/ModelUtils.java View File

@@ -253,7 +253,10 @@ class ModelUtils {
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);
@@ -266,9 +269,6 @@ class ModelUtils {
} else if (String.class.isAssignableFrom(objectClass)) {
// STRING
value = o.toString();
} else if (Boolean.class.isAssignableFrom(objectClass)) {
// BOOLEAN
value = o.toString();
}
if (value == null) {
return "''";
@@ -313,10 +313,6 @@ class ModelUtils {
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()) {

+ 6
- 11
src/com/iciql/Query.java View File

@@ -193,7 +193,7 @@ public class Query<T> {
public UpdateColumnIncrement<T, Byte> increment(byte field) {
return incrementPrimitive(field);
}
public UpdateColumnIncrement<T, Short> increment(short field) {
return incrementPrimitive(field);
}
@@ -511,13 +511,13 @@ public class Query<T> {
}
return orderBy(alias);
}
public Query<T> orderBy(Object expr) {
OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
addOrderBy(e);
return this;
}
/**
* Order by a number of columns.
*
@@ -575,12 +575,12 @@ public class Query<T> {
}
return groupBy(alias);
}
public Query<T> groupBy(Object expr) {
groupByExpressions.add(expr);
return this;
}
public Query<T> groupBy(Object... groupBy) {
this.groupByExpressions.addAll(Arrays.asList(groupBy));
return this;
@@ -712,12 +712,7 @@ public class Query<T> {
stat.appendSQL(" ");
}
}
if (limit > 0) {
db.getDialect().appendLimit(stat, limit);
}
if (offset > 0) {
db.getDialect().appendOffset(stat, offset);
}
db.getDialect().appendLimitOffset(stat, limit, offset);
StatementLogger.select(stat.getSQL());
}

+ 1
- 0
src/com/iciql/QueryBetween.java View File

@@ -18,6 +18,7 @@ package com.iciql;
/**
* This class represents a "between y and z" condition.
*
* @param <T>
* the return type of the query
* @param <A>

+ 1
- 0
src/com/iciql/QueryWhere.java View File

@@ -344,6 +344,7 @@ public class QueryWhere<T> {
query.orderBy(field);
return this;
}
/**
* Order by a number of Object columns.
*

+ 19
- 12
src/com/iciql/SQLDialect.java View File

@@ -62,7 +62,7 @@ public interface SQLDialect {
* @param def
*/
<T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def);
/**
* Get the CREATE INDEX statement.
*
@@ -90,24 +90,16 @@ public interface SQLDialect {
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.
@@ -115,7 +107,14 @@ public interface SQLDialect {
* @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.
*
@@ -123,4 +122,12 @@ public interface SQLDialect {
*/
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();
}

+ 23
- 9
src/com/iciql/SQLDialectDefault.java View File

@@ -61,11 +61,21 @@ public class SQLDialectDefault implements SQLDialect {
return sqlType;
}
@Override
public Class<? extends java.util.Date> getDateTimeClass() {
return java.util.Date.class;
}
@Override
public boolean supportsMemoryTables() {
return false;
}
@Override
public boolean supportsIfNotExists() {
return true;
}
@Override
public boolean supportsLimitOffset() {
return true;
@@ -88,9 +98,13 @@ public class SQLDialectDefault implements SQLDialect {
public <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def) {
StatementBuilder buff;
if (def.memoryTable && supportsMemoryTables()) {
buff = new StatementBuilder("CREATE MEMORY TABLE IF NOT EXISTS ");
buff = new StatementBuilder("CREATE MEMORY TABLE ");
} else {
buff = new StatementBuilder("CREATE TABLE IF NOT EXISTS ");
buff = new StatementBuilder("CREATE TABLE ");
}
if (supportsIfNotExists()) {
buff.append("IF NOT EXISTS ");
}
buff.append(prepareTableName(def.schemaName, def.tableName)).append('(');
@@ -183,12 +197,12 @@ public class SQLDialectDefault implements SQLDialect {
}
@Override
public void appendLimit(SQLStatement stat, long limit) {
stat.appendSQL(" LIMIT " + limit);
}
@Override
public void appendOffset(SQLStatement stat, long offset) {
stat.appendSQL(" OFFSET " + offset);
public void appendLimitOffset(SQLStatement stat, long limit, long offset) {
if (limit > 0) {
stat.appendSQL(" LIMIT " + limit);
}
if (offset > 0) {
stat.appendSQL(" OFFSET " + offset);
}
}
}

+ 151
- 0
src/com/iciql/SQLDialectDerby.java View File

@@ -0,0 +1,151 @@
/*
* 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;
import java.text.MessageFormat;
import com.iciql.TableDefinition.FieldDefinition;
import com.iciql.TableDefinition.IndexDefinition;
import com.iciql.util.StatementBuilder;
/**
* Derby database dialect.
*/
public class SQLDialectDerby extends SQLDialectDefault {
@Override
public Class<? extends java.util.Date> getDateTimeClass() {
return java.sql.Timestamp.class;
}
@Override
protected String convertSqlType(String sqlType) {
if ("TINYINT".equals(sqlType)) {
// Derby does not have a TINYINT/BYTE type
return "SMALLINT";
}
return sqlType;
}
@Override
public boolean supportsMemoryTables() {
return false;
}
@Override
public boolean supportsIfNotExists() {
return false;
}
@Override
public boolean supportsLimitOffset() {
// FETCH/OFFSET added in 10.5
return databaseVersion >= 10.5f;
}
@Override
public void appendLimitOffset(SQLStatement stat, long limit, long offset) {
if (offset > 0) {
stat.appendSQL(" OFFSET " + offset + (offset == 1 ? " ROW" : " ROWS"));
}
if (limit > 0) {
stat.appendSQL(" FETCH NEXT " + limit + (limit == 1 ? " ROW" : " ROWS") + " ONLY");
}
}
@Override
public String prepareColumnName(String name) {
return name;
}
@Override
protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement,
boolean isPrimaryKey) {
if (isAutoIncrement) {
buff.append(" GENERATED BY DEFAULT AS IDENTITY");
}
return false;
}
@Override
public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) {
StatementBuilder buff = new StatementBuilder();
buff.append("CREATE ");
switch (index.type) {
case UNIQUE:
buff.append("UNIQUE ");
break;
case UNIQUE_HASH:
buff.append("UNIQUE ");
break;
}
buff.append("INDEX ");
buff.append(index.indexName);
buff.append(" ON ");
buff.append(table);
buff.append("(");
for (String col : index.columnNames) {
buff.appendExceptFirst(", ");
buff.append(prepareColumnName(col));
}
buff.append(") ");
stat.setSQL(buff.toString().trim());
}
/**
* Derby does not support the SQL2003 MERGE syntax, but we can use a trick
* to insert a row if it does not exist and call update() in Db.merge() if
* the affected row count is 0;
*
* http://stackoverflow.com/questions/407688
*/
@Override
public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
TableDefinition<T> def, Object obj) {
StatementBuilder buff = new StatementBuilder("INSERT INTO ");
buff.append(prepareTableName(schemaName, tableName));
buff.append(" (");
buff.resetCount();
for (FieldDefinition field : def.fields) {
buff.appendExceptFirst(", ");
buff.append(prepareColumnName(field.columnName));
}
buff.append(") (SELECT ");
buff.resetCount();
for (FieldDefinition field : def.fields) {
buff.appendExceptFirst(", ");
buff.append('?');
Object value = def.getValue(obj, field);
stat.addParameter(value);
}
buff.append(" FROM ");
buff.append(prepareTableName(schemaName, tableName));
buff.append(" WHERE ");
buff.resetCount();
for (FieldDefinition field : def.fields) {
if (field.isPrimaryKey) {
buff.appendExceptFirst(" AND ");
buff.append(MessageFormat.format("{0} = ?", prepareColumnName(field.columnName)));
Object value = def.getValue(obj, field);
stat.addParameter(value);
}
}
buff.append(" HAVING count(*)=0)");
stat.setSQL(buff.toString());
}
}

+ 4
- 3
src/com/iciql/SQLDialectH2.java View File

@@ -57,11 +57,12 @@ public class SQLDialectH2 extends SQLDialectDefault {
buff.append(col);
}
buff.append(")");
stat.setSQL(buff.toString());
stat.setSQL(buff.toString());
}
@Override
public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition<T> def, Object obj) {
public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
TableDefinition<T> def, Object obj) {
StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(prepareTableName(schemaName, tableName)).append(" (");
buff.resetCount();

+ 94
- 5
src/com/iciql/SQLDialectHSQL.java View File

@@ -16,6 +16,9 @@
package com.iciql;
import java.text.MessageFormat;
import com.iciql.TableDefinition.FieldDefinition;
import com.iciql.TableDefinition.IndexDefinition;
import com.iciql.util.StatementBuilder;
@@ -23,14 +26,15 @@ import com.iciql.util.StatementBuilder;
* HyperSQL database dialect.
*/
public class SQLDialectHSQL extends SQLDialectDefault {
@Override
public boolean supportsMemoryTables() {
return true;
}
@Override
protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement, boolean isPrimaryKey) {
protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement,
boolean isPrimaryKey) {
boolean isIdentity = false;
if (isAutoIncrement && isPrimaryKey) {
buff.append(" IDENTITY");
@@ -44,8 +48,6 @@ public class SQLDialectHSQL extends SQLDialectDefault {
StatementBuilder buff = new StatementBuilder();
buff.append("CREATE ");
switch (index.type) {
case STANDARD:
break;
case UNIQUE:
buff.append("UNIQUE ");
break;
@@ -65,4 +67,91 @@ public class SQLDialectHSQL extends SQLDialectDefault {
buff.append(")");
stat.setSQL(buff.toString());
}
@Override
public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
TableDefinition<T> def, Object obj) {
final String valuePrefix = "v";
StatementBuilder buff = new StatementBuilder("MERGE INTO ");
buff.append(prepareTableName(schemaName, tableName));
// a, b, c....
buff.append(" USING (VALUES(");
for (FieldDefinition field : def.fields) {
buff.appendExceptFirst(", ");
buff.append("CAST(? AS ");
String dataType = convertSqlType(field.dataType);
buff.append(dataType);
if ("VARCHAR".equals(dataType)) {
if (field.length > 0) {
// VARCHAR(x)
buff.append(MessageFormat.format("({0})", field.length));
}
} else if ("DECIMAL".equals(dataType)) {
if (field.length > 0) {
if (field.scale > 0) {
// DECIMAL(x,y)
buff.append(MessageFormat.format("({0},{1})", field.length, field.scale));
} else {
// DECIMAL(x)
buff.append(MessageFormat.format("({0})", field.length));
}
}
}
buff.append(')');
Object value = def.getValue(obj, field);
stat.addParameter(value);
}
// map to temporary table
buff.resetCount();
buff.append(")) AS vals (");
for (FieldDefinition field : def.fields) {
buff.appendExceptFirst(", ");
buff.append(prepareColumnName(valuePrefix + field.columnName));
}
buff.append(") ON ");
// create the ON condition
// (va, vb) = (va,vb)
String[] prefixes = { "", valuePrefix };
for (int i = 0; i < prefixes.length; i++) {
String prefix = prefixes[i];
buff.resetCount();
buff.append('(');
for (FieldDefinition field : def.fields) {
if (field.isPrimaryKey) {
buff.appendExceptFirst(", ");
buff.append(prepareColumnName(prefix + field.columnName));
}
}
buff.append(")");
if (i == 0) {
buff.append('=');
}
}
// UPDATE
// set a=va
buff.append(" WHEN MATCHED THEN UPDATE SET ");
buff.resetCount();
for (FieldDefinition field : def.fields) {
buff.appendExceptFirst(", ");
buff.append(prepareColumnName(field.columnName));
buff.append('=');
buff.append(prepareColumnName(valuePrefix + field.columnName));
}
// INSERT
// insert va, vb, vc....
buff.append(" WHEN NOT MATCHED THEN INSERT ");
buff.resetCount();
buff.append(" VALUES (");
for (FieldDefinition field : def.fields) {
buff.appendExceptFirst(", ");
buff.append(prepareColumnName(valuePrefix + field.columnName));
}
buff.append(')');
stat.setSQL(buff.toString());
}
}

+ 6
- 5
src/com/iciql/SQLDialectMySQL.java View File

@@ -31,7 +31,7 @@ public class SQLDialectMySQL extends SQLDialectDefault {
}
return sqlType;
}
@Override
public boolean supportsMemoryTables() {
return false;
@@ -41,15 +41,16 @@ public class SQLDialectMySQL extends SQLDialectDefault {
public String prepareColumnName(String name) {
return "`" + name + "`";
}
@Override
protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement, boolean isPrimaryKey) {
protected boolean prepareColumnDefinition(StatementBuilder buff, boolean isAutoIncrement,
boolean isPrimaryKey) {
if (isAutoIncrement) {
buff.append(" AUTO_INCREMENT");
}
return false;
}
@Override
public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) {
StatementBuilder buff = new StatementBuilder();
@@ -74,7 +75,7 @@ public class SQLDialectMySQL extends SQLDialectDefault {
buff.append(prepareColumnName(col));
}
buff.append(") ");
// USING
switch (index.type) {
case HASH:

+ 8
- 1
src/com/iciql/SQLStatement.java View File

@@ -65,6 +65,12 @@ public class SQLStatement {
}
public SQLStatement addParameter(Object o) {
// Automatically convert java.util.Date to java.sql.Timestamp
// if the dialect requires java.sql.Timestamp objects (e.g. Derby)
if (o != null && o.getClass().equals(java.util.Date.class)
&& db.getDialect().getDateTimeClass().equals(java.sql.Timestamp.class)) {
o = new java.sql.Timestamp(((java.util.Date) o).getTime());
}
params.add(o);
return this;
}
@@ -112,7 +118,8 @@ public class SQLStatement {
try {
prep.setObject(parameterIndex, x);
} catch (SQLException e) {
IciqlException ix = new IciqlException(e, "error setting parameter {0} as {1}", parameterIndex, x.getClass().getSimpleName());
IciqlException ix = new IciqlException(e, "error setting parameter {0} as {1}", parameterIndex, x
.getClass().getSimpleName());
ix.setSQL(getSQL());
throw ix;
}

+ 19
- 8
src/com/iciql/TableDefinition.java View File

@@ -447,30 +447,35 @@ public class TableDefinition<T> {
}
private boolean skipInsertField(FieldDefinition field, Object obj) {
// skip uninitialized primitive autoincrement values
if (field.isAutoIncrement && field.isPrimitive) {
if (field.isAutoIncrement) {
Object value = getValue(obj, field);
if (value.toString().equals("0")) {
if (field.isPrimitive) {
// skip uninitialized primitive autoincrement values
if (value.toString().equals("0")) {
return true;
}
} else if (value == null) {
// skip null object autoincrement values
return true;
}
}
return false;
}
void merge(Db db, Object obj) {
int merge(Db db, Object obj) {
if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
throw new IllegalStateException("No primary key columns defined " + "for table " + obj.getClass()
throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
+ " - no update possible");
}
SQLStatement stat = new SQLStatement(db);
db.getDialect().prepareMerge(stat, schemaName, tableName, this, obj);
StatementLogger.merge(stat.getSQL());
stat.executeUpdate();
return stat.executeUpdate();
}
int update(Db db, Object obj) {
if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
throw new IllegalStateException("No primary key columns defined " + "for table " + obj.getClass()
throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
+ " - no update possible");
}
SQLStatement stat = new SQLStatement(db);
@@ -552,7 +557,13 @@ public class TableDefinition<T> {
SQLStatement stat = new SQLStatement(db);
db.getDialect().prepareCreateTable(stat, this);
StatementLogger.create(stat.getSQL());
stat.executeUpdate();
try {
stat.executeUpdate();
} catch (IciqlException e) {
if (e.getIciqlCode() != IciqlException.CODE_TABLE_ALREADY_EXISTS) {
throw e;
}
}
// create indexes
for (IndexDefinition index : indexes) {

+ 2
- 1
src/com/iciql/TableInspector.java View File

@@ -120,7 +120,8 @@ public class TableInspector {
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;
}

+ 0
- 1
src/com/iciql/UpdateColumnSet.java View File

@@ -17,7 +17,6 @@

package com.iciql;


/**
* This class represents "SET column = value" in an UPDATE statement.
*

+ 8
- 5
src/com/iciql/build/Build.java View File

@@ -57,6 +57,7 @@ public class Build {
downloadFromApache(MavenObject.H2, BuildType.RUNTIME);
downloadFromApache(MavenObject.H2, BuildType.COMPILETIME);
downloadFromApache(MavenObject.HSQLDB, BuildType.RUNTIME);
downloadFromApache(MavenObject.DERBY, BuildType.RUNTIME);
downloadFromApache(MavenObject.JCOMMANDER, BuildType.RUNTIME);
downloadFromApache(MavenObject.JCOMMANDER, BuildType.COMPILETIME);
downloadFromApache(MavenObject.MARKDOWNPAPERS, BuildType.RUNTIME);
@@ -169,13 +170,15 @@ public class Build {
"219a3540f3b27d7cc3b1d91d6ea046cd8723290e", "0bb50eec177acf0e94d58e0cf07262fe5164331d",
"c7adc475ca40c288c93054e0f4fe58f3a98c0cb5");
public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.158",
"4bac13427caeb32ef6e93b70101e61f370c7b5e2", "6bb165156a0831879fa7797df6e18bdcd4421f2d",
"446d3f58c44992534cb54f67134532d95961904a");
public static final MavenObject H2 = new MavenObject("com/h2database", "h2", "1.3.159",
"dd89f939661eb5593909584e1c243db0c25de130", "4d953bf765e8a13c7e06ca51165438338966c698",
"4c79ed03f994820a1a873150c8a9f13c667784d3");
public static final MavenObject HSQLDB = new MavenObject("org/hsqldb", "hsqldb", "2.2.4",
"6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "",
"");
"6a6e040b07f5ee409fc825f1c5e5b574b1fa1428", "", "");
public static final MavenObject DERBY = new MavenObject("org/apache/derby", "derby", "10.8.1.2",
"2f8717d96eafe3eef3de445ba653f142d54ddab1", "", "");
public static final MavenObject JUNIT = new MavenObject("junit", "junit", "4.8.2",
"c94f54227b08100974c36170dcb53329435fe5ad", "", "");

+ 17
- 6
src/com/iciql/build/BuildSite.java View File

@@ -87,8 +87,8 @@ public class BuildSite {
aliasMap.put(values[0], values[1]);
}
System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ", markdownFiles.length,
sourceFolder.getAbsolutePath()));
System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ",
markdownFiles.length, sourceFolder.getAbsolutePath()));
String linkPattern = "<a href=''{0}''>{1}</a>";
StringBuilder sb = new StringBuilder();
for (File file : markdownFiles) {
@@ -168,7 +168,8 @@ public class BuildSite {
// get remainder of text
if (endCode < markdownContent.length()) {
strippedContent.append(markdownContent.substring(endCode, markdownContent.length()));
strippedContent.append(markdownContent.substring(endCode,
markdownContent.length()));
}
markdownContent = strippedContent.toString();
nmd++;
@@ -193,9 +194,16 @@ public class BuildSite {
String[] kv = token.split("!!!", 2);
content = content.replaceAll(kv[0], kv[1]);
}
for (String alias : params.loads) {
String[] kv = alias.split("=", 2);
String loadedContent = StringUtils.readContent(new File(kv[1]), "\n");
loadedContent = StringUtils.escapeForHtml(loadedContent, false);
loadedContent = StringUtils.breakLinesForHtml(loadedContent);
content = content.replace(kv[0], loadedContent);
}
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(destinationFolder,
fileName)), Charset.forName("UTF-8"));
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(
destinationFolder, fileName)), Charset.forName("UTF-8"));
writer.write(header);
if (!StringUtils.isNullOrEmpty(htmlAdSnippet)) {
writer.write(htmlAdSnippet);
@@ -317,9 +325,12 @@ public class BuildSite {
@Parameter(names = { "--substitute" }, description = "%TOKEN%=value", required = false)
public List<String> substitutions = new ArrayList<String>();
@Parameter(names = { "--load" }, description = "%TOKEN%=filename", required = false)
public List<String> loads = new ArrayList<String>();
@Parameter(names = { "--nomarkdown" }, description = "%STARTTOKEN%:%ENDTOKEN%", required = false)
public List<String> nomarkdown = new ArrayList<String>();
@Parameter(names = { "--regex" }, description = "searchPattern!!!replacePattern", required = false)
public List<String> regex = new ArrayList<String>();

+ 10
- 7
src/com/iciql/bytecode/ClassReader.java View File

@@ -65,7 +65,8 @@ public class ClassReader {
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) {
@@ -108,7 +109,8 @@ public class ClassReader {
}
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;
}
@@ -1380,8 +1382,8 @@ public class ClassReader {
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;
}

@@ -1389,8 +1391,8 @@ public class ClassReader {
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;
}

@@ -1444,7 +1446,8 @@ public class ClassReader {

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() {

+ 4
- 2
src/com/iciql/util/GenerateModels.java View File

@@ -128,13 +128,15 @@ public class GenerateModels {
* 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"));

+ 1
- 1
src/com/iciql/util/JdbcUtils.java View File

@@ -206,7 +206,7 @@ public class JdbcUtils {
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 {

+ 3
- 3
src/com/iciql/util/Slf4jStatementListener.java View File

@@ -60,7 +60,7 @@ public class Slf4jStatementListener implements StatementListener {
/**
* Sets the logging level for a particular statement type.
*
*
* @param type
* @param level
*/
@@ -84,10 +84,10 @@ public class Slf4jStatementListener implements StatementListener {
case DEBUG:
logger.debug(statement);
break;
case TRACE:
case TRACE:
logger.trace(statement);
break;
case OFF:
case OFF:
break;
}
}

+ 2
- 1
src/com/iciql/util/StatementBuilder.java View File

@@ -99,9 +99,10 @@ public class StatementBuilder {
builder.append(x);
return this;
}
/**
* Returns the current value of the loop counter.
*
* @return the loop counter
*/
public int getCount() {

+ 74
- 0
src/com/iciql/util/StringUtils.java View File

@@ -17,7 +17,12 @@

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;
@@ -305,4 +310,73 @@ public class StringUtils {
}
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("&amp;");
} else if (inStr.charAt(i) == '<') {
retStr.append("&lt;");
} else if (inStr.charAt(i) == '>') {
retStr.append("&gt;");
} else if (inStr.charAt(i) == '\"') {
retStr.append("&quot;");
} else if (changeSpace && inStr.charAt(i) == ' ') {
retStr.append("&nbsp;");
} else if (changeSpace && inStr.charAt(i) == '\t') {
retStr.append(" &nbsp; &nbsp;");
} 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();
}
}

+ 7
- 3
src/com/iciql/util/Utils.java View File

@@ -116,7 +116,9 @@ public class Utils {
}
}
}
throw new IciqlException(e, "Missing default constructor? Exception trying to instantiate {0}: {1}", clazz.getName(), e.getMessage());
throw new IciqlException(e,
"Missing default constructor? Exception trying to instantiate {0}: {1}",
clazz.getName(), e.getMessage());
}
}
};
@@ -194,7 +196,9 @@ public class Utils {
}
}
}
throw new IciqlException(e, "Missing default constructor?! Exception trying to instantiate {0}: {1}", clazz.getName(), e.getMessage());
throw new IciqlException(e,
"Missing default constructor?! Exception trying to instantiate {0}: {1}",
clazz.getName(), e.getMessage());
}
}
@@ -242,7 +246,7 @@ public class Utils {
float f = 0f;
try {
f = Float.parseFloat(s);
} catch (Exception e) {
} catch (Exception e) {
}
return f > 0 || s.equals("true") || s.equals("yes");
}

+ 2
- 2
tests/com/iciql/test/AliasMapTest.java View File

@@ -42,7 +42,7 @@ public class AliasMapTest {
*/
@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
@@ -91,7 +91,7 @@ public class AliasMapTest {
*/
@Test
public void testPrimitiveAliasMapping() throws Exception {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
PrimitivesModel model = new PrimitivesModel();
model.myLong = 100L;
db.insert(model);

+ 11
- 3
tests/com/iciql/test/AnnotationsTest.java View File

@@ -53,7 +53,7 @@ public class AnnotationsTest {

@Before
public void setUp() {
db = IciqlSuite.openDb();
db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(ProductAnnotationOnly.getList());
db.insertAll(ProductMixedAnnotation.getList());
@@ -69,7 +69,15 @@ 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");
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());
@@ -104,7 +112,7 @@ public class AnnotationsTest {
} 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());


+ 2
- 2
tests/com/iciql/test/BooleanModelTest.java View File

@@ -39,7 +39,7 @@ public class BooleanModelTest {
@Test
public void testBooleanColumn() {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
db.insertAll(BooleanModel.getList());
BooleanAsIntModel b = new BooleanAsIntModel();
List<BooleanAsIntModel> models = db.from(b).select();
@@ -82,7 +82,7 @@ public class BooleanModelTest {
@Test
public void testIntColumn() {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
// insert INT column
db.insertAll(BooleanAsIntModel.getList());

+ 4
- 3
tests/com/iciql/test/ClobTest.java View File

@@ -38,13 +38,13 @@ public class ClobTest {
@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");
@@ -95,7 +95,8 @@ public class ClobTest {
}

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."),

+ 70
- 64
tests/com/iciql/test/ConcurrencyTest.java View File

@@ -22,8 +22,8 @@ import static org.junit.Assert.assertTrue;
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;
@@ -37,43 +37,43 @@ import com.iciql.util.Utils;
*/
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.
//
@@ -114,6 +114,7 @@ public class ConcurrencyTest {
}

@Test
@Ignore
public void testConcurrencyThreadLocal() throws Exception {
List<Thread> threads = Utils.newArrayList();
final AtomicInteger failures = new AtomicInteger(0);
@@ -149,45 +150,50 @@ public class ConcurrencyTest {
}

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();
}
}
}

+ 1
- 1
tests/com/iciql/test/DefaultValuesTest.java View File

@@ -34,7 +34,7 @@ public class DefaultValuesTest {
@Test
public void testDefaultObjectValues() {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
// insert random model
DefaultValuesModel model = new DefaultValuesModel();

+ 1
- 1
tests/com/iciql/test/EnumsTest.java View File

@@ -40,7 +40,7 @@ public class EnumsTest {
@Before
public void setUp() {
db = IciqlSuite.openDb();
db = IciqlSuite.openNewDb();
db.insertAll(EnumIdModel.createList());
db.insertAll(EnumOrdinalModel.createList());
db.insertAll(EnumStringModel.createList());

+ 220
- 23
tests/com/iciql/test/IciqlSuite.java View File

@@ -15,8 +15,13 @@
*/
package com.iciql.test;
import java.io.PrintStream;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
@@ -27,7 +32,13 @@ import org.junit.runner.notification.Failure;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.iciql.Constants;
import com.iciql.Db;
import com.iciql.util.StringUtils;
/**
* JUnit 4 iciql test suite.
@@ -45,29 +56,62 @@ import com.iciql.Db;
RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UUIDTest.class })
public class IciqlSuite {
private static final TestDb[] TEST_DBS = {
new TestDb("H2", "jdbc:h2:mem:"),
new TestDb("HSQL", "jdbc:hsqldb:mem:db{0,number,000}") };
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 DEFAULT_TEST_DB = TEST_DBS[0];
private static final PrintStream ERR = System.err;
private static AtomicInteger openCount = new AtomicInteger(0);
private static String username = "sa";
private static String password = "sa";
private static PrintStream out = System.out;
public static void assertStartsWith(String value, String startsWith) {
Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", startsWith, value),
value.startsWith(startsWith));
}
public static Db openDb() {
/**
* Increment the database counter, open and create a new database.
*
* @return a fresh database
*/
public static Db openNewDb() {
String testUrl = System.getProperty("iciql.url");
if (testUrl == null) {
testUrl = DEFAULT_TEST_DB.url;
}
testUrl = MessageFormat.format(testUrl, openCount.incrementAndGet());
return Db.open(testUrl, "sa", "sa");
return Db.open(testUrl, username, password);
}
public static String getDatabaseName(Db db) {
/**
* Open the current database.
*
* @return the current database
*/
public static Db openCurrentDb() {
String testUrl = System.getProperty("iciql.url");
if (testUrl == null) {
testUrl = DEFAULT_TEST_DB.url;
}
testUrl = MessageFormat.format(testUrl, openCount.get());
return Db.open(testUrl, username, password);
}
/**
* Returns the name of the underlying database engine for the Db object.
*
* @param db
* @return the database engine name
*/
public static String getDatabaseEngineName(Db db) {
String database = "";
try {
database = db.getConnection().getMetaData().getDatabaseProductName();
@@ -76,23 +120,161 @@ public class IciqlSuite {
return database;
}
public static void main(String... args) {
/**
* Returns true if the underlying database engine is Derby.
*
* @param db
* @return true if underlying database engine is Derby
*/
public static boolean isDerby(Db db) {
return IciqlSuite.getDatabaseEngineName(db).equals("Apache Derby");
}
/**
* Returns true if the underlying database engine is H2.
*
* @param db
* @return true if underlying database engine is H2
*/
public static boolean isH2(Db db) {
return IciqlSuite.getDatabaseEngineName(db).equals("H2");
}
/**
* Gets the default schema of the underlying database engine.
*
* @param db
* @return the default schema
*/
public static String getDefaultSchema(Db db) {
if (isDerby(db)) {
// Derby sets default schema name to username
return username.toUpperCase();
}
return "PUBLIC";
}
/**
* Main entry point for the test suite. Executing this method will run the
* test suite on all registered databases.
*
* @param args
* @throws Exception
*/
public static void main(String... args) throws Exception {
Params params = new Params();
JCommander jc = new JCommander(params);
try {
jc.parse(args);
} catch (ParameterException t) {
usage(jc, t);
}
// Replace System.out with a file
if (!StringUtils.isNullOrEmpty(params.outputFile)) {
out = new PrintStream(params.outputFile);
System.setErr(out);
}
SuiteClasses suiteClasses = IciqlSuite.class.getAnnotation(SuiteClasses.class);
long quickestDatabase = Long.MAX_VALUE;
String dividerMajor = buildDivider('*', 70);
String dividerMinor = buildDivider('-', 70);
// Header
out.println(dividerMajor);
out.println(MessageFormat.format("{0} {1} ({2}) testing {3} databases", Constants.NAME,
Constants.VERSION, Constants.VERSION_DATE, TEST_DBS.length));
out.println(dividerMajor);
out.println();
showProperty("java.vendor");
showProperty("java.runtime.version");
showProperty("java.vm.name");
showProperty("os.name");
showProperty("os.version");
showProperty("os.arch");
out.println();
// Test a database
for (TestDb testDb : TEST_DBS) {
System.out.println("*********************************************");
System.out.println("Testing " + testDb.name + " " + testDb.getVersion());
System.out.println("*********************************************");
out.println(dividerMinor);
out.println("Testing " + testDb.name + " " + testDb.getVersion());
out.println(dividerMinor);
System.setProperty("iciql.url", testDb.url);
Result result = JUnitCore.runClasses(suiteClasses.value());
System.out.println(MessageFormat.format("{0} runs, {1} failures, {2} ignores in {3} msecs",
testDb.runtime = result.getRunTime();
if (testDb.runtime < quickestDatabase) {
quickestDatabase = testDb.runtime;
}
out.println(MessageFormat.format("{0} tests: {1} failures, {2} ignores in {3,number,0.000} secs",
result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(),
result.getRunTime()));
for (Failure failure : result.getFailures()) {
System.out.println(MessageFormat.format("{0}: {1}", failure.getTestHeader(),
failure.getMessage()));
result.getRunTime() / 1000f));
if (result.getFailureCount() == 0) {
out.println();
} else {
for (Failure failure : result.getFailures()) {
out.println(MessageFormat.format("\n + {0}\n {1}\n", failure.getTestHeader(),
failure.getMessage()));
}
}
}
// Display runtime results sorted by performance leader
out.println(dividerMajor);
out.println(MessageFormat.format("{0} {1} ({2}) test suite performance results", Constants.NAME,
Constants.VERSION, Constants.VERSION_DATE));
out.println(dividerMajor);
List<TestDb> dbs = Arrays.asList(TEST_DBS);
Collections.sort(dbs, new Comparator<TestDb>() {
@Override
public int compare(TestDb o1, TestDb o2) {
if (o1.runtime == o2.runtime) {
return 0;
}
if (o1.runtime > o2.runtime) {
return 1;
}
return -1;
}
});
for (TestDb testDb : dbs) {
out.println(MessageFormat.format("{0} {1} {2,number,0.000} secs ({3,number,#.0}x)",
StringUtils.pad(testDb.name, 6, " ", true),
StringUtils.pad(testDb.getVersion(), 22, " ", true), testDb.runtime / 1000f,
((double) testDb.runtime) / quickestDatabase));
}
// close PrintStream and restore System.err
out.close();
System.setErr(ERR);
}
private static void showProperty(String name) {
out.print(StringUtils.pad(name, 25, " ", true));
out.println(System.getProperty(name));
}
private static void usage(JCommander jc, ParameterException t) {
System.out.println(Constants.NAME + " test suite v" + Constants.VERSION);
System.out.println();
if (t != null) {
System.out.println(t.getMessage());
System.out.println();
}
if (jc != null) {
jc.usage();
}
System.exit(0);
}
private static String buildDivider(char c, int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(c);
}
return sb.toString();
}
/**
@@ -101,6 +283,8 @@ public class IciqlSuite {
private static class TestDb {
final String name;
final String url;
String version;
long runtime;
TestDb(String name, String url) {
this.name = name;
@@ -108,14 +292,27 @@ public class IciqlSuite {
}
String getVersion() {
try {
Db db = Db.open(url, "sa", "sa");
String version = db.getConnection().getMetaData().getDatabaseProductVersion();
db.close();
return version;
} catch (SQLException s) {
if (version == null) {
try {
Db db = Db.open(url, username, password);
version = db.getConnection().getMetaData().getDatabaseProductVersion();
db.close();
return version;
} catch (SQLException s) {
version = "";
}
}
return "";
return version;
}
}
}
/**
* Command-line parameters for TestSuite.
*/
@Parameters(separators = " ")
private static class Params {
@Parameter(names = { "--outputFile" }, description = "Results text file", required = false)
public String outputFile;
}
}

+ 17
- 9
tests/com/iciql/test/ModelsTest.java View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@@ -58,7 +59,7 @@ public class ModelsTest {

@Before
public void setUp() {
db = IciqlSuite.openDb();
db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(ProductAnnotationOnly.getList());
db.insertAll(ProductMixedAnnotation.getList());
@@ -71,14 +72,17 @@ public class ModelsTest {

@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();
@@ -91,7 +95,7 @@ public class ModelsTest {
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());
}

@@ -117,12 +121,16 @@ public class ModelsTest {
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());
}
}

@@ -143,7 +151,7 @@ public class ModelsTest {

@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();

+ 3
- 3
tests/com/iciql/test/PrimitivesTest.java View File

@@ -34,7 +34,7 @@ public class PrimitivesTest {
@Test
public void testPrimitives() {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
// insert random models in reverse order
List<PrimitivesModel> models = PrimitivesModel.getList();
@@ -74,11 +74,11 @@ public class PrimitivesTest {
List<PrimitivesModel> list = db.from(p).orderBy(p.myLong).select();
assertEquals(models.size(), list.size());
assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString());
// test model update
retrievedModel.myInteger = 1337;
assertEquals(1, db.update(retrievedModel));
assertEquals(1, db.delete(retrievedModel));
assertEquals(1, db.delete(retrievedModel));
db.close();
}

+ 8
- 7
tests/com/iciql/test/RuntimeQueryTest.java View File

@@ -34,7 +34,7 @@ public class RuntimeQueryTest {
@Test
public void testRuntimeQuery() {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
Product p = new Product();
@@ -50,14 +50,15 @@ public class RuntimeQueryTest {
@Test
public void testExecuteQuery() throws SQLException {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
// test plain statement
List<Product> products = db.executeQuery(Product.class, "select * from product where unitsInStock=120");
List<Product> products = db.executeQuery(Product.class,
"select * from product where unitsInStock=120");
assertEquals(1, products.size());
assertEquals("Condiments", products.get(0).category);
// test prepared statement
products = db.executeQuery(Product.class, "select * from product where unitsInStock=?", 120);
assertEquals(1, products.size());
@@ -65,10 +66,10 @@ public class RuntimeQueryTest {
db.close();
}
@Test
public void testBuildObjects() throws SQLException {
Db db = IciqlSuite.openDb();
Db db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
// test plain statement
@@ -78,7 +79,7 @@ public class RuntimeQueryTest {
assertEquals(1, products.size());
assertEquals("Condiments", products.get(0).category);
// test prepared statement
rs = db.executeQuery("select * from product where unitsInStock=?", 120);
products = db.buildObjects(Product.class, rs);

+ 2
- 2
tests/com/iciql/test/SamplesTest.java View File

@@ -60,7 +60,7 @@ public class SamplesTest {
@Before
public void setUp() {
db = IciqlSuite.openDb();
db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(Customer.getList());
db.insertAll(Order.getList());
@@ -278,7 +278,7 @@ public class SamplesTest {
@Test
public void testSum() {
Product p = new Product();
Long sum = db.from(p).selectFirst(sum(p.unitsInStock));
Number sum = db.from(p).selectFirst(sum(p.unitsInStock));
assertEquals(323, sum.intValue());
Double sumPrice = db.from(p).selectFirst(sum(p.unitPrice));
assertEquals(313.35, sumPrice.doubleValue(), 0.001);

+ 1
- 1
tests/com/iciql/test/UpdateTest.java View File

@@ -41,7 +41,7 @@ public class UpdateTest {

@Before
public void setUp() throws Exception {
db = IciqlSuite.openDb();
db = IciqlSuite.openNewDb();
db.insertAll(Product.getList());
db.insertAll(Customer.getList());
db.insertAll(Order.getList());

+ 2
- 0
tests/com/iciql/test/models/ComplexObject.java View File

@@ -17,6 +17,7 @@

package com.iciql.test.models;

import static com.iciql.Define.length;
import static com.iciql.Define.primaryKey;

import java.math.BigDecimal;
@@ -55,6 +56,7 @@ public class ComplexObject implements Iciql {

public void defineIQ() {
primaryKey(id);
length(name, 25);
}

public static List<ComplexObject> getList() {

+ 1
- 1
tests/com/iciql/test/models/Customer.java View File

@@ -31,7 +31,7 @@ public class Customer {
@IQColumn(length = 25)
public String customerId;
@IQColumn(length = 2)
public String region;

+ 3
- 3
tests/com/iciql/test/models/DefaultValuesModel.java View File

@@ -31,14 +31,14 @@ public class DefaultValuesModel {
@IQColumn(primaryKey = true, autoIncrement = true)
public Long myLong;
@SuppressWarnings("deprecation")
@IQColumn
public Date myDate = new Date(100, 7, 1);
@IQColumn
public Integer myInteger = 12345;
@IQColumn
public Tree myEnumIdTree = Tree.WALNUT;

+ 7
- 4
tests/com/iciql/test/models/Order.java View File

@@ -36,7 +36,7 @@ import com.iciql.Iciql;
public class Order implements Iciql {
public String customerId;
public Integer orderId;
public Date orderDate;
public Date orderDate;
public BigDecimal total;
public Order(String customerId, Integer orderId, String total, String orderDate) {
@@ -60,9 +60,12 @@ public class Order implements Iciql {
public static List<Order> getList() {
Order[] list = { new Order("ALFKI", 10702, "330.00", "2007-01-02"),
new Order("ALFKI", 10952, "471.20", "2007-02-03"), new Order("ANATR", 10308, "88.80", "2007-01-03"),
new Order("ANATR", 10625, "479.75", "2007-03-03"), new Order("ANATR", 10759, "320.00", "2007-04-01"),
new Order("ANTON", 10365, "403.20", "2007-02-13"), new Order("ANTON", 10682, "375.50", "2007-03-13"),
new Order("ALFKI", 10952, "471.20", "2007-02-03"),
new Order("ANATR", 10308, "88.80", "2007-01-03"),
new Order("ANATR", 10625, "479.75", "2007-03-03"),
new Order("ANATR", 10759, "320.00", "2007-04-01"),
new Order("ANTON", 10365, "403.20", "2007-02-13"),
new Order("ANTON", 10682, "375.50", "2007-03-13"),
new Order("ANTON", 10355, "480.00", "2007-04-11") };
return Arrays.asList(list);
}

+ 1
- 1
tests/com/iciql/test/models/PrimitivesModel.java View File

@@ -81,7 +81,7 @@ public class PrimitivesModel {
}
return list;
}
@Override
public String toString() {
return String.valueOf(myLong);

+ 4
- 2
tests/com/iciql/test/models/Product.java View File

@@ -59,7 +59,8 @@ public class Product implements Iciql {
index(productName, category);
}
private static Product create(int productId, String productName, String category, double unitPrice, int unitsInStock) {
private static Product create(int productId, String productName, String category, double unitPrice,
int unitsInStock) {
return new Product(productId, productName, category, unitPrice, unitsInStock);
}
@@ -71,7 +72,8 @@ public class Product implements Iciql {
create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120),
create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15),
create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6),
create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29), create(10, "Ikura", "Seafood", 31.0, 31), };
create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29),
create(10, "Ikura", "Seafood", 31.0, 31), };
return Arrays.asList(list);
}

+ 2
- 1
tests/com/iciql/test/models/ProductInheritedAnnotation.java View File

@@ -42,7 +42,8 @@ public class ProductInheritedAnnotation extends ProductMixedAnnotation {

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() {

+ 12
- 6
tests/com/iciql/test/models/ProductMixedAnnotation.java View File

@@ -20,7 +20,8 @@ package com.iciql.test.models;
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;

@@ -29,8 +30,8 @@ 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;
@@ -59,9 +60,10 @@ public class ProductMixedAnnotation {
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() {
@@ -91,4 +93,8 @@ public class ProductMixedAnnotation {
return productName;
}

@Override
public void defineIQ() {
Define.length(mappedField, 25);
}
}

+ 9
- 7
tests/com/iciql/test/models/SupportedTypes.java View File

@@ -124,13 +124,15 @@ public class SupportedTypes {

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());
@@ -144,10 +146,10 @@ public class SupportedTypes {
// 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;
@@ -181,7 +183,7 @@ public class SupportedTypes {
same &= myOtherFavoriteTree.equals(s.myOtherFavoriteTree);
return same;
}
/**
* This class demonstrates the table upgrade.
*/

Loading…
Cancel
Save