This is an automated email from the ASF dual-hosted git repository.

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new 2e71ca1  EMPIREDB-362 Split DBModelChecker, DBModelParser and improved 
CodeGenerator
2e71ca1 is described below

commit 2e71ca17e1a8ab4a95356e69fb4a5d26c7063c32
Author: Rainer Döbele <[email protected]>
AuthorDate: Fri Feb 25 18:08:18 2022 +0100

    EMPIREDB-362 Split DBModelChecker, DBModelParser and improved CodeGenerator
---
 .../apache/empire/db/codegen/CodeGenConfig.java    |  34 +-
 .../db/codegen/CodeGenConfigInvalidException.java  |  83 +--
 .../apache/empire/db/codegen/CodeGenParser.java    | 589 ---------------------
 .../empire/db/codegen/CodeGenParserMySQL.java      |  61 ---
 .../apache/empire/db/codegen/CodeGenerator.java    | 132 ++++-
 .../apache/empire/db/codegen/WriterService.java    |  15 +
 .../src/main/resources/templates/Table.vm          |  15 +-
 .../empire/db/codegen/CodeGenParserTest.java       |  40 +-
 .../src/test/resources/testconfig.xml              |   9 +-
 .../empire-db-example-codegen/generate-config.xml  |   3 +
 .../empire-db-example-codegen/generate-example.xml |  20 +-
 .../empire-db-example-codegen/pom.xml              |   1 +
 .../org/apache/empire/db/maven/CodeGenMojo.java    |  19 +-
 .../main/java/org/apache/empire/db/DBUtils.java    |   2 +-
 .../empire/db/validation/DBModelChecker.java       | 512 +-----------------
 .../apache/empire/db/validation/DBModelParser.java | 584 ++++++++++++++++++++
 .../java/org/apache/empire/dbms/DBMSHandler.java   |   7 +
 .../org/apache/empire/dbms/DBMSHandlerBase.java    |  14 +-
 .../java/org/apache/empire/dbms/DBSqlPhrase.java   |  24 +-
 .../apache/empire/dbms/hsql/DBMSHandlerHSql.java   |   8 +-
 .../empire/dbms/mysql/MySQLDBModelParser.java      | 100 ++++
 .../empire/dbms/oracle/DBMSHandlerOracle.java      |  21 +-
 .../empire/dbms/oracle/OracleDBModelChecker.java   |  29 +-
 .../OracleDBModelParser.java}                      |  37 +-
 .../empire/dbms/sqlserver/DBMSHandlerMSSQL.java    |  11 +-
 ...DBModelChecker.java => MSSqlDBModelParser.java} |  18 +-
 .../exceptions/InvalidPropertyException.java       |   5 +
 .../org/apache/empire/xml/XMLConfiguration.java    |   2 +-
 28 files changed, 1086 insertions(+), 1309 deletions(-)

