EMPIREDB-362 Split DBModelChecker, DBModelParser and improved CodeGenerator
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 @@
private String jdbcPwd;
+ private String dbmsHandlerClass;
+
// generation options
/**
* name of the database catalog (may be null)
@@ -65,7 +67,13 @@
* 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 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 @@
return dbCatalog;
}
- public void setDbCatalog(String dbCatalog) {
+ public void setDbCatalog(String dbCatalog) {
this.dbCatalog = dbCatalog;
}
@@ -352,6 +372,16 @@
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-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfigInvalidException.java b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfigInvalidException.java
new file mode 100644
index 0000000..5940c4b
--- /dev/null
+++ b/empire-db-codegen/src/main/java/org/apache/empire/db/codegen/CodeGenConfigInvalidException.java
@@ -0,0 +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.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 @@
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 @@
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 @@
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 @@
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 @@
#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);
+ setPrimaryKey($parser.getKeyColumns($table));
#end
-#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.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 @@
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 @@
*/
@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 @@
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 @@
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 @@
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 @@
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);
- }
+ protected final DBModelParser modelParser;
- 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>();
-
+ 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 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 @@
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.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 @@
* @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.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 @@
}
/**
+ * 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 @@
{
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 @@
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 @@
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, boolean aggregate)
+ {
+ this.sqlDefault = sqlDefault;
+ this.aggregate = aggregate;
+ }
private DBSqlPhrase(String sqlDefault)
{
- this.sqlDefault = 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 @@
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 @@
}
/**
- * 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 @@
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 @@
*/
@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 @@
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 65%
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
{
- /**
- * create a MSSqlDBModelChecker
- * @param db the database
- * @param catalog the catalog
- */
- public MSSqlDBModelChecker(String catalog, String schema)
+ public OracleDBModelParser(String schemaName)
{
- super(catalog, StringUtils.coalesce(schema, "DBO"));
+ super(null, schemaName);
+ }
+
+ /**
+ * @return the database schema name
+ */
+ public String getSchemaName()
+ {
+ return schemaPattern;
}
/**
@@ -50,5 +47,15 @@
{
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.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 @@
* @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 @@
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 72%
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 @@
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 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 @@
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);
}
}