]> source.dussan.org Git - iciql.git/commitdiff
Added Derby dialect. Finished HSQL dialect. Documentation.
authorJames Moger <james.moger@gmail.com>
Mon, 15 Aug 2011 20:44:14 +0000 (16:44 -0400)
committerJames Moger <james.moger@gmail.com>
Mon, 15 Aug 2011 20:44:14 +0000 (16:44 -0400)
* 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.

61 files changed:
.classpath
.gitignore
NOTICE
README.markdown
build.xml
docs/00_index.mkd
docs/02_usage.mkd
docs/03_natural_syntax.mkd [deleted file]
docs/03_performance.mkd [new file with mode: 0644]
docs/05_building.mkd
docs/05_releases.mkd
src/com/iciql/Constants.java
src/com/iciql/Db.java
src/com/iciql/DbInspector.java
src/com/iciql/Iciql.java
src/com/iciql/IciqlException.java
src/com/iciql/ModelUtils.java
src/com/iciql/Query.java
src/com/iciql/QueryBetween.java
src/com/iciql/QueryWhere.java
src/com/iciql/SQLDialect.java
src/com/iciql/SQLDialectDefault.java
src/com/iciql/SQLDialectDerby.java [new file with mode: 0644]
src/com/iciql/SQLDialectH2.java
src/com/iciql/SQLDialectHSQL.java
src/com/iciql/SQLDialectMySQL.java
src/com/iciql/SQLStatement.java
src/com/iciql/TableDefinition.java
src/com/iciql/TableInspector.java
src/com/iciql/UpdateColumnSet.java
src/com/iciql/build/Build.java
src/com/iciql/build/BuildSite.java
src/com/iciql/bytecode/ClassReader.java
src/com/iciql/util/GenerateModels.java
src/com/iciql/util/JdbcUtils.java
src/com/iciql/util/Slf4jStatementListener.java
src/com/iciql/util/StatementBuilder.java
src/com/iciql/util/StringUtils.java
src/com/iciql/util/Utils.java
tests/com/iciql/test/AliasMapTest.java
tests/com/iciql/test/AnnotationsTest.java
tests/com/iciql/test/BooleanModelTest.java
tests/com/iciql/test/ClobTest.java
tests/com/iciql/test/ConcurrencyTest.java
tests/com/iciql/test/DefaultValuesTest.java
tests/com/iciql/test/EnumsTest.java
tests/com/iciql/test/IciqlSuite.java
tests/com/iciql/test/ModelsTest.java
tests/com/iciql/test/PrimitivesTest.java
tests/com/iciql/test/RuntimeQueryTest.java
tests/com/iciql/test/SamplesTest.java
tests/com/iciql/test/UpdateTest.java
tests/com/iciql/test/models/ComplexObject.java
tests/com/iciql/test/models/Customer.java
tests/com/iciql/test/models/DefaultValuesModel.java
tests/com/iciql/test/models/Order.java
tests/com/iciql/test/models/PrimitivesModel.java
tests/com/iciql/test/models/Product.java
tests/com/iciql/test/models/ProductInheritedAnnotation.java
tests/com/iciql/test/models/ProductMixedAnnotation.java
tests/com/iciql/test/models/SupportedTypes.java

index c52fc50efde38bba14c6972a4534850810f82147..1d41ba727df4704bf21b253082ffe039571aff43 100644 (file)
                        <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
