You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2015/04/30 18:07:19 UTC
empire-db git commit: EMPIREDB-217 submitted by Jan on April 13th
Repository: empire-db
Updated Branches:
refs/heads/master 9fb58953f -> 8911cc5de
EMPIREDB-217
submitted by Jan on April 13th
Project: http://git-wip-us.apache.org/repos/asf/empire-db/repo
Commit: http://git-wip-us.apache.org/repos/asf/empire-db/commit/8911cc5d
Tree: http://git-wip-us.apache.org/repos/asf/empire-db/tree/8911cc5d
Diff: http://git-wip-us.apache.org/repos/asf/empire-db/diff/8911cc5d
Branch: refs/heads/master
Commit: 8911cc5deaafee958a881e29c2d651d263f31694
Parents: 9fb5895
Author: Rainer Döbele <do...@apache.org>
Authored: Thu Apr 30 18:07:15 2015 +0200
Committer: Rainer Döbele <do...@apache.org>
Committed: Thu Apr 30 18:07:15 2015 +0200
----------------------------------------------------------------------
.gitignore | 1 +
empire-db-examples/.gitignore | 1 +
.../empire/db/oracle/OracleDBModelChecker.java | 72 +++
.../empire/db/validation/DBModelChecker.java | 625 +++++++++++++++++++
.../db/validation/DBModelErrorHandler.java | 64 ++
.../db/validation/DBModelErrorLogger.java | 76 +++
6 files changed, 839 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/empire-db/blob/8911cc5d/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 0c2b833..988e912 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/.settings/
/.project
+/target/
http://git-wip-us.apache.org/repos/asf/empire-db/blob/8911cc5d/empire-db-examples/.gitignore
----------------------------------------------------------------------
diff --git a/empire-db-examples/.gitignore b/empire-db-examples/.gitignore
index 0c2b833..988e912 100644
--- a/empire-db-examples/.gitignore
+++ b/empire-db-examples/.gitignore
@@ -1,2 +1,3 @@
/.settings/
/.project
+/target/
http://git-wip-us.apache.org/repos/asf/empire-db/blob/8911cc5d/empire-db/src/main/java/org/apache/empire/db/oracle/OracleDBModelChecker.java
----------------------------------------------------------------------
diff --git a/empire-db/src/main/java/org/apache/empire/db/oracle/OracleDBModelChecker.java b/empire-db/src/main/java/org/apache/empire/db/oracle/OracleDBModelChecker.java
new file mode 100644
index 0000000..611af4a
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/oracle/OracleDBModelChecker.java
@@ -0,0 +1,72 @@
+package org.apache.empire.db.oracle;
+
+import java.sql.Connection;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBDatabaseDriver;
+import org.apache.empire.db.oracle.DBDatabaseDriverOracle.BooleanType;
+import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelErrorHandler;
+import org.apache.empire.exceptions.InvalidPropertyException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This implementation is used to check Oracle Databases
+ */
+public class OracleDBModelChecker extends DBModelChecker
+{
+ private static final Logger log = LoggerFactory.getLogger(OracleDBModelChecker.class);
+
+ private BooleanType booleanType = BooleanType.NUMBER;
+
+ @Override
+ public void checkModel(DBDatabase db, Connection conn, String dbSchema, DBModelErrorHandler handler)
+ {
+ // Get boolean type from driver
+ DBDatabaseDriver driver = db.getDriver();
+ if (driver instanceof DBDatabaseDriverOracle)
+ {
+ booleanType = ((DBDatabaseDriverOracle)driver).getBooleanType();
+ }
+ else
+ log.warn("Provided driver is not of type DBDatabaseDriverOracle");
+
+ // check now
+ super.checkModel(db, conn, dbSchema, handler);
+
+ }
+
+ @Override
+ protected void checkBoolColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkColumnNullable(column, remoteColumn, handler);
+
+ // check data type
+ DataType booleanDataType = null;
+ switch (booleanType)
+ {
+ case NUMBER:
+ booleanDataType = DataType.DECIMAL;
+ break;
+ case CHAR:
+ booleanDataType = DataType.CHAR;
+ break;
+ default:
+ throw new InvalidPropertyException("booleanType", booleanType);
+ }
+
+ if (remoteColumn.getDataType() != booleanDataType)
+ {
+ handler.columnTypeMismatch(column, booleanDataType);
+ }
+
+ // size should always be 1
+ if (remoteColumn.getSize() != 1)
+ {
+ handler.columnSizeMismatch(column, 1, 0);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/empire-db/blob/8911cc5d/empire-db/src/main/java/org/apache/empire/db/validation/DBModelChecker.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..4e850dc
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelChecker.java
@@ -0,0 +1,625 @@
+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.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBRelation;
+import org.apache.empire.db.DBRelation.DBReference;
+import org.apache.empire.db.DBTable;
+import org.apache.empire.db.DBTableColumn;
+import org.apache.empire.db.DBView;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DBModelChecker
+{
+ private static final Logger log = LoggerFactory.getLogger(DBModelChecker.class);
+
+ private final Map<String, DBTable> tableMap = new HashMap<String, DBTable>();
+ private final DBDatabase remoteDb = new InMemoryDatabase();
+
+ public static class InMemoryDatabase extends DBDatabase
+ {
+ private static final long serialVersionUID = 1L;
+ }
+
+ /**
+ * This method is used to check the database model
+ *
+ * @param db
+ * The Empire-db definition to be checked
+ * @param conn
+ * A connection to the database
+ * @param dbSchema
+ * The database schema
+ * @param handler
+ * The {@link DBModelErrorHandler} implementation that is called whenever an error
+ * occurs
+ */
+ public void checkModel(DBDatabase db, Connection conn, String dbSchema, DBModelErrorHandler handler)
+ {
+ try
+ {
+ DatabaseMetaData dbMeta = conn.getMetaData();
+
+ // collect tables & views
+ collectTables(dbMeta, dbSchema);
+
+ // Collect all columns
+ collectColumns(dbMeta, dbSchema);
+
+ // Collect PKs
+ collectPrimaryKeys(dbMeta, dbSchema);
+
+ // Collect FKs
+ collectForeignKeys(dbMeta, dbSchema);
+
+ // check Tables
+ for (DBTable table : db.getTables())
+ {
+ checkTable(table, handler);
+ }
+
+ // check Views
+ for (DBView view : db.getViews())
+ {
+ checkView(view, conn, handler);
+ }
+ }
+ catch (SQLException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ private void collectTables(DatabaseMetaData dbMeta, String dbSchema)
+ throws SQLException
+ {
+ ResultSet dbTables = dbMeta.getTables(null, dbSchema, null, new String[] { "TABLE", "VIEW" });
+ while (dbTables.next())
+ {
+ String name = dbTables.getString("TABLE_NAME");
+ // Ignore system tables containing a '$' symbol (required for Oracle!)
+ if (name.indexOf('$') >= 0)
+ {
+ DBModelChecker.log.info("Ignoring system table " + name);
+ continue;
+ }
+ this.tableMap.put(name.toUpperCase(), new DBTable(name, this.remoteDb));
+ }
+ }
+
+ private void collectColumns(DatabaseMetaData dbMeta, String dbSchema)
+ throws SQLException
+ {
+ ResultSet dbColumns = dbMeta.getColumns(null, dbSchema, null, null);
+ while (dbColumns.next())
+ {
+ String tableName = dbColumns.getString("TABLE_NAME");
+ DBTable t = this.tableMap.get(tableName.toUpperCase());
+ if (t == null)
+ {
+ DBModelChecker.log.error("Table not found: {}", tableName);
+ continue;
+ }
+ addColumn(t, dbColumns);
+ }
+ }
+
+ private void collectPrimaryKeys(DatabaseMetaData dbMeta, String dbSchema)
+ throws SQLException
+ {
+ for (String t : this.tableMap.keySet())
+ {
+ List<String> pkCols = new ArrayList<String>();
+ ResultSet primaryKeys = dbMeta.getPrimaryKeys(null, dbSchema, t);
+ while (primaryKeys.next())
+ {
+ pkCols.add(primaryKeys.getString("COLUMN_NAME"));
+ }
+ if (pkCols.size() > 0)
+ {
+ DBTable table = this.tableMap.get(t.toUpperCase());
+ DBColumn[] keys = new DBColumn[pkCols.size()];
+ for (int i = 0; i < keys.length; i++)
+ {
+ keys[i] = table.getColumn(pkCols.get(i).toUpperCase());
+ }
+ table.setPrimaryKey(keys);
+ }
+ }
+ }
+
+ // Findet nur Foreign Keys die auf eine Primary Key Spalte gehen
+ private void collectForeignKeys(DatabaseMetaData dbMeta, String dbSchema)
+ throws SQLException
+ {
+ ResultSet foreignKeys = dbMeta.getImportedKeys(null, dbSchema, null);
+ 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) this.remoteDb.getTable(fkTable.toUpperCase()).getColumn(fkColumn.toUpperCase());
+ DBTableColumn c2 = (DBTableColumn) this.remoteDb.getTable(pkTable.toUpperCase()).getColumn(pkColumn.toUpperCase());
+
+ DBRelation relation = this.remoteDb.getRelation(fkName);
+ if (relation == null)
+ {
+ this.remoteDb.addRelation(fkName, c1.referenceOn(c2));
+ }
+ else
+ {
+ // get existing references
+ DBReference[] refs = relation.getReferences();
+ // remove old
+ this.remoteDb.getRelations().remove(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;
+ this.remoteDb.addRelation(fkName, newRefs);
+ }
+ }
+ }
+
+ private void checkTable(DBTable table, DBModelErrorHandler handler)
+ {
+ DBTable remoteTable = this.tableMap.get(table.getName().toUpperCase());
+
+ if (remoteTable == null)
+ {
+ handler.itemNotFound(table);
+ return;
+ }
+
+ // Check primary Key
+ checkPrimaryKey(table, remoteTable, handler);
+
+ // check foreign keys
+ checkForeignKeys(table, remoteTable, handler);
+
+ // Check Columns
+ for (DBColumn column : table.getColumns())
+ {
+ DBColumn remoteColumn = remoteTable.getColumn(column.getName());
+ if (remoteColumn == null)
+ {
+ handler.itemNotFound(column);
+ continue;
+ }
+ checkColumn(column, remoteColumn, handler);
+ }
+ }
+
+ private void checkPrimaryKey(DBTable table, DBTable remoteTable, DBModelErrorHandler handler)
+ {
+ if (table.getPrimaryKey() == null)
+ {
+ // no primary key defined
+ return;
+ }
+
+ if (remoteTable.getPrimaryKey() == null)
+ {
+ // primary key missing in DB
+ handler.itemNotFound(table.getPrimaryKey());
+ return;
+ }
+
+ DBColumn[] pk = table.getPrimaryKey().getColumns();
+ DBColumn[] remotePk = remoteTable.getPrimaryKey().getColumns();
+
+ pkColLoop: for (DBColumn pkCol : pk)
+ {
+ for (DBColumn remotePkCol : remotePk)
+ {
+ if (pkCol.getFullName().equalsIgnoreCase(remotePkCol.getFullName()))
+ {
+ // found
+ continue pkColLoop;
+ }
+ }
+ // PK-Column not found
+ handler.primaryKeyColumnMissing(table.getPrimaryKey(), pkCol);
+ }
+ }
+
+ private void checkForeignKeys(DBTable table, DBTable remoteTable, DBModelErrorHandler handler)
+ {
+ if (table.getForeignKeyRelations().isEmpty())
+ {
+ // no foreign keys defined
+ return;
+ }
+
+ List<DBRelation> relations = table.getForeignKeyRelations();
+ List<DBRelation> remoteRelations = remoteTable.getForeignKeyRelations();
+
+ for (DBRelation relation : relations)
+ {
+ referenceLoop: for (DBReference reference : relation.getReferences())
+ {
+ if (reference.getTargetColumn().getRowSet() instanceof DBTable)
+ {
+ DBTable targetTable = (DBTable) reference.getTargetColumn().getRowSet();
+ DBTableColumn targetColumn = reference.getTargetColumn();
+ if (!targetTable.getPrimaryKey().contains(targetColumn))
+ {
+ DBModelChecker.log.info("The column "
+ + targetColumn.getName()
+ + " of foreign key {} is not a primary key of table {} and cant be checked because of a limitation in JDBC",
+ relation.getName(), targetTable.getName());
+ continue;
+ }
+ }
+
+ for (DBRelation remoteRelation : remoteRelations)
+ {
+ for (DBReference remoteReference : remoteRelation.getReferences())
+ {
+ if (reference.getSourceColumn().getFullName().equalsIgnoreCase(remoteReference.getSourceColumn().getFullName())
+ && reference.getTargetColumn().getFullName().equalsIgnoreCase(remoteReference.getTargetColumn().getFullName()))
+ {
+ // found
+ continue referenceLoop;
+ }
+ }
+
+ }
+ // Not found
+ handler.itemNotFound(relation);
+ }
+
+ }
+
+ }
+
+ private void checkView(DBView view, Connection conn, DBModelErrorHandler handler)
+ {
+ DBTable remoteView = this.tableMap.get(view.getName().toUpperCase());
+
+ if (remoteView == null)
+ {
+ handler.itemNotFound(remoteView);
+ return;
+ }
+
+ for (DBColumn column : view.getColumns())
+ {
+ DBColumn remoteColumn = remoteView.getColumn(column.getName());
+ if (remoteColumn == null)
+ {
+ handler.itemNotFound(column);
+ continue;
+ }
+ checkColumn(column, remoteColumn, handler);
+ }
+ }
+
+ private void checkColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ switch (column.getDataType())
+ {
+ case UNKNOWN:
+ checkUnknownColumn(column, remoteColumn, handler);
+ break;
+ case INTEGER:
+ checkIntegerColumn(column, remoteColumn, handler);
+ break;
+ case AUTOINC:
+ checkAutoIncColumn(column, remoteColumn, handler);
+ break;
+ case TEXT:
+ checkTextColumn(column, remoteColumn, handler);
+ break;
+ case DATE:
+ case DATETIME:
+ checkDateColumn(column, remoteColumn, handler);
+ break;
+ case CHAR:
+ checkCharColumn(column, remoteColumn, handler);
+ break;
+ case FLOAT:
+ checkFloatColumn(column, remoteColumn, handler);
+ break;
+ case DECIMAL:
+ checkDecimalColumn(column, remoteColumn, handler);
+ break;
+ case BOOL:
+ checkBoolColumn(column, remoteColumn, handler);
+ break;
+ case CLOB:
+ checkClobColumn(column, remoteColumn, handler);
+ break;
+ case BLOB:
+ checkBlobColumn(column, remoteColumn, handler);
+ break;
+ case UNIQUEID:
+ checkUniqueIdColumn(column, remoteColumn, handler);
+ break;
+ default:
+ throw new RuntimeException("Invalid DataType " + column.getDataType());
+ }
+
+ }
+
+ private void checkGenericColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkColumnType(column, remoteColumn, handler);
+ checkColumnNullable(column, remoteColumn, handler);
+ checkColumnSize(column, remoteColumn, handler);
+ }
+
+ protected void checkColumnType(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ if (column.getDataType() != remoteColumn.getDataType())
+ {
+ handler.columnTypeMismatch(column, remoteColumn.getDataType());
+ }
+ }
+
+ protected void checkColumnNullable(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ if (column.isRequired() && !remoteColumn.isRequired())
+ {
+ handler.columnNullableMismatch(column, remoteColumn.isRequired());
+ }
+ }
+
+ protected void checkColumnSize(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ if (((int) column.getSize() != (int) remoteColumn.getSize()))
+ {
+ handler.columnSizeMismatch(column, (int) remoteColumn.getSize(), 0);
+ }
+ }
+
+ /** empire-db DataType-specific checker **/
+ protected void checkUnknownColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+ }
+
+ protected void checkIntegerColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+ }
+
+ protected void checkAutoIncColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkColumnSize(column, remoteColumn, handler);
+ checkColumnNullable(column, remoteColumn, handler);
+ }
+
+ protected void checkTextColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+ }
+
+ protected void checkDateColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ // check nullable
+ checkColumnNullable(column, remoteColumn, handler);
+
+ // check type
+ if (!(remoteColumn.getDataType() == DataType.DATE || remoteColumn.getDataType() == DataType.DATETIME))
+ {
+ handler.columnTypeMismatch(column, remoteColumn.getDataType());
+ }
+ }
+
+ protected void checkCharColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+ }
+
+ protected void checkFloatColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+ }
+
+ protected void checkDecimalColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+
+ // check scale
+ if (column instanceof DBTableColumn && remoteColumn instanceof DBTableColumn)
+ {
+ DBTableColumn tableColumn = (DBTableColumn) column;
+ DBTableColumn tableRemoteColumn = (DBTableColumn) remoteColumn;
+
+ if (tableColumn.getDecimalScale() != tableRemoteColumn.getDecimalScale())
+ {
+ handler.columnSizeMismatch(column, (int) remoteColumn.getSize(), tableRemoteColumn.getDecimalScale());
+ }
+ }
+
+ }
+
+ protected void checkBoolColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ // Dont check size
+ checkColumnType(column, remoteColumn, handler);
+ checkColumnNullable(column, remoteColumn, handler);
+ }
+
+ protected void checkBlobColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ // Dont check size
+ checkColumnType(column, remoteColumn, handler);
+ checkColumnNullable(column, remoteColumn, handler);
+ }
+
+ protected void checkClobColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ // Dont check size
+ checkColumnType(column, remoteColumn, handler);
+ checkColumnNullable(column, remoteColumn, handler);
+ }
+
+ protected void checkUniqueIdColumn(DBColumn column, DBColumn remoteColumn, DBModelErrorHandler handler)
+ {
+ checkGenericColumn(column, remoteColumn, handler);
+ }
+
+ /** taken from CodeGenParser **/
+ private DBTableColumn addColumn(DBTable 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)
+ { // parse
+ try
+ {
+ 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;
+ }
+ }
+
+ // 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 driver for MySQL.
+ // This will avoid the driver 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 driver 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 + ")");
+ 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;
+
+ }
+
+ 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:
+ empireType = DataType.TEXT;
+ break;
+ case Types.DATE:
+ empireType = DataType.DATE;
+ break;
+ case Types.TIMESTAMP:
+ case Types.TIME:
+ empireType = DataType.DATETIME;
+ break;
+ case Types.CHAR:
+ 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:
+ 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;
+ }
+}
http://git-wip-us.apache.org/repos/asf/empire-db/blob/8911cc5d/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorHandler.java
----------------------------------------------------------------------
diff --git a/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorHandler.java b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorHandler.java
new file mode 100644
index 0000000..41da57d
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorHandler.java
@@ -0,0 +1,64 @@
+package org.apache.empire.db.validation;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBIndex;
+import org.apache.empire.db.DBObject;
+
+public interface DBModelErrorHandler
+{
+
+ /**
+ * This method is called when an object (e. g. table or column) is missing in
+ * the database.
+ *
+ * @param dbo
+ * The missing object
+ */
+ void itemNotFound(DBObject dbo);
+
+ /**
+ * This method is called when a column in a primary key of the Empire-db definition
+ * is missing in the database
+ *
+ * @param primaryKey
+ * The primary key that misses the column
+ * @param column
+ * The missing column
+ */
+ void primaryKeyColumnMissing(DBIndex primaryKey, DBColumn column);
+
+ /**
+ * This method is called when the type of a column in the Empire-db
+ * definition does not match the database.
+ *
+ * @param col
+ * The affected column
+ * @param type
+ */
+ void columnTypeMismatch(DBColumn col, DataType type);
+
+ /**
+ * This method is called when the size of a column in the Empire-db
+ * definition does not match the database.
+ *
+ * @param col
+ * The affected column
+ * @param size
+ * Size in the database
+ * @param scale
+ * Decimal scale in the database (only for decimal types, 0 otherwise)
+ */
+ void columnSizeMismatch(DBColumn col, int size, int scale);
+
+ /**
+ * This method is called when a NOT NULL constraints of a column in
+ * the Empire-db definition does not match the database.
+ *
+ * @param col
+ * The affected column
+ * @param nullable
+ * true if the column is required in the database
+ */
+ void columnNullableMismatch(DBColumn col, boolean nullable);
+}
http://git-wip-us.apache.org/repos/asf/empire-db/blob/8911cc5d/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorLogger.java
----------------------------------------------------------------------
diff --git a/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorLogger.java b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorLogger.java
new file mode 100644
index 0000000..690122c
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/validation/DBModelErrorLogger.java
@@ -0,0 +1,76 @@
+package org.apache.empire.db.validation;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBIndex;
+import org.apache.empire.db.DBObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An implemtnation of the {@link DBModelErrorHandler} interface that logs all errors
+ */
+public class DBModelErrorLogger implements DBModelErrorHandler
+{
+ private static final Logger log = LoggerFactory.getLogger(DBModelErrorLogger.class);
+
+ /**
+ * handle itemNotFound errors
+ */
+ public void itemNotFound(DBObject dbo)
+ {
+ if (dbo instanceof DBIndex)
+ {
+ DBIndex dbi = (DBIndex) dbo;
+ DBModelErrorLogger.log.error("The primary key " + dbi.getName() + " for table " + dbi.getTable().getName()
+ + " does not exist in the target database.");
+ }
+ else
+ {
+ DBModelErrorLogger.log.error("The object " + dbo.toString() + " does not exist in the target database.");
+ }
+ }
+
+ /**
+ * handle columnTypeMismatch errors
+ */
+ public void columnTypeMismatch(DBColumn col, DataType type)
+ {
+ DBModelErrorLogger.log.error("The column " + col.getFullName() + " type of " + col.getDataType().toString()
+ + " does not match the database type of " + type.toString());
+ }
+
+ /**
+ * handle columnSizeMismatch errors
+ */
+ public void columnSizeMismatch(DBColumn col, int size, int scale)
+ {
+ DBModelErrorLogger.log.error("The column " + col.getFullName() + " size of " + String.valueOf(col.getSize())
+ + " does not match the size database size of " + String.valueOf(size));
+ }
+
+ /**
+ * handle columnNullableMismatch errors
+ */
+ public void columnNullableMismatch(DBColumn col, boolean nullable)
+ {
+ if (nullable)
+ {
+ DBModelErrorLogger.log.error("The column " + col.getFullName() + " must not be nullable");
+ }
+ else
+ {
+ DBModelErrorLogger.log.error("The column " + col.getFullName() + " must be nullable");
+ }
+ }
+
+ /**
+ * handle primaryKeyColumnMissing errors
+ */
+ public void primaryKeyColumnMissing(DBIndex primaryKey, DBColumn column)
+ {
+ DBModelErrorLogger.log.error("The primary key " + primaryKey.getName() + " of table " + primaryKey.getTable().getName()
+ + " misses the column " + column.getName());
+ }
+
+}