diff --git 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
index ff747ef..94e0198 100644
--- 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
+++ 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfig.java
@@ -42,6 +42,8 @@ public class CodeGenConfig extends XMLConfiguration {
 
        private String jdbcPwd;
 
+    private String dbmsHandlerClass;
+       
        // generation options
        /**
         * name of the database catalog (may be null)
@@ -65,7 +67,13 @@ public class CodeGenConfig extends XMLConfiguration {
      * flag whether to parse and generate views
      */
     private boolean generateRecords = true;
-       /**
+
+    /**
+     * Name of the identity column used for Autoincrement
+     */
+    private String identityColumn = null;
+    
+    /**
         * Name of the timestamp column used for optimistic locking (may be 
null)
         * e.g. "UPDATE_TIMESTAMP";
         */
@@ -307,6 +315,18 @@ public class CodeGenConfig extends XMLConfiguration {
        public void setJdbcPwd(String jdbcPwd) {
                this.jdbcPwd = jdbcPwd;
        }
+       
+       // ------- DBMS Handler -------
+
+    public String getDbmsHandlerClass()
+    {
+        return dbmsHandlerClass;
+    }
+
+    public void setDbmsHandlerClass(String dbmsHandlerClass)
+    {
+        this.dbmsHandlerClass = dbmsHandlerClass;
+    }
 
        // ------- generation options -------
 
@@ -314,7 +334,7 @@ public class CodeGenConfig extends XMLConfiguration {
                return dbCatalog;
        }
 
-       public void setDbCatalog(String dbCatalog) {
+    public void setDbCatalog(String dbCatalog) {
                this.dbCatalog = dbCatalog;
        }
 
@@ -352,6 +372,16 @@ public class CodeGenConfig extends XMLConfiguration {
         this.generateRecords = generateRecords;
     }
 
+    public String getIdentityColumn()
+    {
+        return identityColumn;
+    }
+
+    public void setIdentityColumn(String identityColumn)
+    {
+        this.identityColumn = identityColumn;
+    }
+
     public String getTimestampColumn() {
                return timestampColumn;
        }
diff --git 
a/empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfigInvalidException.java
similarity index 54%
copy from 
empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
copy to 
empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfigInvalidException.java
index 47c3e05..5940c4b 100644
--- 
a/empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
+++ 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfigInvalidException.java
@@ -1,34 +1,49 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.empire.exceptions;
-
-import org.apache.empire.commons.ErrorType;
-import org.apache.empire.commons.StringUtils;
-
-public class InvalidPropertyException extends EmpireException
-{
-    private static final long serialVersionUID = 1L;
-    
-    public static final ErrorType errorType = new 
ErrorType("error.propertyInvalid", "The property {0} is not valid. Current 
value is {1}.");
-    
-    public InvalidPropertyException(String property, Object value)
-    {
-        super(errorType, new String[] { property, StringUtils.valueOf(value) 
});
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.db.codegen;
+
+import org.apache.empire.exceptions.InvalidPropertyException;
+import org.slf4j.Logger;
+
+public class CodeGenConfigInvalidException extends InvalidPropertyException
+{
+    private static final Logger log = 
org.slf4j.LoggerFactory.getLogger(CodeGenConfig.class);
+    
+    private static final long serialVersionUID = 1L;
+    
+    public CodeGenConfigInvalidException(String property, Object value, 
Exception cause)
+    {
+        super(property, value, cause);
+    }
+    
+    public CodeGenConfigInvalidException(String property, Object value)
+    {
+        this(property, value, null);
+    }
+    
+    /**
+     * log the error (info must be enabled)
+     */
+    @Override
+    protected void log()
+    {
+        log.error("Invalid configuration: "+getMessage(), this);
+    }
+    
+}
diff --git 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java
 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java
deleted file mode 100644
index ab1b851..0000000
--- 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParser.java
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.empire.db.codegen;
-
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.DriverManager;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Types;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.empire.commons.StringUtils;
-import org.apache.empire.data.DataType;
-import org.apache.empire.db.DBColumn;
-import org.apache.empire.db.DBCommandExpr;
-import org.apache.empire.db.DBDatabase;
-import org.apache.empire.db.DBRelation;
-import org.apache.empire.db.DBTable;
-import org.apache.empire.db.DBTableColumn;
-import org.apache.empire.db.DBView;
-import org.apache.empire.db.DBView.DBViewColumn;
-import org.apache.empire.exceptions.ItemNotFoundException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This class is used to create a in memory DBDatabase of a given SQLConnection
- * and Configuration
- * 
- * @author Benjamin Venditti
- */
-public class CodeGenParser {
-
-       public static class InMemoryDatabase extends DBDatabase {
-        // *Deprecated* private static final long serialVersionUID = 1L;
-        
-        @Override
-        public List<DBRelation> getRelations()
-        {
-            return relations;
-        }
-       }
-       
-       public static class InMemoryView extends DBView {
-    // *Deprecated* private static final long serialVersionUID = 1L;
-
-               public InMemoryView(String name, DBDatabase db) {
-                       super(name, db);
-               }
-               
-               public DBViewColumn addCol(String columnName,DataType dataType)
-               {
-                       return addColumn(columnName, dataType);
-               }
-               
-               @Override
-               public DBCommandExpr createCommand() {
-                       return null;
-               }
-       }
-
-       private static final Logger log = 
LoggerFactory.getLogger(CodeGenParser.class);
-       
-       private DatabaseMetaData dbMeta;
-       private Connection con;
-       private CodeGenConfig config;
-
-       /**
-        * create a empty in memory Database and populates it
-        */
-       public CodeGenParser(CodeGenConfig config) {
-           this.config = config;
-       }
-
-       /**
-        * returns the populated DBDatabase
-        */
-       public DBDatabase loadDbModel() {
-               DBDatabase db = new InMemoryDatabase();
-           try {           
-            con = openJDBCConnection(config);
-            populateDatabase(db);
-        } 
-        catch (SQLException e) 
-        {
-            throw new RuntimeException("Unable to read database metadata: " + 
e.getMessage(), e);
-        }
-        finally 
-        {
-            close(con);
-        }
-        return db;
-       }
-
-       /**
-     * Opens and returns a JDBC-Connection.
-     * JDBC url, user and password for the connection are obained from the 
SampleConfig bean
-     * Please use the config.xml file to change connection params.
-     */
-       protected Connection openJDBCConnection(CodeGenConfig config) throws 
SQLException{
-        log.info("Connecting to Database'" + config.getJdbcURL() + "' / User=" 
+ config.getJdbcUser());
-        Connection conn = null;
-        try {
-            Class.forName(config.getJdbcClass()).newInstance();
-        }catch(Exception ex){
-               throw new SQLException("Could not load database dbms: " + 
config.getJdbcClass());
-        }
-        conn = DriverManager.getConnection(config.getJdbcURL(), 
config.getJdbcUser(), config.getJdbcPwd());
-        log.info("Connected successfully");
-        return conn;
-    }
-
-       /**
-        * Returns whether to add the table or ignore it
-        * @param tableName
-        */
-    protected boolean isPopulateTable(String tableName)
-    {
-        if (tableName.indexOf('$') >= 0)
-            return false;
-        return true;
-    }
-    
-    /**
-     * Returns whether to add the column or ignore it
-     * @param columnName
-     */
-    protected boolean isPopulateColumn(String columnName)
-    {
-        return true;
-    }
-       
-       /**
-        * Queries the metadata of the database for tables and vies and 
populates the
-        * database with those
-        * @throws SQLException 
-        */
-    protected void populateDatabase(DBDatabase db) throws SQLException {
-               ResultSet tables = null;
-               ArrayList<String> populatedTables=new ArrayList<String>();
-               try{
-            this.dbMeta = con.getMetaData();
-            String[] tablePatterns = {null}; // Could be null, so start that 
way.
-                       if(config.getDbTablePattern() != null)
-                               tablePatterns = 
config.getDbTablePattern().split(","); // Support a comma separated list of 
table patterns (i.e. specify a list of table names in the config file).
-            
-            int tableCount = 0; // Moved to be outside table pattern loop.
-            int viewCount = 0;
-            for(String pattern : tablePatterns){
-                // types
-                String[] types = config.isGenerateViews() ? new String[] { 
"TABLE", "VIEW" }
-                                                          : new String[] { 
"TABLE" }; 
-                           // Get table metadata
-                   tables = dbMeta.getTables(
-                                   config.getDbCatalog(), 
-                                   config.getDbSchema(), 
-                                   pattern == null ? pattern: pattern.trim(),
-                                               types);
-                   
-                   // Add all tables and views 
-                               while (tables.next()) {
-                                       String tableName = 
tables.getString("TABLE_NAME");
-                               if (!isPopulateTable(tableName)) {
-                        log.info("Ignoring table " + tableName);
-                                   continue;
-                               }
-                                       // show
-                                       String tableType = 
tables.getString("TABLE_TYPE");
-                                       String tableSchema = 
tables.getString("TABLE_SCHEM");
-                                       String templ = 
StringUtils.isNotEmpty(tableSchema) ? "{0}: {1} ({2})" : "{0}: {1}"; 
-                                   log.info(MessageFormat.format(templ, 
tableType, tableName, tableSchema));
-                                       // Table or View
-                                       if(tableType.equalsIgnoreCase("VIEW")){
-                                               InMemoryView view = new 
InMemoryView(tableName, db);
-                                               populateView(view);
-                                               viewCount++;
-                                       } else {
-                                               DBTable table = new 
DBTable(tableName, db);
-                                               populateTable(table);
-                                               populatedTables.add(tableName);
-                                               tableCount++;
-                                       }
-                               }
-                       }
-                       // Add all relations
-                       gatherRelations(db, dbMeta, populatedTables);
-
-                       if (tableCount==0 && viewCount==0) {
-                           // getTables returned no result
-                           String info = "catalog="+config.getDbCatalog(); 
-                info += "/ schema="+config.getDbSchema(); 
-                info += "/ pattern="+config.getDbTablePattern(); 
-                           log.warn("DatabaseMetaData.getTables() returned no 
tables or views! Please check parameters: "+info);
-                               log.info("Available catalogs: " + 
getCatalogs(dbMeta));
-                               log.info("Available schemata: " + 
getSchemata(dbMeta));
-                       }
-               } finally {
-                       close(tables);
-               }
-       }
-       
-    protected void gatherRelations(DBDatabase db, DatabaseMetaData dbMeta, 
ArrayList<String> tables) throws SQLException{
-               ResultSet relations = null;
-               String fkTableName, pkTableName, fkColName, pkColName, relName;
-               DBTableColumn fkCol, pkCol;
-               DBTable fkTable, pkTable;
-               DBColumn col;
-               
-               // Add all Relations
-               for (String tableName :tables) {
-                       
-                       // check for foreign-keys
-                       relations = 
dbMeta.getImportedKeys(config.getDbCatalog(), config .getDbSchema(), tableName);
-                       while (relations.next()) {
-                               pkCol=fkCol=null;
-                               
-                               fkTableName=relations.getString("FKTABLE_NAME");
-                               pkTableName=relations.getString("PKTABLE_NAME");
-                               fkColName=relations.getString("FKCOLUMN_NAME");
-                               pkColName=relations.getString("PKCOLUMN_NAME");
-
-                               // Detect relation name
-                               relName=relations.getString("FK_NAME");
-                               if (StringUtils.isEmpty(relName))
-                                       
relName=fkTableName+"."+fkColName+"-"+pkTableName+"."+pkColName;
-                               
-                               pkTable = db.getTable(pkTableName);
-                               fkTable = db.getTable(fkTableName);
-                               
-                               // check if both tables really exist in the 
model
-                               if(pkTable==null || fkTable==null){
-                                       log.error("Unable to add the relation 
\""+relName+"\"! One of the tables could not be found.");
-                                       continue;
-                               }
-                               
-                               col=pkTable.getColumn(pkColName);
-                               if(col instanceof DBTableColumn)
-                                       pkCol = (DBTableColumn) col;
-       
-                               col=fkTable.getColumn(fkColName);
-                               if(col instanceof DBTableColumn)
-                                       fkCol = (DBTableColumn) col;
-                               
-                               // check if both columns really exist in the 
model
-                               if(fkCol==null || pkCol==null){
-                                       log.error("Unable to add the relation 
\""+relName+"\"! One of the columns could not be found.");
-                                       continue;
-                               }
-                               
-                               // add the relation
-                               DBRelation.DBReference reference = 
fkCol.referenceOn(pkCol);
-                               DBRelation.DBReference[] refs = null;
-                       DBRelation r = db.getRelation(relName);
-                       if (r!=null) {
-                               DBRelation.DBReference[] refsOld = 
r.getReferences();
-                               refs = new 
DBRelation.DBReference[refsOld.length+1];
-                               int i=0;
-                               for (; i<refsOld.length; i++)
-                                       refs[i]=refsOld[i];
-                               refs[i]=reference;
-                               // remove old relation
-                    log.warn("Duplicate relation {}", r.getName());
-                               db.getRelations().remove(r);
-                       } else {
-                               refs = new DBRelation.DBReference[] { reference 
};
-                       }
-                               // Add a new relation
-                               db.addRelation(relName, refs);
-                               log.info("Added relation (FK-PK): "+relName);
-                       }
-               }
-       }
-
-    protected String getCatalogs(DatabaseMetaData dbMeta) throws SQLException {
-               String retVal = "";
-               ResultSet rs = dbMeta.getCatalogs();
-               while (rs.next()) {
-                       retVal += rs.getString("TABLE_CAT") + ", ";
-               }
-               if(retVal.length()>2)
-                       retVal=retVal.substring(0,retVal.length()-2);
-               
-               return retVal;
-       }
-
-    protected String getSchemata(DatabaseMetaData dbMeta) throws SQLException {
-               String retVal = "";
-               ResultSet rs = dbMeta.getSchemas();
-               while (rs.next()) {
-                       retVal += rs.getString("TABLE_SCHEM") + ", ";
-               }
-               if(retVal.length()>2)
-                       retVal=retVal.substring(0,retVal.length()-2);
-               return retVal;
-       }
-
-       /**
-        * queries the metadata for columns of a specific table and populates 
the
-        * table with that information
-        * @throws SQLException 
-        */
-    protected void populateTable(DBTable t) throws SQLException {
-               List<String> pkCols = this.findPkColumns(t.getName());
-               String lockColName = config.getTimestampColumn();
-               DBColumn[] key = new DBColumn[pkCols.size()];
-               ResultSet rs = null;
-               try {
-                       rs = dbMeta.getColumns(config.getDbCatalog(), 
config.getDbSchema(),     t.getName(), null);
-               int i=0;
-                       while (rs.next()) {
-                               DBTableColumn c = addColumn(t, rs, lockColName);
-                               if (c==null)
-                                   continue;
-                               // check if it is a KeyColumn
-                               if (pkCols.contains(c.getName()))
-                                       key[i++] = c;
-                       }
-               // Check whether all key columns have been set
-               for (i=0; i<key.length; i++)
-                   if (key[i]==null){
-                       throw new ItemNotFoundException(pkCols.get(i));
-                   }
-               if(key.length > 0){
-                       t.setPrimaryKey(key);
-               }
-               } finally {
-                       close(rs);
-               }
-       }
-       
-       /**
-        * queries the metadata for columns of a specific table and populates 
the
-        * table with that information
-        * @throws SQLException 
-        */
-    protected void populateView(InMemoryView v) throws SQLException {
-               ResultSet rs = null;
-               try {
-                       rs = dbMeta.getColumns(config.getDbCatalog(), 
config.getDbSchema(),
-                                       v.getName(), null);
-                       while (rs.next()) {
-                               addColumn(v, rs);
-                       }
-               } finally {
-                       close(rs);
-               }
-       }
-
-       /**
-        * Returns a list of column names that define the primarykey of the 
given
-        * table.
-        * @throws SQLException 
-        */
-    protected List<String> findPkColumns(String tableName) throws SQLException 
{
-               List<String> cols = new ArrayList<String>();
-               ResultSet rs = null;
-               try {
-                       rs = dbMeta.getPrimaryKeys(config.getDbCatalog(), config
-                                       .getDbSchema(), tableName);
-                       while (rs.next()) {
-                               cols.add(rs.getString("COLUMN_NAME"));
-                       }
-               } finally {
-                       close(rs);
-               }
-               return cols;
-       }
-
-       /**
-        * Adds DBColumn object to the given DBTable. The DBColumn is created 
from
-        * the given ResultSet
-        */
-    protected DBTableColumn addColumn(DBTable t, ResultSet rs, String 
lockColName)
-                       throws SQLException {
-               
-           String name = rs.getString("COLUMN_NAME");
-           if (!isPopulateColumn(name)) {
-            log.info("Ignoring column " + name);
-            return null;
-           }
-           
-               DataType empireType = getEmpireDataType(rs.getInt("DATA_TYPE"));
-               double colSize = getColumnSize(empireType, 
rs.getInt("DATA_TYPE"), rs.getInt("COLUMN_SIZE"));
-
-               // Timestamp column
-        if (empireType==DataType.DATETIME && 
StringUtils.isNotEmpty(lockColName) && name.equalsIgnoreCase(lockColName))
-        {
-            empireType=DataType.TIMESTAMP;
-            colSize=0;
-        }
-
-        // Number column
-               if (empireType==DataType.DECIMAL || empireType==DataType.FLOAT)
-               {       // decimal digits
-                       int decimalDig = rs.getInt("DECIMAL_DIGITS");
-                       if (decimalDig>0)
-                       {       // parse
-                               try {
-                                       int intSize = rs.getInt("COLUMN_SIZE");
-                                       colSize = 
Double.parseDouble(String.valueOf(intSize)+'.'+decimalDig);
-                               } catch(Exception e) {
-                                       log.error("Failed to parse decimal 
digits for column "+name);
-                               }
-                       }
-                       // make integer?
-                       if (colSize<1.0d)
-                       {       // Turn into an integer
-                               empireType=DataType.INTEGER;
-                       }
-               }
-               
-               // mandatory field?
-               boolean required = false;
-               String defaultValue = rs.getString("COLUMN_DEF");
-               if (rs.getString("IS_NULLABLE").equalsIgnoreCase("NO"))
-                       required = true;
-               
-               // The following is a hack for MySQL which currently gets sent 
a string "CURRENT_TIMESTAMP" from the Empire-db dbms for MySQL.
-               // This will avoid the dbms problem because CURRENT_TIMESTAMP 
in the db will just do the current datetime.
-               // Essentially, Empire-db needs the concept of default values 
of one type that get mapped to another.
-               // In this case, MySQL "CURRENT_TIMESTAMP" for Types.TIMESTAMP 
needs to emit from the Empire-db dbms the null value and not 
"CURRENT_TIMESTAMP".
-               if(rs.getInt("DATA_TYPE") == Types.TIMESTAMP && defaultValue != 
null && defaultValue.equals("CURRENT_TIMESTAMP")){
-                       required = false; // It is in fact not required even 
though MySQL schema is required because it has a default value. Generally, 
should Empire-db emit (required && defaultValue != null) to truly determine if 
a column is required?
-                       defaultValue = null; // If null (and required per 
schema?) MySQL will apply internal default value.
-               }
-               
-               // AUTOINC indicator is not in java.sql.Types but rather meta 
data from DatabaseMetaData.getColumns()
-               // getEmpireDataType() above is not enough to support AUTOINC 
as it will only return DataType.INTEGER
-               DataType originalType = empireType;
-               ResultSetMetaData metaData = rs.getMetaData();
-               int colCount = metaData.getColumnCount();
-               String colName;
-               for (int i = 1; i <= colCount; i++) {
-                       colName = metaData.getColumnName(i);
-                       // MySQL matches on IS_AUTOINCREMENT column.
-                       // SQL Server matches on TYPE_NAME column with identity 
somewhere in the string value.
-                       if ((colName.equalsIgnoreCase("IS_AUTOINCREMENT") && 
rs.getString(i).equalsIgnoreCase("YES")) ||
-                                       (colName.equals("TYPE_NAME") && 
rs.getString(i).matches(".*(?i:identity).*"))){
-                               empireType = DataType.AUTOINC;
-                               
-                       }
-               }
-               
-               // Move from the return statement below so we can add
-               // some AUTOINC meta data to the column to be used by
-               // the ParserUtil and ultimately the template.
-               log.info("\tCOLUMN:\t" + name + " ("+empireType+")");
-               DBTableColumn col = t.addColumn(name, empireType, colSize, 
required, defaultValue);
-               
-               // We still need to know the base data type for this AUTOINC
-               // because the Record g/setters need to know this, right?
-               // So, let's add it as meta data every time the column is 
AUTOINC
-               // and reference it in the template.
-               if(empireType.equals(DataType.AUTOINC))
-                       col.setAttribute("AutoIncDataType", originalType);
-               return col;
-               
-       }
-       
-       protected double getColumnSize(DataType empireType, int dataType, int 
columnSize) {
-               return columnSize;
-       }
-
-       /**
-        * Adds DBColumn object to the given DBTable. The DBColumn is created 
from
-        * the given ResultSet
-        */
-       private DBViewColumn addColumn(InMemoryView v, ResultSet rs)
-                       throws SQLException {
-               String name = rs.getString("COLUMN_NAME");
-               DataType empireType = getEmpireDataType(rs.getInt("DATA_TYPE"));
-               
-               log.info("\tCOLUMN:\t" + name + " ("+empireType+")");
-               return v.addCol(name, empireType);
-       }
-
-       /**
-        * converts a SQL DataType to a EmpireDataType
-        */
-       private DataType getEmpireDataType(int sqlType) {
-               DataType empireType = DataType.UNKNOWN;
-               switch (sqlType) {
-               case Types.INTEGER:
-               case Types.SMALLINT:
-               case Types.TINYINT:
-               case Types.BIGINT:
-                       empireType = DataType.INTEGER;
-                       break;
-               case Types.VARCHAR:
-               case Types.NVARCHAR:
-                       empireType = DataType.VARCHAR;
-                       break;
-               case Types.DATE:
-                       empireType = DataType.DATE;
-                       break;
-               case Types.TIMESTAMP:
-               case Types.TIME:
-                       empireType = DataType.DATETIME;
-                       break;
-               case Types.CHAR:
-        case Types.NCHAR:
-                       empireType = DataType.CHAR;
-                       break;
-               case Types.DOUBLE:
-               case Types.FLOAT:
-               case Types.REAL:
-                       empireType = DataType.FLOAT;
-                       break;
-               case Types.DECIMAL:
-               case Types.NUMERIC:
-                       empireType = DataType.DECIMAL;
-                       break;
-               case Types.BIT:
-               case Types.BOOLEAN:
-                       empireType = DataType.BOOL;
-                       break;
-               case Types.CLOB:
-               case Types.NCLOB:
-               case Types.LONGVARCHAR:
-               case Types.LONGNVARCHAR:
-                       empireType = DataType.CLOB;
-                       break;
-               case Types.BINARY:
-               case Types.VARBINARY:
-               case Types.LONGVARBINARY:
-               case Types.BLOB:
-                       empireType = DataType.BLOB;
-                       break;
-               default:
-                       empireType = DataType.UNKNOWN;
-                       log.warn("SQL column type " + sqlType + " not 
supported.");
-               }
-               log.debug("Mapping date type " + String.valueOf(sqlType) + " to 
"
-                               + empireType);
-               return empireType;
-       }
-
-    /**
-     * Closes a sql resultset and logs exceptions.
-     * 
-     * @param rs the resultset to close
-     * @param log the logger instance to use for logging
-     * @return true on succes
-     */
-    protected boolean close(ResultSet rs)
-    {   try {
-            if (rs != null)
-                rs.close();
-            return true;
-        } catch (SQLException e) {
-            log.error("The resultset could not be closed!", e);
-            return false;
-        }
-    }
-
-    /**
-     * Closes a JDBC-Connection and logs exceptions.
-     */
-    protected void close(Connection conn)
-    {   try  {
-            log.info("Closing database connection");
-            if (conn != null)
-                conn.close();
-        } catch (Exception e) {
-            log.error("Error closing connection", e);
-        }
-    }
-       
-}
diff --git 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParserMySQL.java
 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParserMySQL.java
deleted file mode 100644
index 242244f..0000000
--- 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenParserMySQL.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.empire.db.codegen;
-
-import java.sql.Types;
-
-import org.apache.empire.data.DataType;
-
-public class CodeGenParserMySQL extends CodeGenParser {
-
-       // private static final Logger log = 
LoggerFactory.getLogger(CodeGenParserMySQL.class);
-       
-       public CodeGenParserMySQL(CodeGenConfig config) {
-               super(config);
-       }
-
-       @Override
-       protected double getColumnSize(DataType empireType, int dataType, int 
columnSize) {
-               
-               switch (empireType) {
-                       
-                       case INTEGER: {
-                               // return size in byte, depending on MySQL 
Integer Types
-                               // see 
http://dev.mysql.com/doc/refman/5.7/en/integer-types.html
-                               // ignore the "real" columnsize as its just a 
"format hint"
-                               switch(dataType) {
-                                       case Types.TINYINT:
-                                               return 1; // TINYINT, 1 byte
-                                       case Types.SMALLINT:
-                                               return 2; // SMALLINT, 2 byte
-                                       case Types.BIGINT:
-                                               return 8; // BIGINT, 8 byte
-                                       default: 
-                                               return 4; // Types.INTEGER, 
INT, 4 byte
-                               }
-                       }
-                       
-                       default:
-                               return super.getColumnSize(empireType, 
dataType, columnSize);
-                       
-               }
-               
-       }
-       
-}
diff --git 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenerator.java
 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenerator.java
index 25325a6..c11b907 100644
--- 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenerator.java
+++ 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenerator.java
@@ -18,7 +18,13 @@
  */
 package org.apache.empire.db.codegen;
 
-import org.apache.empire.db.DBDatabase;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.db.validation.DBModelParser;
+import org.apache.empire.dbms.DBMSHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,34 +50,54 @@ public class CodeGenerator {
                CodeGenerator app = new CodeGenerator();
                app.generate((args.length > 0 ? args[0] : DEFAULT_CONFIG_FILE));
        }       
+
+    /**
+     * Starts the actual generation according to the provided config file
+     */
+    public void generate(final String configFile) {
+        // load configuration file
+        CodeGenConfig config = loadConfig(configFile);
+        // generate now
+        generate(config);
+    }   
+
+    /**
+     * Starts the actual generation according to the provided config file
+     */
+    public void generate(final CodeGenConfig config) {
+        // get the DBMS
+        DBMSHandler dbms = getDBMSHandler(config);
+        // get the JDBC-Connection
+        Connection conn = getJDBCConnection(config);
+        // generate now
+        generate(dbms, conn, config);
+    }   
        
        /**
         * Starts the actual generation according to the provided configuration
         */
-       public void generate(CodeGenConfig config) {
-               // log all options
+       public void generate(DBMSHandler dbms, Connection conn, CodeGenConfig 
config) {
+               
+           // log all options
                listOptions(config);
                
                // read the database model
-               CodeGenParser parser = new CodeGenParser(config);
-               // CodeGenParser parser = new CodeGenParserMySQL(config);
-               DBDatabase db = parser.loadDbModel();
-               
+               // CodeGenParser parser = new CodeGenParser(config);
+               DBModelParser modelParser = 
dbms.createModelParser(config.getDbCatalog(), config.getDbSchema());
+               // set options
+               modelParser.setStandardIdentityColumnName 
(config.getIdentityColumn());
+               
modelParser.setStandardTimestampColumnName(config.getTimestampColumn());
+               // parse now
+        modelParser.parseModel(conn);
+
+        log.info("Model parsing completed successfully!");
+        
                // create the source-code for that database
                CodeGenWriter codeGen = new CodeGenWriter(config);
-               codeGen.generateCodeFiles(db);
+               codeGen.generateCodeFiles(modelParser.getDatabase());
                
                log.info("Code generation completed successfully!");
        }
-
-       /**
-        * Starts the actual generation according to the provided config file
-        */
-       public void generate(final String file) {
-               // load configuration file
-               CodeGenConfig config = loadConfig(file);
-               generate(config);
-       }       
        
        /**
         * Loads the configuration file and
@@ -87,7 +113,7 @@ public class CodeGenerator {
        
        protected void listOptions(CodeGenConfig config){
                // List options
-               log.info("Database connection successful. Config options are:");
+               log.info("Config options are:");
                log.info("SchemaName=" + String.valueOf(config.getDbSchema()));
                log.info("TimestampColumn="     + 
String.valueOf(config.getTimestampColumn()));
                log.info("TargetFolder=" + config.getTargetFolder());
@@ -102,5 +128,75 @@ public class CodeGenerator {
                log.info("NestViews=" + config.isNestViews());
                log.info("CreateRecordProperties=" + 
config.isCreateRecordProperties());
        }
+    
+    /**
+     * <PRE>
+     * Opens and returns a JDBC-Connection.
+     * JDBC url, user and password for the connection are obtained from the 
SampleConfig bean
+     * Please use the config.xml file to change connection params.
+     * </PRE>
+     */
+       protected Connection getJDBCConnection(CodeGenConfig config)
+    {
+        String jdbcDriverClass = config.getJdbcClass();
+        try
+        {   // Get Driver Class Name
+            if (StringUtils.isEmpty(jdbcDriverClass))
+                throw new CodeGenConfigInvalidException("jdbcClass", 
jdbcDriverClass);
+            // Getting the JDBC-Driver
+            Class.forName(jdbcDriverClass).newInstance();
+            // Connect to the database
+            String jdbcURL = config.getJdbcURL();
+            if (StringUtils.isEmpty(jdbcURL))
+                throw new CodeGenConfigInvalidException("jdbcURL", jdbcURL);
+            String jdbcUser = config.getJdbcUser();
+            if (StringUtils.isEmpty(jdbcUser))
+                throw new CodeGenConfigInvalidException("jdbcUser", jdbcUser);
+            log.info("Connecting to Database'" + jdbcURL + "' / User=" + 
jdbcUser);
+            Connection conn = DriverManager.getConnection(config.getJdbcURL(), 
config.getJdbcUser(), config.getJdbcPwd());
+            log.info("Connected successfully");
+            // set the AutoCommit to false for this connection. 
+            // commit must be called explicitly! 
+            conn.setAutoCommit(false);
+            log.info("AutoCommit has been set to " + conn.getAutoCommit());
+            return conn;
+        }
+        catch (InstantiationException | IllegalAccessException | 
ClassNotFoundException e)
+        {
+            throw new CodeGenConfigInvalidException("jdbcClass", 
jdbcDriverClass, e);
+        }
+        catch (SQLException e)
+        {
+            throw new CodeGenConfigInvalidException("jdbcURL/jdbUser", 
config.getJdbcURL()+"/"+config.getJdbcUser(), e);
+        }
+    }
+
+    /**
+     * Creates an Empire-db DatabaseDriver for the given provider and applies 
dbms specific configuration 
+     */
+       protected DBMSHandler getDBMSHandler(CodeGenConfig config)
+    {
+        // Create dbms
+        String dbmsHandlerClass = config.getDbmsHandlerClass();
+        try
+        {   // Get DBMSHandler class
+            if (StringUtils.isEmpty(dbmsHandlerClass))
+                throw new CodeGenConfigInvalidException("dbmsHandlerClass", 
dbmsHandlerClass);
+            // Find class
+            DBMSHandler dbms = (DBMSHandler) 
Class.forName(dbmsHandlerClass).newInstance();
+            // Configure dbms
+            try {
+                config.readProperties(dbms, "dbmsHandlerClass-properties");
+            } catch(Exception e) {
+                log.info("No DbmsHandlerClass-properties provieded.");
+            }
+            // done
+            return dbms;
+        }
+        catch (InstantiationException | IllegalAccessException | 
ClassNotFoundException e)
+        {
+            throw new CodeGenConfigInvalidException("dbmsHandlerClass", 
dbmsHandlerClass, e);
+        }
+    }
 
 }
diff --git 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
index d95cf19..48661e5 100644
--- 
a/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
+++ 
b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/WriterService.java
@@ -281,6 +281,21 @@ public class WriterService {
                return "\"" + String.valueOf(val) + "\"";
        }
        
+    /**
+     * Returns the list of key columns
+     */
+    public String getKeyColumns(DBTable t)
+    {
+        DBColumn[] keyColumns =t.getKeyColumns();
+        StringBuilder b = new StringBuilder();
+        for (int i=0; i<keyColumns.length; i++)
+        {
+            if (i>0)
+                b.append(", ");
+            b.append(keyColumns[i].getName());
+        }
+        return b.toString();
+    }
        
        /**
         * Derives a java class name from a database table name.
diff --git a/empire-db-codegen/src/main/resources/templates/Table.vm 
b/empire-db-codegen/src/main/resources/templates/Table.vm
index aac0b1c..e72cc68 100644
--- a/empire-db-codegen/src/main/resources/templates/Table.vm
+++ b/empire-db-codegen/src/main/resources/templates/Table.vm
@@ -55,21 +55,12 @@ public class $parser.getTableClassName($table.name) extends 
${baseTableClassName
 
 #if($table.keyColumns && $table.keyColumns.size()>0)
                // configure key columns (primary key)
-#if($table.keyColumns.size()==1)
-               setPrimaryKey(${parser.getColumnName($table.keyColumns[0])});
-#else
-               DBTableColumn[] keyColumns = new DBTableColumn[] {
-#foreach ($col in $table.keyColumns)
-#if($foreach.isLast()==false)
-                       ${parser.getColumnName($col)},
-#else
-                       ${parser.getColumnName($col)} };
-               setPrimaryKey(keyColumns);
-#end
+               setPrimaryKey($parser.getKeyColumns($table));
 #end
+#if($table.timestampColumn)
+               setTimestampColumn($table.getTimestampColumn().name);
 #end
 
-#end
        }
 #if($nestTables == true)
   }
diff --git 
a/empire-db-codegen/src/test/java/org/apache/empire/db/codegen/CodeGenParserTest.java
 
b/empire-db-codegen/src/test/java/org/apache/empire/db/codegen/CodeGenParserTest.java
index 745b578..6848c71 100644
--- 
a/empire-db-codegen/src/test/java/org/apache/empire/db/codegen/CodeGenParserTest.java
+++ 
b/empire-db-codegen/src/test/java/org/apache/empire/db/codegen/CodeGenParserTest.java
@@ -24,33 +24,61 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 
+import java.sql.Connection;
 import java.util.List;
 
 import org.apache.empire.db.DBColumn;
 import org.apache.empire.db.DBDatabase;
 import org.apache.empire.db.DBRelation;
-import org.apache.empire.db.DBTable;
 import org.apache.empire.db.DBRelation.DBReference;
+import org.apache.empire.db.validation.DBModelParser;
+import org.apache.empire.dbms.DBMSHandler;
+import org.apache.empire.db.DBTable;
 import org.junit.Before;
 import org.junit.Test;
 
 
 public class CodeGenParserTest {
-    private transient CodeGenParser parser;
 
+    private static class TestCodeGenerator extends CodeGenerator
+    {
+        /**
+         * Starts the actual generation according to the provided config file
+         */
+        public DBDatabase parseModel(final CodeGenConfig config) {
+            // get the DBMS
+            DBMSHandler dbms = getDBMSHandler(config);
+            // get the JDBC-Connection
+            Connection conn = getJDBCConnection(config);
+            // read the database model
+            // CodeGenParser parser = new CodeGenParser(config);
+            DBModelParser modelParser = 
dbms.createModelParser(config.getDbCatalog(), config.getDbSchema());
+            // set options
+            modelParser.setStandardIdentityColumnName 
(config.getIdentityColumn());
+            
modelParser.setStandardTimestampColumnName(config.getTimestampColumn());
+            // parse now
+            modelParser.parseModel(conn);
+            // done
+            return modelParser.getDatabase();
+        }
+    }
+    
+    private transient CodeGenConfig config; 
+    private transient TestCodeGenerator codeGen = new TestCodeGenerator();
+    
     @Before
     public void setUp() throws Exception {
-        final CodeGenConfig config = new CodeGenConfig();
+        config = new CodeGenConfig();
         config.init("src/test/resources/testconfig.xml");
         config.setDbSchema("PUBLIC");
         config.setDbTablePattern("DEPARTMENTS,EMPLOYEES,ORGANIZATIONS");
-        parser = new CodeGenParser(config);
     }
 
     @Test
     public void testLoadDbModel() {
-        final DBDatabase db = parser.loadDbModel();
-
+        
+        DBDatabase db = codeGen.parseModel(config);
+        
         final DBTable departments = db.getTable("DEPARTMENTS");
         final DBTable employees = db.getTable("EMPLOYEES");
 
diff --git a/empire-db-codegen/src/test/resources/testconfig.xml 
b/empire-db-codegen/src/test/resources/testconfig.xml
index 1d45a32..b793faf 100644
--- a/empire-db-codegen/src/test/resources/testconfig.xml
+++ b/empire-db-codegen/src/test/resources/testconfig.xml
@@ -26,6 +26,9 @@
                <jdbcUser>sa</jdbcUser>
                <jdbcPwd></jdbcPwd>
                <packageName>org.apache.empire.db.example</packageName>
+
+               <!-- Empire-db DBMS Handler class -->
+               
<dbmsHandlerClass>org.apache.empire.dbms.hsql.DBMSHandlerHSql</dbmsHandlerClass>
                
                <!-- Schema options -->
                <dbCatalog></dbCatalog>
@@ -47,6 +50,10 @@
                <nestViews>false</nestViews>
                <createRecordProperties>true</createRecordProperties>
        </properties>
+
+       <dbmsHandlerClass-properties>
+               <!-- add handler properties here (if any) -->
+       </dbmsHandlerClass-properties>
        
        <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/";>
 
@@ -59,7 +66,7 @@
                </appender>
        
                <!-- log detail configuration -->
-               <logger name="org.apache.empire.commons" additivity="false">
+               <logger name="org.apache.empire.dbms" additivity="false">
                        <level value="warn"/>
                        <appender-ref ref="default"/>
                </logger>
diff --git a/empire-db-examples/empire-db-example-codegen/generate-config.xml 
b/empire-db-examples/empire-db-example-codegen/generate-config.xml
index 241e6a4..0b819b5 100644
--- a/empire-db-examples/empire-db-example-codegen/generate-config.xml
+++ b/empire-db-examples/empire-db-example-codegen/generate-config.xml
@@ -25,6 +25,9 @@
                
<jdbcURL>jdbc:hsqldb:file:src/test/resources/hsqldb/sample;shutdown=true</jdbcURL>
                <jdbcUser>sa</jdbcUser>
                <jdbcPwd></jdbcPwd>
+               
+               <!-- Empire-db DBMS Handler class -->
+               
<dbmsHandlerClass>org.apache.empire.dbms.hsql.DBMSHandlerHSql</dbmsHandlerClass>
 
                <!-- Schema options -->
                <dbCatalog></dbCatalog>
diff --git a/empire-db-examples/empire-db-example-codegen/generate-example.xml 
b/empire-db-examples/empire-db-example-codegen/generate-example.xml
index 52e5e4e..3f03783 100644
--- a/empire-db-examples/empire-db-example-codegen/generate-example.xml
+++ b/empire-db-examples/empire-db-example-codegen/generate-example.xml
@@ -20,35 +20,34 @@
 <config>
 
        <properties>
-               <!-- provider name must match the property-section containing 
the connection data -->
+               <!-- JDBC-Connection to connect to server -->
                <jdbcClass>org.hsqldb.jdbc.JDBCDriver</jdbcClass>
                
<jdbcURL>jdbc:hsqldb:file:src/test/resources/hsqldb/sample;shutdown=true</jdbcURL>
                <jdbcUser>sa</jdbcUser>
                <jdbcPwd></jdbcPwd>
+               
+               <!-- Empire-db DBMS Handler class -->
+               
<dbmsHandlerClass>org.apache.empire.dbms.hsql.DBMSHandlerHSql</dbmsHandlerClass>
 
                <!-- Schema options -->
                <dbCatalog></dbCatalog>
                <dbSchema></dbSchema>
                <dbTablePattern></dbTablePattern>
-               <timestampColumn>UPDATE_TIMESTAMP</timestampColumn>
                
-               <!-- generation options -->
-               <!-- 
-               <targetFolder>target/generated-sources/java</targetFolder>
-                -->
+               <!-- generation options (required) -->
                <targetFolder>src/main/java</targetFolder>
-               <packageName>org.apache.empire.example.db</packageName>
-               <dbClassName>CarSalesDB</dbClassName>
                <nestTables>true</nestTables>
                <nestViews>false</nestViews>
                <createRecordProperties>true</createRecordProperties>
                <preserverCharacterCase>false</preserverCharacterCase>
                <preserveRelationNames>false</preserveRelationNames>
+               <packageName>org.apache.empire.example.db</packageName>
                
                <!-- generation options (optional) --> 
                <tablePackageName></tablePackageName>
                <viewPackageName></viewPackageName>
                <recordPackageName></recordPackageName>
+               <dbClassName>CarSalesDB</dbClassName>
                <tableBaseName>CarSalesTable</tableBaseName>
                <viewBaseName>CarSalesView</viewBaseName>
                <recordBaseName>CarSalesRecord</recordBaseName>
@@ -59,9 +58,14 @@
                <viewClassPrefix></viewClassPrefix>
                <viewClassSuffix></viewClassSuffix>
                <columnNamePrefix></columnNamePrefix>
+               <timestampColumn>UPDATE_TIMESTAMP</timestampColumn>
                
        </properties>
        
+       <dbmsHandlerClass-properties>
+               <!-- add handler properties here (if any) -->
+       </dbmsHandlerClass-properties>
+       
        <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/";>
 
                <!-- Console -->
diff --git a/empire-db-examples/empire-db-example-codegen/pom.xml 
b/empire-db-examples/empire-db-example-codegen/pom.xml
index 9d45686..ecc3f2e 100644
--- a/empire-db-examples/empire-db-example-codegen/pom.xml
+++ b/empire-db-examples/empire-db-example-codegen/pom.xml
@@ -78,6 +78,7 @@
                                        
<jdbcURL>jdbc:hsqldb:file:${project.basedir}/src/test/resources/hsqldb/sample;shutdown=true</jdbcURL>
                                        <jdbcUser>sa</jdbcUser>
                                        <jdbcPwd></jdbcPwd>
+                                       
<dbmsHandlerClass>org.apache.empire.dbms.hsql.DBMSHandlerHSql</dbmsHandlerClass>
                                        
<packageName>org.apache.empire.db.example</packageName>
                                        <dbClassName>MyDatabase</dbClassName>
                                        -->
diff --git 
a/empire-db-maven-plugin/src/main/java/org/apache/empire/db/maven/CodeGenMojo.java
 
b/empire-db-maven-plugin/src/main/java/org/apache/empire/db/maven/CodeGenMojo.java
index cf9cfdd..6b2a820 100644
--- 
a/empire-db-maven-plugin/src/main/java/org/apache/empire/db/maven/CodeGenMojo.java
+++ 
b/empire-db-maven-plugin/src/main/java/org/apache/empire/db/maven/CodeGenMojo.java
@@ -20,10 +20,8 @@ package org.apache.empire.db.maven;
 
 import java.io.File;
 
-import org.apache.empire.db.DBDatabase;
 import org.apache.empire.db.codegen.CodeGenConfig;
-import org.apache.empire.db.codegen.CodeGenParser;
-import org.apache.empire.db.codegen.CodeGenWriter;
+import org.apache.empire.db.codegen.CodeGenerator;
 import org.apache.empire.exceptions.InvalidPropertyException;
 import org.apache.log4j.AppenderSkeleton;
 import org.apache.log4j.Level;
@@ -87,6 +85,12 @@ public class CodeGenMojo extends AbstractMojo {
         */
     @Parameter(property = "empiredb.jdbcPwd")
        private String jdbcPwd;
+    
+    /**
+     * DBMSHandler class name
+     */
+    @Parameter(property = "empiredb.dbmsHandlerClass")
+    private String dbmsHandlerClass;
        
        /**
         * Code generator template directory, if not set the default templates
@@ -147,6 +151,7 @@ public class CodeGenMojo extends AbstractMojo {
                        config.setJdbcClass(jdbcClass);
                        config.setJdbcUser(jdbcUser);
                        config.setJdbcPwd(jdbcPwd);
+                       config.setDbmsHandlerClass(dbmsHandlerClass);
                        
config.setTargetFolder(targetDirectory.getAbsolutePath());
                        config.setTemplateFolder(templateDirectory);
                        config.setPackageName(packageName);
@@ -162,11 +167,9 @@ public class CodeGenMojo extends AbstractMojo {
                
                getLog().info("Generating code for " + jdbcURL + " ...");
                
-               CodeGenParser parser = new CodeGenParser(config);
-               DBDatabase db = parser.loadDbModel();
-               
-               CodeGenWriter codeGen = new CodeGenWriter(config);
-               codeGen.generateCodeFiles(db);
+               // generate now
+               CodeGenerator codeGen = new CodeGenerator();
+               codeGen.generate(config);
                
                getLog().info("Code successfully generated in: " + 
targetDirectory);
                
diff --git a/empire-db/src/main/java/org/apache/empire/db/DBUtils.java 
b/empire-db/src/main/java/org/apache/empire/db/DBUtils.java
index 92b1890..2e1fda8 100644
--- a/empire-db/src/main/java/org/apache/empire/db/DBUtils.java
+++ b/empire-db/src/main/java/org/apache/empire/db/DBUtils.java
@@ -95,7 +95,7 @@ public class DBUtils implements DBContextAware
             int affected = dbms.executeSQL(sqlCmd, sqlParams, 
context.getConnection(), setGenKeys);
             // number of affected records
             if (affected < 0)
-                throw new UnexpectedReturnValueException(affected, 
"dbms.executeSQL()");
+                log.warn("Unexpected return value {} from 
dbms.executeSQL(\"{}\")", affected, sqlCmd);
             // Log
             long execTime = (System.currentTimeMillis() - start);
             if (log.isInfoEnabled())
diff --git 
a/empire-db/src/main/java/org/apache/empire/db/validation/DBModelChecker.java 
b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelChecker.java
index ad09038..817c8bd 100644
--- 
a/empire-db/src/main/java/org/apache/empire/db/validation/DBModelChecker.java
+++ 
b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelChecker.java
@@ -19,30 +19,15 @@
 package org.apache.empire.db.validation;
 
 import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Types;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
-import org.apache.empire.commons.StringUtils;
-import org.apache.empire.data.DataType;
 import org.apache.empire.db.DBColumn;
-import org.apache.empire.db.DBCommandExpr;
 import org.apache.empire.db.DBDatabase;
 import org.apache.empire.db.DBRelation;
 import org.apache.empire.db.DBRelation.DBReference;
-import org.apache.empire.db.DBRowSet;
 import org.apache.empire.db.DBTable;
 import org.apache.empire.db.DBTableColumn;
 import org.apache.empire.db.DBView;
-import org.apache.empire.exceptions.InternalException;
-import org.apache.empire.exceptions.NotSupportedException;
 import org.apache.empire.exceptions.ObjectNotValidException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -50,66 +35,19 @@ import org.slf4j.LoggerFactory;
 public class DBModelChecker
 {
     private static final Logger log = 
LoggerFactory.getLogger(DBModelChecker.class);
-
-    /**
-     * The remote Database
-     * This will be populated by the ModelChecker
-     */
-    private static class RemoteDatabase extends DBDatabase
-    {
-        /*
-         * Will be dynamically populated
-         */
-    }
     
-    private static class RemoteView extends DBView
-    {
-        public RemoteView(String name, DBDatabase db)
-        {
-            super(name, db);
-        }
-
-        public DBColumn addColumn(String columnName, DataType dataType, double 
size, boolean dummy)
-        {
-            return super.addColumn(columnName, dataType, size);
-        }
-        
-        @Override
-        public DBCommandExpr createCommand()
-        {
-            throw new NotSupportedException(this, "createCommand");
-        }
-    }
-
-    protected final String catalog;
-    protected final String schemaPattern;
-
-    protected final String remoteName;
-    protected DBDatabase remoteDb = null;  /* will be recreated on every call 
to checkModel */
+    protected final DBModelParser modelParser;
 
-    protected final Map<String, DBRowSet> tableMap = new HashMap<String, 
DBRowSet>();
-    
+    protected DBDatabase remoteDb = null; 
+            
     /**
      * Creates a new Model Checker
      * @param catalog
      * @param schemaPattern
      */
-    public DBModelChecker(String catalog, String schemaPattern)
+    public DBModelChecker(DBModelParser modelParser)
     {
-        this.catalog = catalog;
-        this.schemaPattern = schemaPattern;
-        // set origin
-        StringBuilder b = new StringBuilder();
-        if (StringUtils.isNotEmpty(catalog))
-            b.append(catalog);
-        if (StringUtils.isNotEmpty(schemaPattern))
-        {   if (b.length()>0)
-                b.append(".");
-            b.append(schemaPattern);
-        }
-        if (b.length()==0)
-            b.append("[Unknown]");
-        this.remoteName = b.toString();
+        this.modelParser = modelParser;
     }
 
     /**
@@ -131,256 +69,12 @@ public class DBModelChecker
     public void checkModel(DBDatabase db, Connection conn, DBModelErrorHandler 
handler)
     {
         // parse first
-        parseModel(conn);
+        modelParser.parseModel(conn);
+        // set remote
+        this.remoteDb = modelParser.getDatabase();
         // check database
         checkRemoteAgainst(db, handler);
     }
-    
-    /**
-     * This method is used to parse the populate the remote database
-     * @param conn the connection for retrieving the remote database metadata
-     */
-    public void parseModel(Connection conn)
-    {
-        try
-        {   // create remote db instance
-            remoteDb = createRemoteDatabase();
-            // populate
-            DatabaseMetaData dbMeta = conn.getMetaData();
-            populateRemoteDatabase(dbMeta);
-        }
-        catch (SQLException e)
-        {
-            log.error("checkModel failed for {}", remoteName);
-            throw new InternalException(e);
-        }
-        finally 
-        {   // cleanup
-            tableMap.clear();            
-        }
-    }
-
-    protected void populateRemoteDatabase(DatabaseMetaData dbMeta)
-        throws SQLException
-    {
-        // collect tables & views
-        int count = collectTablesAndViews(dbMeta, null);
-        log.info("{} tables and views added for schema \"{}\"", count, 
remoteName);
-
-        // Collect all columns
-        count = collectColumns(dbMeta);
-        log.info("{} columns added for schema \"{}\"", count, remoteName);
-
-        // Collect PKs
-        count = collectPrimaryKeys(dbMeta);
-        log.info("{} primary keys added for schema \"{}\"", count, remoteName);
-
-        // Collect FKs
-        count = collectForeignKeys(dbMeta);
-        log.info("{} foreign keys added for schema \"{}\"", count, remoteName);
-    }
-
-    /**
-     * Checks if the tableName belongs to a system or hidden table
-     * @param tableName the table name
-     * @param tableMeta the table meta
-     * @return true if the table is hidden or false otherwise
-     */
-    protected boolean isSystemTable(String tableName, ResultSet tableMeta)
-    {   // system tables containing a '$' symbol (required for Oracle!)
-        return (tableName.indexOf('$') >= 0);
-    }
-    
-    /**
-     * collects table and view information from database meta data
-     * @param dbMeta
-     * @param dbSchema
-     * @throws SQLException
-     */
-    protected int collectTablesAndViews(DatabaseMetaData dbMeta, String 
tablePattern)
-        throws SQLException
-    {
-        tableMap.clear();
-        ResultSet dbTables = dbMeta.getTables(catalog, schemaPattern, 
tablePattern, new String[] { "TABLE", "VIEW" });
-        try {
-            // ResultSet dbTables = dbMeta.getTables("PATOOL", "DBO", null, 
new String[] { "TABLE", "VIEW" });
-            int count = 0;
-            while (dbTables.next())
-            {
-                String tableName = dbTables.getString("TABLE_NAME");
-                String tableType = dbTables.getString("TABLE_TYPE");
-                if (isSystemTable(tableName, dbTables))
-                {   // ignore system table
-                    DBModelChecker.log.info("Ignoring system table " + 
tableName);
-                    continue;
-                }
-                if ("VIEW".equalsIgnoreCase(tableType))
-                    addView(tableName);
-                else
-                    addTable(tableName);
-                count++;
-            }
-            return count;
-        } finally {
-            dbTables.close();
-        }
-    }
-
-    /**
-     * collects column information from database meta data for each table
-     */
-    protected int collectColumns(DatabaseMetaData dbMeta)
-            throws SQLException
-    {
-        int count = 0;
-        for (DBRowSet t : getTables())
-        {
-            ResultSet dbColumns = dbMeta.getColumns(catalog, schemaPattern, 
t.getName(), null);
-            try {
-                while (dbColumns.next())
-                {   // add the column
-                    addColumn(t, dbColumns);
-                    count++;
-                }
-            } finally {
-                dbColumns.close();
-            }
-        }
-        return count;
-    }
-
-    /**
-     * collects column information from database meta data for whole schema
-     */
-    protected int collectColumns(DatabaseMetaData dbMeta, String tablePattern)
-        throws SQLException
-    {
-        ResultSet dbColumns = dbMeta.getColumns(catalog, schemaPattern, 
tablePattern, null);
-        try {
-            int count = 0;
-            while (dbColumns.next())
-            {
-                String tableName = dbColumns.getString("TABLE_NAME");
-                DBRowSet t = getTable(tableName);
-                if (t == null)
-                {   log.error("Table not found: {}", tableName);
-                    continue;
-                }
-                addColumn(t, dbColumns);
-                count++;
-            }
-            return count;
-        } finally {
-            dbColumns.close();
-        }
-    }
-    
-    /**
-     * collects primary key information from database meta data
-     * @param dbMeta
-     * @param dbSchema
-     * @throws SQLException
-     */
-    protected int collectPrimaryKeys(DatabaseMetaData dbMeta)
-        throws SQLException
-    {
-        int count = 0;
-        for (DBRowSet rs : getTables())
-        {
-            if (!(rs instanceof DBTable))
-                continue; // not a table
-            // read pk
-            DBTable t = (DBTable)rs;
-            List<String> pkCols = new ArrayList<String>();
-            ResultSet primaryKeys = dbMeta.getPrimaryKeys(catalog, 
schemaPattern, t.getName());
-            try {
-                while (primaryKeys.next())
-                {
-                    pkCols.add(primaryKeys.getString("COLUMN_NAME"));
-                }
-                if (pkCols.size() > 0)
-                {
-                    DBColumn[] keys = new DBColumn[pkCols.size()];
-                    for (int i = 0; i < keys.length; i++)
-                    {
-                        keys[i] = t.getColumn(pkCols.get(i).toUpperCase());
-                    }
-                    t.setPrimaryKey(keys);
-                    count++;
-                }
-            } finally {
-                primaryKeys.close();
-            }
-        }
-        return count;
-    }
-
-    /**
-     * collects foreign key information from database meta data
-     * @throws SQLException
-     */
-    protected int collectForeignKeys(DatabaseMetaData dbMeta)
-            throws SQLException
-    {
-        int count = 0;
-        for (DBRowSet t : getTables())
-        {
-            if (t instanceof DBTable)
-                count += collectForeignKeys(dbMeta, t.getName());
-        }
-        return count;
-    }
-    
-    /**
-     * collects foreign key information from database meta data
-     * @throws SQLException
-     */
-    protected int collectForeignKeys(DatabaseMetaData dbMeta, String 
tablePattern)
-        throws SQLException
-    {
-        ResultSet foreignKeys = dbMeta.getImportedKeys(catalog, schemaPattern, 
tablePattern);
-        try {
-            int count = 0;
-            while (foreignKeys.next())
-            {
-                String fkTable = foreignKeys.getString("FKTABLE_NAME");
-                String fkColumn = foreignKeys.getString("FKCOLUMN_NAME");
-
-                String pkTable = foreignKeys.getString("PKTABLE_NAME");
-                String pkColumn = foreignKeys.getString("PKCOLUMN_NAME");
-
-                String fkName = foreignKeys.getString("FK_NAME");
-
-                DBTableColumn c1 = (DBTableColumn) 
getTable(fkTable).getColumn(fkColumn.toUpperCase());
-                DBTableColumn c2 = (DBTableColumn) 
getTable(pkTable).getColumn(pkColumn.toUpperCase());
-
-                DBRelation relation = this.remoteDb.getRelation(fkName);
-                if (relation == null)
-                {
-                    addRelation(fkName, c1.referenceOn(c2));
-                    count++;
-                }
-                else
-                {   // get existing references
-                    DBReference[] refs = relation.getReferences();
-                    // remove old
-                    this.remoteDb.removeRelation(relation);
-                    DBReference[] newRefs = new DBReference[refs.length + 1];
-                    // copy existing
-                    DBReference newRef = new DBReference(c1, c2);
-                    for (int i = 0; i < refs.length; i++)
-                    {
-                        newRefs[i] = refs[i];
-                    }
-                    newRefs[newRefs.length - 1] = newRef;
-                    addRelation(fkName, newRefs);
-                }
-            }
-            return count;
-        } finally {
-            foreignKeys.close();
-        }
-    }
 
     /**
      * Check the remote database against an existing model
@@ -715,194 +409,4 @@ public class DBModelChecker
         checkGenericColumn(column, remoteColumn, handler);
     }
 
-    /*
-     * internal methods
-     */
-    protected final Collection<DBRowSet> getTables()
-    {
-        return this.tableMap.values();
-    }
-    
-    protected final DBRowSet getTable(String tableName)
-    {
-        return this.tableMap.get(tableName.toUpperCase());
-    }
-
-    protected DBDatabase createRemoteDatabase()
-    {
-        return new RemoteDatabase();
-    }
-    
-    protected void addTable(String tableName)
-    {
-        this.tableMap.put(tableName.toUpperCase(), new DBTable(tableName, 
this.remoteDb));
-    }
-    
-    protected void addView(String viewName)
-    {
-        this.tableMap.put(viewName.toUpperCase(), new RemoteView(viewName, 
this.remoteDb));
-    }
-    
-    protected void addRelation(String relName, DBReference... references)
-    {
-        this.remoteDb.addRelation(relName, references);
-    }
-    
-    protected DBColumn addColumn(DBRowSet t, ResultSet rs)
-        throws SQLException
-    {
-        String name = rs.getString("COLUMN_NAME");
-        DataType empireType = getEmpireDataType(rs.getInt("DATA_TYPE"));
-
-        double colSize = rs.getInt("COLUMN_SIZE");
-        if (empireType == DataType.DECIMAL || empireType == DataType.FLOAT)
-        { // decimal digits
-            int decimalDig = rs.getInt("DECIMAL_DIGITS");
-            if (decimalDig > 0)
-            {   try
-                {   // concat and parse
-                    int intSize = rs.getInt("COLUMN_SIZE");
-                    colSize = Double.parseDouble(String.valueOf(intSize) + '.' 
+ decimalDig);
-                }
-                catch (Exception e)
-                {
-                    DBModelChecker.log.error("Failed to parse decimal digits 
for column " + name);
-                }
-            }
-            // make integer?
-            if (colSize < 1.0d)
-            { // Turn into an integer
-                empireType = DataType.INTEGER;
-            }
-        } 
-        else if (empireType == DataType.INTEGER || empireType == DataType.CLOB 
|| empireType == DataType.BLOB)
-        {
-            colSize = 0.0;
-        }
-
-        // mandatory field?
-        boolean required = false;
-        String defaultValue = rs.getString("COLUMN_DEF");
-        if (rs.getString("IS_NULLABLE").equalsIgnoreCase("NO"))
-        {
-            required = true;
-        }
-
-        // The following is a hack for MySQL which currently gets sent a 
string "CURRENT_TIMESTAMP" from the Empire-db dbms for MySQL.
-        // This will avoid the dbms problem because CURRENT_TIMESTAMP in the 
db will just do the current datetime.
-        // Essentially, Empire-db needs the concept of default values of one 
type that get mapped to another.
-        // In this case, MySQL "CURRENT_TIMESTAMP" for Types.TIMESTAMP needs 
to emit from the Empire-db dbms the null value and not "CURRENT_TIMESTAMP".
-        if (rs.getInt("DATA_TYPE") == Types.TIMESTAMP && defaultValue != null 
&& defaultValue.equals("CURRENT_TIMESTAMP"))
-        {
-            required = false; // It is in fact not required even though MySQL 
schema is required because it has a default value. Generally, should Empire-db 
emit (required && defaultValue != null) to truly determine if a column is 
required?
-            defaultValue = null; // If null (and required per schema?) MySQL 
will apply internal default value.
-        }
-
-        // AUTOINC indicator is not in java.sql.Types but rather meta data 
from DatabaseMetaData.getColumns()
-        // getEmpireDataType() above is not enough to support AUTOINC as it 
will only return DataType.INTEGER
-        DataType originalType = empireType;
-        ResultSetMetaData metaData = rs.getMetaData();
-        int colCount = metaData.getColumnCount();
-        String colName;
-        for (int i = 1; i <= colCount; i++)
-        {
-            colName = metaData.getColumnName(i);
-            // MySQL matches on IS_AUTOINCREMENT column.
-            // SQL Server matches on TYPE_NAME column with identity somewhere 
in the string value.
-            if ((colName.equalsIgnoreCase("IS_AUTOINCREMENT") && 
rs.getString(i).equalsIgnoreCase("YES"))
-                || (colName.equals("TYPE_NAME") && 
rs.getString(i).matches(".*(?i:identity).*")))
-            {
-                empireType = DataType.AUTOINC;
-
-            }
-        }
-
-        // Move from the return statement below so we can add
-        // some AUTOINC meta data to the column to be used by
-        // the ParserUtil and ultimately the template.
-        //        DBModelChecker.log.info("\tCOLUMN:\t" + name + " (" + 
empireType + ")");
-        DBColumn col;
-        if (t instanceof DBTable)
-        {
-            col = ((DBTable)t).addColumn(name, empireType, colSize, required, 
defaultValue);
-            // We still need to know the base data type for this AUTOINC
-            // because the Record g/setters need to know this, right?
-            // So, let's add it as meta data every time the column is AUTOINC
-            // and reference it in the template.
-            if (empireType.equals(DataType.AUTOINC))
-            {
-                col.setAttribute("AutoIncDataType", originalType);
-            }
-        }
-        else if (t instanceof RemoteView)
-        {
-           col = ((RemoteView)t).addColumn(name, empireType, colSize, false);
-        }
-        else
-        {   // Unknown type
-            log.error("Unknown Object Type {}", t.getClass().getName());
-            col = null;
-        }
-        // done
-        return col;
-
-    }
-
-    protected DataType getEmpireDataType(int sqlType)
-    {
-        DataType empireType = DataType.UNKNOWN;
-        switch (sqlType)
-        {
-            case Types.INTEGER:
-            case Types.SMALLINT:
-            case Types.TINYINT:
-            case Types.BIGINT:
-                empireType = DataType.INTEGER;
-                break;
-            case Types.VARCHAR:
-            case Types.NVARCHAR:
-                empireType = DataType.VARCHAR;
-                break;
-            case Types.DATE:
-                empireType = DataType.DATE;
-                break;
-            case Types.TIMESTAMP:
-            case Types.TIME:
-                empireType = DataType.DATETIME;
-                break;
-            case Types.CHAR:
-            case Types.NCHAR:
-                empireType = DataType.CHAR;
-                break;
-            case Types.DOUBLE:
-            case Types.FLOAT:
-            case Types.REAL:
-                empireType = DataType.FLOAT;
-                break;
-            case Types.DECIMAL:
-            case Types.NUMERIC:
-                empireType = DataType.DECIMAL;
-                break;
-            case Types.BIT:
-            case Types.BOOLEAN:
-                empireType = DataType.BOOL;
-                break;
-            case Types.CLOB:
-            case Types.LONGVARCHAR:
-            case Types.LONGNVARCHAR:
-                empireType = DataType.CLOB;
-                break;
-            case Types.BINARY:
-            case Types.VARBINARY:
-            case Types.LONGVARBINARY:
-            case Types.BLOB:
-                empireType = DataType.BLOB;
-                break;
-            default:
-                empireType = DataType.UNKNOWN;
-                DBModelChecker.log.warn("SQL column type " + sqlType + " not 
supported.");
-        }
-        DBModelChecker.log.debug("Mapping date type " + 
String.valueOf(sqlType) + " to " + empireType);
-        return empireType;
-    }
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/db/validation/DBModelParser.java 
b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelParser.java
new file mode 100644
index 0000000..b2492ef
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelParser.java
@@ -0,0 +1,584 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.db.validation;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBCommandExpr;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBRelation;
+import org.apache.empire.db.DBRelation.DBReference;
+import org.apache.empire.db.DBRowSet;
+import org.apache.empire.db.DBTable;
+import org.apache.empire.db.DBTableColumn;
+import org.apache.empire.db.DBView;
+import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.exceptions.NotSupportedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DBModelParser
+{
+    protected static final Logger log = 
LoggerFactory.getLogger(DBModelParser.class);
+
+    /**
+     * The remote Database
+     * This will be populated by the ModelChecker
+     */
+    private static class RemoteDatabase extends DBDatabase
+    {
+        /*
+         * Will be dynamically populated
+         */
+    }
+    
+    private static class RemoteView extends DBView
+    {
+        public RemoteView(String name, DBDatabase db)
+        {
+            super(name, db);
+        }
+
+        public DBColumn addColumn(String columnName, DataType dataType, double 
size, boolean dummy)
+        {
+            return super.addColumn(columnName, dataType, size);
+        }
+        
+        @Override
+        public DBCommandExpr createCommand()
+        {
+            throw new NotSupportedException(this, "createCommand");
+        }
+    }
+
+    protected final String catalog;
+    protected final String schemaPattern;
+
+    protected final String remoteName;
+    protected DBDatabase remoteDb = null;  /* will be recreated on every call 
to checkModel */
+
+    protected final Map<String, DBRowSet> tableMap = new HashMap<String, 
DBRowSet>();
+    
+    private String standardIdentityColumnName  = null;
+    private String standardTimestampColumnName = null;
+    
+    /**
+     * Creates a new Model Checker
+     * @param catalog
+     * @param schemaPattern
+     */
+    public DBModelParser(String catalog, String schemaPattern)
+    {
+        this.catalog = catalog;
+        this.schemaPattern = schemaPattern;
+        // set origin
+        StringBuilder b = new StringBuilder();
+        if (StringUtils.isNotEmpty(catalog))
+            b.append(catalog);
+        if (StringUtils.isNotEmpty(schemaPattern))
+        {   if (b.length()>0)
+                b.append(".");
+            b.append(schemaPattern);
+        }
+        if (b.length()==0)
+            b.append("[Unknown]");
+        this.remoteName = b.toString();
+    }
+
+    public void setStandardIdentityColumnName(String 
standardIdentityColumnName)
+    {
+        this.standardIdentityColumnName = standardIdentityColumnName;
+    }
+
+    public void setStandardTimestampColumnName(String 
standardTimestampColumnName)
+    {
+        this.standardTimestampColumnName = standardTimestampColumnName;
+    }
+
+    /**
+     * Returns the RemoteDatabase
+     * Only available after parseModel() is called 
+     * @return the remote Database
+     */
+    public DBDatabase getDatabase()
+    {
+        return remoteDb;
+    }
+    
+    /**
+     * This method is used to parse the populate the remote database
+     * @param conn the connection for retrieving the remote database metadata
+     */
+    public void parseModel(Connection conn)
+    {
+        try
+        {   // create remote db instance
+            remoteDb = createRemoteDatabase();
+            // populate
+            DatabaseMetaData dbMeta = conn.getMetaData();
+            populateRemoteDatabase(dbMeta);
+        }
+        catch (SQLException e)
+        {
+            log.error("checkModel failed for {}", remoteName);
+            throw new InternalException(e);
+        }
+        finally 
+        {   // cleanup
+            tableMap.clear();            
+        }
+    }
+
+    protected void populateRemoteDatabase(DatabaseMetaData dbMeta)
+        throws SQLException
+    {
+        // collect tables & views
+        int count = collectTablesAndViews(dbMeta, null);
+        log.info("{} tables and views added for schema \"{}\"", count, 
remoteName);
+
+        // Collect all columns
+        count = collectColumns(dbMeta);
+        log.info("{} columns added for schema \"{}\"", count, remoteName);
+
+        // Collect PKs
+        count = collectPrimaryKeys(dbMeta);
+        log.info("{} primary keys added for schema \"{}\"", count, remoteName);
+
+        // Collect FKs
+        count = collectForeignKeys(dbMeta);
+        log.info("{} foreign keys added for schema \"{}\"", count, remoteName);
+    }
+
+    /**
+     * Checks if the tableName belongs to a system or hidden table
+     * @param tableName the table name
+     * @param tableMeta the table meta
+     * @return true if the table is hidden or false otherwise
+     */
+    protected boolean isSystemTable(String tableName, ResultSet tableMeta)
+    {   // system tables containing a '$' symbol (required for Oracle!)
+        return (tableName.indexOf('$') >= 0);
+    }
+    
+    /**
+     * collects table and view information from database meta data
+     * @param dbMeta
+     * @param dbSchema
+     * @throws SQLException
+     */
+    protected int collectTablesAndViews(DatabaseMetaData dbMeta, String 
tablePattern)
+        throws SQLException
+    {
+        tableMap.clear();
+        ResultSet dbTables = dbMeta.getTables(catalog, schemaPattern, 
tablePattern, new String[] { "TABLE", "VIEW" });
+        try {
+            // ResultSet dbTables = dbMeta.getTables("PATOOL", "DBO", null, 
new String[] { "TABLE", "VIEW" });
+            int count = 0;
+            while (dbTables.next())
+            {
+                String tableName = dbTables.getString("TABLE_NAME");
+                String tableType = dbTables.getString("TABLE_TYPE");
+                if (isSystemTable(tableName, dbTables))
+                {   // ignore system table
+                    DBModelParser.log.info("Ignoring system table " + 
tableName);
+                    continue;
+                }
+                if ("VIEW".equalsIgnoreCase(tableType))
+                    addView(tableName);
+                else
+                    addTable(tableName);
+                count++;
+            }
+            return count;
+        } finally {
+            dbTables.close();
+        }
+    }
+
+    /**
+     * collects column information from database meta data for each table
+     */
+    protected int collectColumns(DatabaseMetaData dbMeta)
+            throws SQLException
+    {
+        int count = 0;
+        for (DBRowSet t : getTables())
+        {
+            ResultSet dbColumns = dbMeta.getColumns(catalog, schemaPattern, 
t.getName(), null);
+            try {
+                while (dbColumns.next())
+                {   // add the column
+                    addColumn(t, dbColumns);
+                    count++;
+                }
+            } finally {
+                dbColumns.close();
+            }
+        }
+        return count;
+    }
+
+    /**
+     * collects column information from database meta data for whole schema
+     */
+    protected int collectColumns(DatabaseMetaData dbMeta, String tablePattern)
+        throws SQLException
+    {
+        ResultSet dbColumns = dbMeta.getColumns(catalog, schemaPattern, 
tablePattern, null);
+        try {
+            int count = 0;
+            while (dbColumns.next())
+            {
+                String tableName = dbColumns.getString("TABLE_NAME");
+                DBRowSet t = getTable(tableName);
+                if (t == null)
+                {   log.error("Table not found: {}", tableName);
+                    continue;
+                }
+                addColumn(t, dbColumns);
+                count++;
+            }
+            return count;
+        } finally {
+            dbColumns.close();
+        }
+    }
+    
+    /**
+     * collects primary key information from database meta data
+     * @param dbMeta
+     * @param dbSchema
+     * @throws SQLException
+     */
+    protected int collectPrimaryKeys(DatabaseMetaData dbMeta)
+        throws SQLException
+    {
+        int count = 0;
+        for (DBRowSet rs : getTables())
+        {
+            if (!(rs instanceof DBTable))
+                continue; // not a table
+            // read pk
+            DBTable t = (DBTable)rs;
+            List<String> pkCols = new ArrayList<String>();
+            ResultSet primaryKeys = dbMeta.getPrimaryKeys(catalog, 
schemaPattern, t.getName());
+            try {
+                while (primaryKeys.next())
+                {
+                    pkCols.add(primaryKeys.getString("COLUMN_NAME"));
+                }
+                if (pkCols.size() > 0)
+                {
+                    DBColumn[] keys = new DBColumn[pkCols.size()];
+                    for (int i = 0; i < keys.length; i++)
+                    {
+                        keys[i] = t.getColumn(pkCols.get(i).toUpperCase());
+                    }
+                    t.setPrimaryKey(keys);
+                    count++;
+                }
+            } finally {
+                primaryKeys.close();
+            }
+        }
+        return count;
+    }
+
+    /**
+     * collects foreign key information from database meta data
+     * @throws SQLException
+     */
+    protected int collectForeignKeys(DatabaseMetaData dbMeta)
+            throws SQLException
+    {
+        int count = 0;
+        for (DBRowSet t : getTables())
+        {
+            if (t instanceof DBTable)
+                count += collectForeignKeys(dbMeta, t.getName());
+        }
+        return count;
+    }
+    
+    /**
+     * collects foreign key information from database meta data
+     * @throws SQLException
+     */
+    protected int collectForeignKeys(DatabaseMetaData dbMeta, String 
tablePattern)
+        throws SQLException
+    {
+        ResultSet foreignKeys = dbMeta.getImportedKeys(catalog, schemaPattern, 
tablePattern);
+        try {
+            int count = 0;
+            while (foreignKeys.next())
+            {
+                String fkTable = foreignKeys.getString("FKTABLE_NAME");
+                String fkColumn = foreignKeys.getString("FKCOLUMN_NAME");
+
+                String pkTable = foreignKeys.getString("PKTABLE_NAME");
+                String pkColumn = foreignKeys.getString("PKCOLUMN_NAME");
+
+                String fkName = foreignKeys.getString("FK_NAME");
+
+                DBTableColumn c1 = (DBTableColumn) 
getTable(fkTable).getColumn(fkColumn.toUpperCase());
+                DBTableColumn c2 = (DBTableColumn) 
getTable(pkTable).getColumn(pkColumn.toUpperCase());
+
+                DBRelation relation = this.remoteDb.getRelation(fkName);
+                if (relation == null)
+                {
+                    addRelation(fkName, c1.referenceOn(c2));
+                    count++;
+                }
+                else
+                {   // get existing references
+                    DBReference[] refs = relation.getReferences();
+                    // remove old
+                    this.remoteDb.removeRelation(relation);
+                    DBReference[] newRefs = new DBReference[refs.length + 1];
+                    // copy existing
+                    DBReference newRef = new DBReference(c1, c2);
+                    for (int i = 0; i < refs.length; i++)
+                    {
+                        newRefs[i] = refs[i];
+                    }
+                    newRefs[newRefs.length - 1] = newRef;
+                    addRelation(fkName, newRefs);
+                }
+            }
+            return count;
+        } finally {
+            foreignKeys.close();
+        }
+    }
+
+    /*
+     * internal methods
+     */
+    protected final Collection<DBRowSet> getTables()
+    {
+        return this.tableMap.values();
+    }
+    
+    protected final DBRowSet getTable(String tableName)
+    {
+        return this.tableMap.get(tableName.toUpperCase());
+    }
+
+    protected DBDatabase createRemoteDatabase()
+    {
+        return new RemoteDatabase();
+    }
+    
+    protected void addTable(String tableName)
+    {
+        this.tableMap.put(tableName.toUpperCase(), new DBTable(tableName, 
this.remoteDb));
+    }
+    
+    protected void addView(String viewName)
+    {
+        this.tableMap.put(viewName.toUpperCase(), new RemoteView(viewName, 
this.remoteDb));
+    }
+    
+    protected void addRelation(String relName, DBReference... references)
+    {
+        this.remoteDb.addRelation(relName, references);
+    }
+    
+    protected DBColumn addColumn(DBRowSet t, ResultSet rs)
+        throws SQLException
+    {
+        String name = rs.getString("COLUMN_NAME");
+        DataType empireType = getEmpireDataType(rs.getInt("DATA_TYPE"));
+
+        // get Size
+        double colSize = getColumnSize(empireType, rs);
+
+        // mandatory field?
+        boolean required = isColumnRequired(rs);
+        Object defaultValue = getColumnDefault(rs);
+
+        // Now add the column to table / view
+        DBColumn col;
+        if (t instanceof DBTable)
+        {   // check Identity and Timestamp
+            boolean timestampColumn = false;
+            if (empireType==DataType.INTEGER && isIdentityColumn(rs))
+                empireType= DataType.AUTOINC;
+            if (empireType.isDate() && (timestampColumn=isTimestampColumn(rs)))
+                empireType= DataType.TIMESTAMP;
+            // Add Column
+            col = ((DBTable)t).addColumn(name, empireType, colSize, required, 
defaultValue);
+            // Set Timestamp
+            if (empireType==DataType.AUTOINC)
+                ((DBTable) t).setPrimaryKey(col);
+            if (timestampColumn)
+                t.setTimestampColumn(col);
+            // info
+            log.info("Added table column {}.{} of type {}", t.getName(), name, 
empireType);
+        }
+        else if (t instanceof DBView)
+        {
+            col = ((RemoteView)t).addColumn(name, empireType, colSize, false);
+            log.info("Added view column {}.{} of type {}", t.getName(), name, 
empireType);
+        }
+        else
+        {   // Unknown type
+            log.error("Unknown Object Type {}", t.getClass().getName());
+            col = null;
+        }
+        // done
+        return col;
+    }
+    
+    protected double getColumnSize(DataType empireType, ResultSet rs)
+        throws SQLException
+    {
+        double colSize = rs.getInt("COLUMN_SIZE");
+        if (empireType == DataType.DECIMAL || empireType == DataType.FLOAT)
+        { // decimal digits
+            int decimalDig = rs.getInt("DECIMAL_DIGITS");
+            if (decimalDig > 0)
+            {   try
+                {   // concat and parse
+                    int intSize = rs.getInt("COLUMN_SIZE");
+                    colSize = Double.parseDouble(String.valueOf(intSize) + '.' 
+ decimalDig);
+                }
+                catch (Exception e)
+                {
+                    String name = rs.getString("COLUMN_NAME");
+                    DBModelParser.log.error("Failed to parse decimal digits 
for column " + name);
+                }
+            }
+            // make integer?
+            if (colSize < 1.0d)
+            { // Turn into an integer
+                empireType = DataType.INTEGER;
+            }
+        } 
+        else if (empireType.isDate())
+        {
+            colSize = 0.0;
+        }
+        else if (empireType == DataType.INTEGER || empireType == DataType.CLOB 
|| empireType == DataType.BLOB)
+        {
+            colSize = 0.0;
+        }
+        return colSize;
+    }
+    
+    protected boolean isColumnRequired(ResultSet rs)
+        throws SQLException
+    {
+        return rs.getString("IS_NULLABLE").equalsIgnoreCase("NO");
+    }
+    
+    protected Object getColumnDefault(ResultSet rs)
+        throws SQLException
+    {
+        return rs.getString("COLUMN_DEF");
+    }
+    
+    protected boolean isIdentityColumn(ResultSet rs)
+    {   try {
+            return (standardIdentityColumnName!=null &&
+                    
standardIdentityColumnName.equalsIgnoreCase(rs.getString("COLUMN_NAME")));
+        } catch(SQLException e) {
+            return false;
+        }
+    }
+    
+    protected boolean isTimestampColumn(ResultSet rs)
+    {   try {
+            return (standardTimestampColumnName!=null &&
+                    
standardTimestampColumnName.equalsIgnoreCase(rs.getString("COLUMN_NAME")));
+        } catch(SQLException e) {
+            return false;
+        }
+    }
+
+    protected DataType getEmpireDataType(int sqlType)
+    {
+        DataType empireType = DataType.UNKNOWN;
+        switch (sqlType)
+        {
+            case Types.INTEGER:
+            case Types.SMALLINT:
+            case Types.TINYINT:
+            case Types.BIGINT:
+                empireType = DataType.INTEGER;
+                break;
+            case Types.VARCHAR:
+            case Types.NVARCHAR:
+                empireType = DataType.VARCHAR;
+                break;
+            case Types.DATE:
+                empireType = DataType.DATE;
+                break;
+            case Types.TIMESTAMP:
+            case Types.TIME:
+                empireType = DataType.DATETIME;
+                break;
+            case Types.CHAR:
+            case Types.NCHAR:
+                empireType = DataType.CHAR;
+                break;
+            case Types.DOUBLE:
+            case Types.FLOAT:
+            case Types.REAL:
+                empireType = DataType.FLOAT;
+                break;
+            case Types.DECIMAL:
+            case Types.NUMERIC:
+                empireType = DataType.DECIMAL;
+                break;
+            case Types.BIT:
+            case Types.BOOLEAN:
+                empireType = DataType.BOOL;
+                break;
+            case Types.CLOB:
+            case Types.LONGVARCHAR:
+            case Types.LONGNVARCHAR:
+                empireType = DataType.CLOB;
+                break;
+            case Types.BINARY:
+            case Types.VARBINARY:
+            case Types.LONGVARBINARY:
+            case Types.BLOB:
+                empireType = DataType.BLOB;
+                break;
+            default:
+                empireType = DataType.UNKNOWN;
+                DBModelParser.log.warn("SQL column type " + sqlType + " not 
supported.");
+        }
+        DBModelParser.log.debug("Mapping date type " + String.valueOf(sqlType) 
+ " to " + empireType);
+        return empireType;
+    }
+}
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java 
b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java
index 3da3c1e..e3b7cd4 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandler.java
@@ -34,6 +34,7 @@ import org.apache.empire.db.DBRelation;
 import org.apache.empire.db.DBSQLScript;
 import org.apache.empire.db.DBTableColumn;
 import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelParser;
 
 /**
  * The DBMSHandler interface implements all RDBMS specific logic
@@ -240,6 +241,12 @@ public interface DBMSHandler
      * @param script the script to which to add the DDL command(s)
      */
     void appendEnableRelationStmt(DBRelation r, boolean enable, DBSQLScript 
script);
+    
+    /**
+     * Creates a DataModelParser instance of this DBMSHandler
+     * @return
+     */
+    DBModelParser createModelParser(String catalog, String schema);
  
     /**
      * Creates a DataModelChecker instance of this DBMSHandler
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java 
b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java
index d59223d..2f10919 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/DBMSHandlerBase.java
@@ -56,6 +56,7 @@ import org.apache.empire.db.DBTableColumn;
 import org.apache.empire.db.exceptions.EmpireSQLException;
 import org.apache.empire.db.exceptions.QueryFailedException;
 import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelParser;
 import org.apache.empire.exceptions.InvalidArgumentException;
 import org.apache.empire.exceptions.NotSupportedException;
 import org.apache.empire.exceptions.UnexpectedReturnValueException;
@@ -786,6 +787,16 @@ public abstract class DBMSHandlerBase implements 
DBMSHandler
     }
 
     /**
+     * Creates a DataModelParser instance of this DBMSHandler
+     * @return
+     */
+    @Override
+    public DBModelParser createModelParser(String catalog, String schema)
+    {
+        return new DBModelParser(catalog, schema);
+    }
+    
+    /**
      * Creates a DataModelChecker instance of this DBMSHandler
      * @return
      */
@@ -794,7 +805,8 @@ public abstract class DBMSHandlerBase implements DBMSHandler
     {
         log.warn("A general and possibly untested DBModelChecker is used for 
DBMSHandler {}. Please override to inklude DBMS specific features.", 
getClass().getSimpleName());
         // the default model checker
-        return new DBModelChecker(null, null);
+        DBModelParser modelParser = createModelParser(null, db.getSchema());
+        return new DBModelChecker(modelParser);
     }
     
     /**
diff --git a/empire-db/src/main/java/org/apache/empire/dbms/DBSqlPhrase.java 
b/empire-db/src/main/java/org/apache/empire/dbms/DBSqlPhrase.java
index a964f74..1225ab9 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/DBSqlPhrase.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/DBSqlPhrase.java
@@ -61,10 +61,10 @@ public enum DBSqlPhrase
     SQL_FUNC_YEAR           ("year(?)"),                // Oracle: 
extract(year from ?)
 
     // Aggregation
-    SQL_FUNC_SUM            ("sum(?)"),
-    SQL_FUNC_MAX            ("max(?)"),
-    SQL_FUNC_MIN            ("min(?)"),
-    SQL_FUNC_AVG            ("avg(?)"),
+    SQL_FUNC_SUM            ("sum(?)", true),
+    SQL_FUNC_MAX            ("max(?)", true),
+    SQL_FUNC_MIN            ("min(?)", true),
+    SQL_FUNC_AVG            ("avg(?)", true),
 
     // Decode
     SQL_FUNC_DECODE         ("case ? {0} end"),         // Oracle: decode(? 
{0})
@@ -72,15 +72,27 @@ public enum DBSqlPhrase
     SQL_FUNC_DECODE_PART    ("when {0} then {1}"),      // Oracle: {0}, {1}
     SQL_FUNC_DECODE_ELSE    ("else {0}");               // Oracle: {0}
 
-    private String sqlDefault;
+    private final String sqlDefault;
+    private final boolean aggregate;
     
-    private DBSqlPhrase(String sqlDefault)
+    private DBSqlPhrase(String sqlDefault, boolean aggregate)
     {
         this.sqlDefault = sqlDefault;
+        this.aggregate = aggregate;
+    }
+    
+    private DBSqlPhrase(String sqlDefault)
+    {
+        this(sqlDefault, false);
     }
 
     public String getSqlDefault()
     {
         return sqlDefault;
     }
+
+    public boolean isAggregate()
+    {
+        return aggregate;
+    }
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java 
b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java
index 2415305..b4c0825 100644
--- a/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java
+++ b/empire-db/src/main/java/org/apache/empire/dbms/hsql/DBMSHandlerHSql.java
@@ -92,7 +92,7 @@ public class DBMSHandlerHSql extends DBMSHandlerBase
         switch (phrase)
         {
             // sql-phrases
-            case SQL_NULL:        return "null";
+            case SQL_NULL:              return "null";
             case SQL_PARAMETER:         return " ? ";
             case SQL_RENAME_TABLE:      return " ";
             case SQL_RENAME_COLUMN:     return " AS ";
@@ -271,14 +271,14 @@ public class DBMSHandlerHSql extends DBMSHandlerBase
     }
 
     /**
-     * Creates a DataModelChecker instance of this DBMSHandler
-     * @return
+     * Creates a DBModelChecker instance of this DBMSHandler
+     * @return the DBModelChecker
      */
     @Override
     public DBModelChecker createModelChecker(DBDatabase db)
     {
         // the default model checker
-        return new DBModelChecker("PUBLIC", "PUBLIC");
+        return new DBModelChecker(createModelParser("PUBLIC", "PUBLIC")); 
     }
     
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/mysql/MySQLDBModelParser.java 
b/empire-db/src/main/java/org/apache/empire/dbms/mysql/MySQLDBModelParser.java
new file mode 100644
index 0000000..76a6481
--- /dev/null
+++ 
b/empire-db/src/main/java/org/apache/empire/dbms/mysql/MySQLDBModelParser.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.dbms.mysql;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.validation.DBModelParser;
+
+public class MySQLDBModelParser extends DBModelParser
+{
+    public MySQLDBModelParser(String catalog, String schemaPattern)
+    {
+        super(catalog, schemaPattern);
+    }
+    
+    @Override
+    protected double getColumnSize(DataType empireType, ResultSet rs)
+            throws SQLException
+    {
+        switch (empireType) 
+        {   
+            case INTEGER: {
+                // return size in byte, depending on MySQL Integer Types
+                // see 
http://dev.mysql.com/doc/refman/5.7/en/integer-types.html
+                // ignore the "real" columnsize as its just a "format hint"
+                int sqlType = rs.getInt("DATA_TYPE");
+                switch(sqlType) {
+                    case Types.TINYINT:
+                        return 1; // TINYINT, 1 byte
+                    case Types.SMALLINT:
+                        return 2; // SMALLINT, 2 byte
+                    case Types.BIGINT:
+                        return 8; // BIGINT, 8 byte
+                    default: 
+                        return 4; // Types.INTEGER, INT, 4 byte
+                }
+            }
+            default:
+                return super.getColumnSize(empireType, rs);
+        }
+    }
+    
+    @Override
+    protected Object getColumnDefault(ResultSet rs)
+        throws SQLException
+    {
+        String defaultValue = rs.getString("COLUMN_DEF");
+        if (defaultValue != null && defaultValue.equals("CURRENT_TIMESTAMP"))
+            return DBDatabase.SYSDATE;
+        return defaultValue;
+    }
+    
+    @Override
+    protected boolean isIdentityColumn(ResultSet rs)
+    {
+        try {
+            int i = rs.findColumn("IS_AUTOINCREMENT");
+            return rs.getString(i).equalsIgnoreCase("YES");
+        } catch(SQLException e) {
+            log.warn("Missing column IS_AUTOINCREMENT. Unable to detect 
Identity column");
+            return false;
+        }
+    }
+    
+    @Override
+    protected boolean isTimestampColumn(ResultSet rs)
+    {   try {
+            String defaultValue = rs.getString("COLUMN_DEF");
+            if (rs.getInt("DATA_TYPE") == Types.TIMESTAMP && defaultValue != 
null && defaultValue.equals("CURRENT_TIMESTAMP"))
+            {
+                return true;
+            }
+            return super.isTimestampColumn(rs);
+        } catch(SQLException e) {
+            log.warn("Missing column COLUMN_DEF. Unable to detect Timestamp 
column");
+            return false;
+        }
+    }
+
+}
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBMSHandlerOracle.java 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBMSHandlerOracle.java
index d30941b..3756b6f 100644
--- 
a/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBMSHandlerOracle.java
+++ 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/DBMSHandlerOracle.java
@@ -524,6 +524,17 @@ public class DBMSHandlerOracle extends DBMSHandlerBase
             rd.close();
         }
     }
+    
+    @Override
+    public OracleDBModelParser createModelParser(String catalog, String schema)
+    {
+        // Check schema
+        String schemaPattern = StringUtils.coalesce(schema, this.schemaName);
+        if (StringUtils.isEmpty(schemaPattern))
+            throw new InvalidPropertyException("schemaName", null);
+        // create parser
+        return new OracleDBModelParser(schemaPattern);
+    }
 
     /**
      * Creates a DataModelChecker instance of this DBMSHandler
@@ -531,13 +542,9 @@ public class DBMSHandlerOracle extends DBMSHandlerBase
      */
     @Override
     public DBModelChecker createModelChecker(DBDatabase db)
-    {
-        // detect schemaPattern
-        String schemaPattern = (db!=null ? 
StringUtils.coalesce(db.getSchema(), this.schemaName) : this.schemaName);
-        if (StringUtils.isEmpty(schemaPattern))
-            throw new InvalidPropertyException("schemaName", null);
-        // the default model checker
-        return new OracleDBModelChecker(schemaPattern, getBooleanType());
+    {   // the default model checker
+        OracleDBModelParser modelParser = createModelParser(null, (db!=null ? 
db.getSchema() : null));
+        return new OracleDBModelChecker(modelParser, getBooleanType());
     }
 
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelChecker.java
 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelChecker.java
index 646b692..d21d3c4 100644
--- 
a/empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelChecker.java
+++ 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelChecker.java
@@ -18,9 +18,6 @@
  */
 package org.apache.empire.dbms.oracle;
 
-import java.sql.DatabaseMetaData;
-import java.sql.SQLException;
-
 import org.apache.empire.data.DataType;
 import org.apache.empire.db.DBColumn;
 import org.apache.empire.db.validation.DBModelChecker;
@@ -39,33 +36,13 @@ public class OracleDBModelChecker extends DBModelChecker
     
     private final BooleanType booleanType;
     
-    public OracleDBModelChecker(String schemaName, BooleanType booleanType)
+    public OracleDBModelChecker(OracleDBModelParser modelParser, BooleanType 
booleanType)
     {
-        super(null, schemaName);
+        super(modelParser);
         // Detect boolean type
         this.booleanType = booleanType;
         // ok
-        log.info("OracleDBModelChecker created for {} with booleanType {}", 
schemaName, booleanType);
-    }
-
-    /**
-     * collects all column information at once
-     */
-    @Override
-    protected int collectColumns(DatabaseMetaData dbMeta)
-            throws SQLException
-    {
-        return super.collectColumns(dbMeta, null);
-    }
-
-    /**
-     * collects all foreign keys at once
-     */
-    @Override
-    protected int collectForeignKeys(DatabaseMetaData dbMeta)
-            throws SQLException
-    {
-        return super.collectForeignKeys(dbMeta, null);
+        log.info("OracleDBModelChecker created for {} with booleanType {}", 
modelParser.getSchemaName(), booleanType);
     }
     
     @Override
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelChecker.java
 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelParser.java
similarity index 66%
copy from 
empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelChecker.java
copy to 
empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelParser.java
index 1dc83e0..1a374d4 100644
--- 
a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelChecker.java
+++ 
b/empire-db/src/main/java/org/apache/empire/dbms/oracle/OracleDBModelParser.java
@@ -16,29 +16,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.empire.dbms.sqlserver;
+package org.apache.empire.dbms.oracle;
 
 import java.sql.DatabaseMetaData;
 import java.sql.SQLException;
 
-import org.apache.empire.commons.StringUtils;
-import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelParser;
 
-/**
- * MSSqlDBModelChecker
- * DataModel checker implementation for Microsoft SQLServer
- * @author doebele
- */
-public class MSSqlDBModelChecker extends DBModelChecker
+public class OracleDBModelParser extends DBModelParser
 {
+    public OracleDBModelParser(String schemaName)    
+    {
+        super(null, schemaName);
+    }
+    
     /**
-     * create a MSSqlDBModelChecker
-     * @param db the database 
-     * @param catalog the catalog
+     * @return the database schema name
      */
-    public MSSqlDBModelChecker(String catalog, String schema)
+    public String getSchemaName()
     {
-        super(catalog, StringUtils.coalesce(schema, "DBO"));
+        return schemaPattern;
     }
 
     /**
@@ -50,5 +47,15 @@ public class MSSqlDBModelChecker extends DBModelChecker
     {
         return super.collectColumns(dbMeta, null);
     }
-    
+
+    /**
+     * collects all foreign keys at once
+     */
+    @Override
+    protected int collectForeignKeys(DatabaseMetaData dbMeta)
+            throws SQLException
+    {
+        return super.collectForeignKeys(dbMeta, null);
+    }
+
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
 
b/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
index 79f6cf4..6faf707 100644
--- 
a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
+++ 
b/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/DBMSHandlerMSSQL.java
@@ -39,6 +39,7 @@ import org.apache.empire.db.DBTable;
 import org.apache.empire.db.DBTableColumn;
 import org.apache.empire.db.exceptions.EmpireSQLException;
 import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelParser;
 import org.apache.empire.dbms.DBMSFeature;
 import org.apache.empire.dbms.DBMSHandler;
 import org.apache.empire.dbms.DBMSHandlerBase;
@@ -562,6 +563,12 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
      * @return
      */
     @Override
+    public DBModelParser createModelParser(String catalog, String schema)
+    {   // the default model checker
+        return new MSSqlDBModelParser(catalog, schema);
+    }
+    
+    @Override
     public DBModelChecker createModelChecker(DBDatabase db)
     {
         // detect catalog
@@ -576,8 +583,8 @@ public class DBMSHandlerMSSQL extends DBMSHandlerBase
             catalog = catalog.substring(schemaSep);
             schema  = catalog.substring(schemaSep+1);
         }
-        // the default model checker
-        return new MSSqlDBModelChecker(catalog, schema);
+        // create the checker
+        return new DBModelChecker(createModelParser(catalog, schema));
     }
 
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelChecker.java
 
b/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelParser.java
similarity index 73%
rename from 
empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelChecker.java
rename to 
empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelParser.java
index 1dc83e0..ef78a21 100644
--- 
a/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelChecker.java
+++ 
b/empire-db/src/main/java/org/apache/empire/dbms/sqlserver/MSSqlDBModelParser.java
@@ -19,24 +19,25 @@
 package org.apache.empire.dbms.sqlserver;
 
 import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 
 import org.apache.empire.commons.StringUtils;
-import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelParser;
 
 /**
  * MSSqlDBModelChecker
  * DataModel checker implementation for Microsoft SQLServer
  * @author doebele
  */
-public class MSSqlDBModelChecker extends DBModelChecker
+public class MSSqlDBModelParser extends DBModelParser
 {
     /**
      * create a MSSqlDBModelChecker
      * @param db the database 
      * @param catalog the catalog
      */
-    public MSSqlDBModelChecker(String catalog, String schema)
+    public MSSqlDBModelParser(String catalog, String schema)
     {
         super(catalog, StringUtils.coalesce(schema, "DBO"));
     }
@@ -51,4 +52,15 @@ public class MSSqlDBModelChecker extends DBModelChecker
         return super.collectColumns(dbMeta, null);
     }
     
+    @Override
+    protected boolean isIdentityColumn(ResultSet rs)
+    {   try {
+            int i = rs.findColumn("TYPE_NAME");
+            return rs.getString(i).matches(".*(?i:identity).*");
+        } catch(SQLException e) {
+            log.warn("Missing column TYPE_NAME. Unable to detect Identity 
column");
+            return false;
+        }
+    }
+    
 }
diff --git 
a/empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
 
b/empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
index 47c3e05..2b120b0 100644
--- 
a/empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
+++ 
b/empire-db/src/main/java/org/apache/empire/exceptions/InvalidPropertyException.java
@@ -27,6 +27,11 @@ public class InvalidPropertyException extends EmpireException
     
     public static final ErrorType errorType = new 
ErrorType("error.propertyInvalid", "The property {0} is not valid. Current 
value is {1}.");
     
+    public InvalidPropertyException(String property, Object value, Exception 
cause)
+    {
+        super(errorType, new String[] { property, StringUtils.valueOf(value) 
}, cause);
+    }
+    
     public InvalidPropertyException(String property, Object value)
     {
         super(errorType, new String[] { property, StringUtils.valueOf(value) 
});
diff --git 
a/empire-db/src/main/java/org/apache/empire/xml/XMLConfiguration.java 
b/empire-db/src/main/java/org/apache/empire/xml/XMLConfiguration.java
index 3c54700..2823a9d 100644
--- a/empire-db/src/main/java/org/apache/empire/xml/XMLConfiguration.java
+++ b/empire-db/src/main/java/org/apache/empire/xml/XMLConfiguration.java
@@ -155,7 +155,7 @@ public class XMLConfiguration
             propertiesNode = XMLUtil.findFirstChild(propertiesNode, nodeName);
             if (propertiesNode == null)
             { // Configuration
-                log.error("Property-Node {} has not been found.", nodeName);
+                log.warn("Property-Node {} has not been found.", nodeName);
                 throw new ItemNotFoundException(nodeName);
             }
         }

Reply via email to