index bed9c0532312a1b1b264322ae15718fb054b0453..fc3f51d198b2505b3b1efefa7fea08cb5c4f7a69 100644 (file)
@@ -6,3 +6,6 @@
 /*.zip
 /*.jar
 /build.properties
+/derby.log
+/performance.txt
+/performance_db.txt
diff --git a/NOTICE b/NOTICE
index 76b6a69e44f3d3abc6a2e9b9e5d2a4cd0f95b538..61f70f69efc91ca8dcc91cb66e736c9052b268ed 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -45,6 +45,14 @@ HSQL Database
    \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
index 26cc96f7ae6ab4e9deb4282f26006fd30626cf77..6c4f5e2292addd71a07d4fc4adf78e45f630108e 100644 (file)
@@ -5,7 +5,7 @@ iciql **is**...
 - 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
@@ -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)\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
index 9e54239a2fc27b2cb8242503c4bfa49ec9ff7a16..725f8b245848e2c7356e38c3fe7a00ed2ded11ae 100644 (file)
--- a/build.xml
+++ b/build.xml
                </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="&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;" />\r
 \r
+                       <arg value="--load" />\r
+                       <arg value="%DBPERFORMANCE%=${basedir}/performance_db.txt" />\r
+\r
                </java> \r
        </target>\r
 \r
index f86e396356990c5a33986a7ecf7ef371368381e7..b3e8cbfded5f3d27a5b2d081b310f381da4d314d 100644 (file)
@@ -5,7 +5,7 @@ iciql **is**...
 - 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
@@ -35,9 +35,13 @@ select * from products
 </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
index 2659e86545842521a935ba2a7432fd90d1f160b8..fe4801b9e0bf7e760ea9de6557c3e1f56c5348cb 100644 (file)
@@ -65,6 +65,30 @@ List&lt;Product&gt; allProducts = db.buildObjects(Product.class, rs);
 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
+            &amp;&amp; co.name.equals(name)\r
+            &amp;&amp; co.value == new BigDecimal("1")\r
+            &amp;&amp; co.amount == 1L\r
+            &amp;&amp; co.birthday.before(new java.util.Date())\r
+            &amp;&amp; co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))\r
+            &amp;&amp; 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
diff --git a/docs/03_natural_syntax.mkd b/docs/03_natural_syntax.mkd
deleted file mode 100644 (file)
index a91c5b8..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-## 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
-            &amp;&amp; co.name.equals(name)\r
-            &amp;&amp; co.value == new BigDecimal("1")\r
-            &amp;&amp; co.amount == 1L\r
-            &amp;&amp; co.birthday.before(new java.util.Date())\r
-            &amp;&amp; co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))\r
-            &amp;&amp; co.time.before(java.sql.Time.valueOf("23:23:23"));\r
-        }\r
-    }).selectCount();\r
-%ENDCODE%
\ No newline at end of file
diff --git a/docs/03_performance.mkd b/docs/03_performance.mkd
new file mode 100644 (file)
index 0000000..1996dc2
--- /dev/null
@@ -0,0 +1,16 @@
+\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
index 6e6196a1695dfb8c4c18901d173061dc997244c2..8a4f45b9832105f2bd1bb35dd8831b42e5949d1e 100644 (file)
@@ -10,6 +10,7 @@ Additionally, [eclipse-cs](http://eclipse-cs.sourceforge.net), [FindBugs](http:/
 ### 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
index c6d84e1af2f3d71af27499fb4fd2cadbccb22b23..91ef18f46bbc77c1668783057d74677d319415cd 100644 (file)
@@ -6,10 +6,17 @@
 \r
 **%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%*\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** &nbsp; *released 2011-08-12*\r
+\r
+- fixed failure of db.delete(PrimitiveModel) and db.update(PrimitiveModel)\r
+\r
 **0.6.4** &nbsp; *released 2011-08-12*\r
 \r
 - api change release (API v4)\r
@@ -28,7 +35,7 @@
 - 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
index e8ac70aeeeaed60c0008041bbb86e415fc2a32f1..1eeef4737f70fd6e87e3ab8ba110ba9de4178d2e 100644 (file)
@@ -25,11 +25,11 @@ public class Constants {
 \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
index cbc1ed4b1d43536e6ff5f3148d2b40d261cda4f4..bb087e476da1a6b08ebccab00d3e997706d8f0d0 100644 (file)
@@ -74,6 +74,7 @@ public class Db {
                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
@@ -192,13 +193,30 @@ public class Db {
         * 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
@@ -220,7 +238,7 @@ public class Db {
        @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
index fb1e5d69dd0e53d2e5a2bd270ec2053ab4d27fd3..c30bbb1ded4a67093bcbf6ff197d10471aff23a7 100644 (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));
                                        }
                                }
                        }
index 2917882baf4056afc1a0d57136dad1da0394dd8b..7a0b58f2ca6cb7f05fa17d1dfb09900704f1e90f 100644 (file)
@@ -72,7 +72,8 @@ import java.lang.annotation.Target;
  * </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
@@ -400,8 +401,8 @@ public interface Iciql {
                 * 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
index 5a99c5e96d4b8652ee408ccc0a4f10b8a195d345..7d6f152fd0d62a12624e1dde2d404ca908126a24 100644 (file)
@@ -28,9 +28,10 @@ public class IciqlException extends RuntimeException {
        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
@@ -51,7 +52,7 @@ public class IciqlException extends RuntimeException {
                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
@@ -60,7 +61,7 @@ public class IciqlException extends RuntimeException {
                        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
@@ -73,7 +74,7 @@ public class IciqlException extends RuntimeException {
                        return e;\r
                }\r
        }\r
-       \r
+\r
        public void setSQL(String sql) {\r
                this.sql = sql;\r
        }\r
@@ -85,7 +86,7 @@ public class IciqlException extends RuntimeException {
        public int getIciqlCode() {\r
                return iciqlCode;\r
        }\r
-       \r
+\r
        private void configureCode(Throwable t) {\r
                if (t == null) {\r
                        return;\r
@@ -96,29 +97,39 @@ public class IciqlException extends RuntimeException {
                        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
index 048ce8d936deba32aba6322f34b6e1e092da99ae..5613d009639c5d50ea6e6c936e679324f1631f4a 100644 (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()) {
index 699574d44cdd57bcbf3e69169db5bd65a1d3c5e9..8f347619fad7febdeab0502d4a460220a418d3e1 100644 (file)
@@ -193,7 +193,7 @@ public class Query<T> {
        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
@@ -511,13 +511,13 @@ public class Query<T> {
                }\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
@@ -575,12 +575,12 @@ public class Query<T> {
                }\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
@@ -712,12 +712,7 @@ public class Query<T> {
                                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
index 8628f40f6b58649151ff38879af2cbe5a0528d0d..72d19dc425a5ee15da07187b4f51647ffb76cd3c 100644 (file)
@@ -18,6 +18,7 @@ package com.iciql;
 \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
index 727285d3684c777b6fc45fbe2df1210c4e905521..c1e3b0363ae42e1e7bdc42ddbd99e75823ce434a 100644 (file)
@@ -344,6 +344,7 @@ public class QueryWhere<T> {
                query.orderBy(field);\r
                return this;\r
        }\r
+\r
        /**\r
         * Order by a number of Object columns.\r
         * \r
index 7821c3fd546ee0e3d51d766105fb90c3540fef2e..97d9fe9597a1432770f2af6c4a70615ebf1dc7cd 100644 (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();
 }
index 7786829f71be4034d2e0a5db6a50637e75d247e2..69502c3ef318ae3722c8cff40456e1c2f316279f 100644 (file)
@@ -61,11 +61,21 @@ public class SQLDialectDefault implements SQLDialect {
                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
@@ -88,9 +98,13 @@ public class SQLDialectDefault implements SQLDialect {
        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
@@ -183,12 +197,12 @@ public class SQLDialectDefault implements SQLDialect {
        }\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
diff --git a/src/com/iciql/SQLDialectDerby.java b/src/com/iciql/SQLDialectDerby.java
new file mode 100644 (file)
index 0000000..42f8cfd
--- /dev/null
@@ -0,0 +1,151 @@
+/*\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
index 80786f98a4a80061b1a0c3ed9c0388197b664bde..ab08475e318842b8f8160958ba03080f65af1b85 100644 (file)
@@ -57,11 +57,12 @@ public class SQLDialectH2 extends SQLDialectDefault {
                        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
index e617758286a3ba40c7b21634eccc7d45cc5c74e8..566bb23943c7597e136f2b835e2a7c6107ab79b0 100644 (file)
@@ -16,6 +16,9 @@
 \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
@@ -23,14 +26,15 @@ import com.iciql.util.StatementBuilder;
  * 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
@@ -44,8 +48,6 @@ public class SQLDialectHSQL extends SQLDialectDefault {
                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
@@ -65,4 +67,91 @@ public class SQLDialectHSQL extends SQLDialectDefault {
                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
index 2593e0ab332e8ee69bed5c80c968f959c1dcf8f3..a6c12183924c6dfcb2323c6b197a824d4462c617 100644 (file)
@@ -31,7 +31,7 @@ public class SQLDialectMySQL extends SQLDialectDefault {
                }\r
                return sqlType;\r
        }\r
-       \r
+\r
        @Override\r
        public boolean supportsMemoryTables() {\r
                return false;\r
@@ -41,15 +41,16 @@ public class SQLDialectMySQL extends SQLDialectDefault {
        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
@@ -74,7 +75,7 @@ public class SQLDialectMySQL extends SQLDialectDefault {
                        buff.append(prepareColumnName(col));\r
                }\r
                buff.append(") ");\r
-               \r
+\r
                // USING\r
                switch (index.type) {\r
                case HASH:\r
index 496250213a371a44c9b2928d48d2891134d2991c..a33fe6c66c3f9b863a508a81d71fce103db31bf5 100644 (file)
@@ -65,6 +65,12 @@ public class SQLStatement {
        }\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
@@ -112,7 +118,8 @@ public class SQLStatement {
                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
index ec797560c73381e14d1ac34db18daeb89a050935..4d9fec224a5c47b77f0d6fb4fae62473f48bd450 100644 (file)
@@ -447,30 +447,35 @@ public class TableDefinition<T> {
        }\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
@@ -552,7 +557,13 @@ public class TableDefinition<T> {
                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
index e682f2fbfb508095e8a012c0c8d056abafa21832..dc289565868d372bd8ce9496e2e4e1c5a7abf6f9 100644 (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;
                                        }
index f2322e339b178d9f4adae8893546ea5f5fb4f50e..8c309820d07f05e4413dbd0ff6d1618fe018313f 100644 (file)
@@ -17,7 +17,6 @@
 
 package com.iciql;
 
-
 /**
  * This class represents "SET column = value" in an UPDATE statement.
  * 
index cc3895c5e9cc80455a9b5faaf5ea962a069c72fb..010273929872fb010dc5b0031d94b405d7bce9f4 100644 (file)
@@ -57,6 +57,7 @@ public class Build {
                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
@@ -169,13 +170,15 @@ public class Build {
                                "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
index 4ff5e68375aeff4806ae8b26b781158464f08b24..81863eedef2030758552e27256efdc95017913d1 100644 (file)
@@ -87,8 +87,8 @@ public class BuildSite {
                        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
@@ -168,7 +168,8 @@ public class BuildSite {
 \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
@@ -193,9 +194,16 @@ public class BuildSite {
                                                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
@@ -317,9 +325,12 @@ public class BuildSite {
                @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
index 35e94e9b2379940e86f620623f9d7a8ff2ba91b7..38fd2f56c9f8720a9dea3bee8053d8a3318025c7 100644 (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() {
index 69ce852d341c52775faa25c5fe29101ce63d341e..ce98105d791b67ee8f9b88deb31655f93484e204 100644 (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"));
index 4ef006ee2089b96222bc211add7fb04d2cc8f23c..4a4a2b612a46ee680dcee41babffacaf54612676 100644 (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 {
index 2398ade3f37a3f417afd21912131493fe27714e6..f46099c029e66fe0411abbd617f7fdd74827d8d4 100644 (file)
@@ -60,7 +60,7 @@ public class Slf4jStatementListener implements StatementListener {
 \r
        /**\r
         * Sets the logging level for a particular statement type.\r
-        *  \r
+        * \r
         * @param type\r
         * @param level\r
         */\r
