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 <[email protected]> Authored: Thu Apr 30 18:07:15 2015 +0200 Committer: Rainer Döbele <[email protected]> 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()); + } + +}