@@ -84,10 +84,10 @@ public class Slf4jStatementListener implements StatementListener {
                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
index 65c06793eb28d28bccd92ddf3d46630462a16884..47e80547dae49b156d4dcbd24ff17438eee1e0be 100644 (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() {
index 2a8c2971faf1e1d5f5a1e9037e1f39471df456c1..dd3f180a7f428d00a760fec4d9d79aa577bf4ce0 100644 (file)
 
 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();
+       }
 }
index d9e065c19d03abcdcd07a42512fcd8bbacbe1fb3..566c6f49102a507fbd6a5235a4dcdb5d203d9d80 100644 (file)
@@ -116,7 +116,9 @@ public class Utils {
                                                        }\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
@@ -194,7 +196,9 @@ public class Utils {
                                        }\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
@@ -242,7 +246,7 @@ public class Utils {
                                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
index 0f91365e6a6caa8e6680df6e62110a8081a4c416..092f38bac21d3b61f0c95427de900b4e5f58f20f 100644 (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);
index b156cac12a87a4f335044bcd7890421c018caa1f..7e4e91888126600e760db5203a68ea72dde9ce70 100644 (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());
 
index 6111e725e0410e5593567b415003de7804a25d5e..1e1630a16fde3c13fd21c3202c5b27088efbc5fc 100644 (file)
@@ -39,7 +39,7 @@ public class BooleanModelTest {
 \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
@@ -82,7 +82,7 @@ public class BooleanModelTest {
 \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
index bd857169720651f491bfbb88877bf91d6ff0d0ce..3b32dcbcc831cdf5835efed57ab1040dc290264d 100644 (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."),
index 1641eb1f09d39bbfdcf18d4c67771161f3fd0be9..e2482655de95aac2c4dddddd6d18a249cbfa67b5 100644 (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();
                }
        }
 }
index 7c7c84f0da31b6e9014b2e2d4b4c934b422b833b..1374379e48e527f9b86121ec9044cc75754e64f1 100644 (file)
@@ -34,7 +34,7 @@ public class DefaultValuesTest {
 \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
index fd6af2f729ad2ebb0827b3ed488d6892d83bdc40..d0106835fa2b275940243a28b69534ded8c48d41 100644 (file)
@@ -40,7 +40,7 @@ public class EnumsTest {
 \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
index 5842a2ece27608f2812e837cfbdfdfa08e13423a..c43bdafd8bf084d45a992e9c28ddb6e87da041a1 100644 (file)
  */\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
@@ -27,7 +32,13 @@ import org.junit.runner.notification.Failure;
 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
@@ -45,29 +56,62 @@ import com.iciql.Db;
                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
@@ -76,23 +120,161 @@ public class IciqlSuite {
                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
@@ -101,6 +283,8 @@ public class IciqlSuite {
        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
@@ -108,14 +292,27 @@ public class IciqlSuite {
                }\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
index 797c247a9249994038a10080070862b937d8d10e..9bbf450d53492230021a1c3529bedd7e80fc518c 100644 (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();
index 9c214b18e739f2e1ea7d413ec021209320616a29..be2b7260520e32b027e2aec4598247f3444149f9 100644 (file)
@@ -34,7 +34,7 @@ public class PrimitivesTest {
 \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
@@ -74,11 +74,11 @@ public class PrimitivesTest {
                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
index fa3ee1c7ba0447207e1a2f1b8580aa7681de11f0..bf6f20d91cfc1213b46d1e518f8f83373df65f8b 100644 (file)
@@ -34,7 +34,7 @@ public class RuntimeQueryTest {
 \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
@@ -50,14 +50,15 @@ public class RuntimeQueryTest {
 \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
@@ -65,10 +66,10 @@ public class RuntimeQueryTest {
 \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
@@ -78,7 +79,7 @@ public class RuntimeQueryTest {
 \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
index 30723bb65661eae9682d6a1d931e27f1d48df2e5..f16672af6d9e2e874686ccf6ba046d8481f939a9 100644 (file)
@@ -60,7 +60,7 @@ public class SamplesTest {
 \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
@@ -278,7 +278,7 @@ public class SamplesTest {
        @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
index a4ee61cb113abef234028084de40f6fdc4f5f47c..717c23ade97f3ad32ea66a54479c53081564b362 100644 (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());
index 294dc27cc5e1a02fef3e8ea1e26b2ede2eb55421..ce9b9eca5633f2399e37b63e67b7aedde688f3bc 100644 (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() {
index a0bbb7e15222d96397fc760fec40b76472a3f9d8..ae8e40d92542bf3d41a635dca41632686ce69799 100644 (file)
@@ -31,7 +31,7 @@ public class Customer {
 \r
        @IQColumn(length = 25)\r
        public String customerId;\r
-       \r
+\r
        @IQColumn(length = 2)\r
        public String region;\r
 \r
index ab091867d2a7eefb2e76edfdff4902fa67f5745b..cb3b4210057c6a7098bb42890f30787e34374541 100644 (file)
@@ -31,14 +31,14 @@ public class DefaultValuesModel {
 \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
index 0755e95c1c8179f76263f7b333684df3177943b4..1fa9097310aa18a8696420200c12957ba9a84460 100644 (file)
@@ -36,7 +36,7 @@ import com.iciql.Iciql;
 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
@@ -60,9 +60,12 @@ public class Order implements Iciql {
 \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
index 07c161174dec17a7015d70d9af21f0df9d6789ce..0affd28f9693644336983864bbc7837016a28ec2 100644 (file)
@@ -81,7 +81,7 @@ public class PrimitivesModel {
                }\r
                return list;\r
        }\r
-       \r
+\r
        @Override\r
        public String toString() {\r
                return String.valueOf(myLong);\r
index 35eaa672f41f1eecacdc1fb47f893a65f469d420..106e51d76d9b09911dd002e2d2ad9468856e8a75 100644 (file)
@@ -59,7 +59,8 @@ public class Product implements Iciql {
                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
@@ -71,7 +72,8 @@ public class Product implements Iciql {
                                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
index 0a63aae48cb3c4471d20bf0413a320bb1a77ed22..112f4ef93c4631d850909bc4dd967b4dc088f719 100644 (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() {
index 646f75c574e232960c1e31eb771e5f60c9aef380..703f8cdf7cd0172fe422f697e46669aefccc7edf 100644 (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);
+       }
 }
index 4bd497d8290f445f1889008bc6d393852fd08584..596af74a94d26e7ce25f1392d96c0e2eabd095d6 100644 (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.
         */