Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.jumpmind.db.platform;
/*
* 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.
*/
import static org.apache.commons.lang.StringUtils.isNotBlank;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.jumpmind.db.model.Column;
import org.jumpmind.db.model.Database;
import org.jumpmind.db.model.ForeignKey;
import org.jumpmind.db.model.IIndex;
import org.jumpmind.db.model.IndexColumn;
import org.jumpmind.db.model.NonUniqueIndex;
import org.jumpmind.db.model.PlatformColumn;
import org.jumpmind.db.model.Reference;
import org.jumpmind.db.model.Table;
import org.jumpmind.db.model.TypeMap;
import org.jumpmind.db.model.UniqueIndex;
import org.jumpmind.db.sql.IConnectionCallback;
import org.jumpmind.db.sql.JdbcSqlTemplate;
import org.jumpmind.db.sql.SqlException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
* An utility class to create a Database model from a live database.
*/
public abstract class AbstractJdbcDdlReader implements IDdlReader {
/* The Log to which logging calls will be made. */
protected Logger log = LoggerFactory.getLogger(getClass());
/* The descriptors for the relevant columns in the table meta data. */
private final List _columnsForTable;
/* The descriptors for the relevant columns in the table column meta data. */
private final List _columnsForColumn;
/* The descriptors for the relevant columns in the primary key meta data. */
private final List _columnsForPK;
/* The descriptors for the relevant columns in the foreign key meta data. */
private final List _columnsForFK;
/* The descriptors for the relevant columns in the index meta data. */
private final List _columnsForIndex;
/* The platform that this model reader belongs to. */
protected IDatabasePlatform platform;
/*
* Contains default column sizes (minimum sizes that a JDBC-compliant db
* must support).
*/
private HashMap _defaultSizes = new HashMap();
/* The default database catalog to read. */
private String _defaultCatalogPattern = "%";
/* The default database schema(s) to read. */
private String _defaultSchemaPattern = "%";
/* The default pattern for reading all tables. */
private String _defaultTablePattern = "%";
/* The default pattern for reading all columns. */
private String _defaultColumnPattern;
/* The table types to recognize per default. */
private String[] _defaultTableTypes = { "TABLE" };
public AbstractJdbcDdlReader(IDatabasePlatform platform) {
this.platform = platform;
_defaultSizes.put(new Integer(Types.CHAR), "254");
_defaultSizes.put(new Integer(Types.VARCHAR), "254");
_defaultSizes.put(new Integer(Types.LONGVARCHAR), "254");
_defaultSizes.put(new Integer(Types.BINARY), "254");
_defaultSizes.put(new Integer(Types.VARBINARY), "254");
_defaultSizes.put(new Integer(Types.LONGVARBINARY), "254");
_defaultSizes.put(new Integer(Types.INTEGER), "32");
_defaultSizes.put(new Integer(Types.BIGINT), "64");
_defaultSizes.put(new Integer(Types.REAL), "7,0");
_defaultSizes.put(new Integer(Types.FLOAT), "15,0");
_defaultSizes.put(new Integer(Types.DOUBLE), "15,0");
_defaultSizes.put(new Integer(Types.DECIMAL), "15,15");
_defaultSizes.put(new Integer(Types.NUMERIC), "15,15");
_columnsForTable = initColumnsForTable();
_columnsForColumn = initColumnsForColumn();
_columnsForPK = initColumnsForPK();
_columnsForFK = initColumnsForFK();
_columnsForIndex = initColumnsForIndex();
}
/*
* Returns the platform that this model reader belongs to.
*
* @return The platform
*/
public IDatabasePlatform getPlatform() {
return platform;
}
/*
* Returns the platform specific settings.
*
* @return The platform settings
*/
public DatabaseInfo getPlatformInfo() {
return platform.getDatabaseInfo();
}
/*
* Returns descriptors for the columns that shall be read from the result
* set when reading the meta data for a table. Note that the columns are
* read in the order defined by this list. Redefine this method if you
* want more columns or a different order.
*
* @return The descriptors for the result set columns
*/
protected List initColumnsForTable() {
List result = new ArrayList();
result.add(new MetaDataColumnDescriptor("TABLE_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("TABLE_TYPE", Types.VARCHAR, "UNKNOWN"));
result.add(new MetaDataColumnDescriptor(getResultSetCatalogName(), Types.VARCHAR));
result.add(new MetaDataColumnDescriptor(getResultSetSchemaName(), Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("REMARKS", Types.VARCHAR));
return result;
}
/*
* Returns descriptors for the columns that shall be read from the result
* set when reading the meta data for table columns. Note that the columns
* are read in the order defined by this list. Redefine this method if
* you want more columns or a different order.
*
* @return The map column name -> descriptor for the result set columns
*/
protected List initColumnsForColumn() {
List result = new ArrayList();
// As suggested by Alexandre Borgoltz, we're reading the COLUMN_DEF
// first because Oracle
// has problems otherwise (it seemingly requires a LONG column to be the
// first to be read)
// See also DDLUTILS-29
result.add(new MetaDataColumnDescriptor("COLUMN_DEF", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("COLUMN_DEFAULT", Types.VARCHAR));
// we're also reading the table name so that a model reader impl can
// filter manually
result.add(new MetaDataColumnDescriptor("TABLE_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("COLUMN_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("TYPE_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("DATA_TYPE", Types.INTEGER, new Integer(
java.sql.Types.OTHER)));
result.add(new MetaDataColumnDescriptor("NUM_PREC_RADIX", Types.INTEGER, new Integer(10)));
result.add(new MetaDataColumnDescriptor("DECIMAL_DIGITS", Types.INTEGER, new Integer(0)));
result.add(new MetaDataColumnDescriptor("COLUMN_SIZE", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("IS_NULLABLE", Types.VARCHAR, "YES"));
result.add(new MetaDataColumnDescriptor("IS_AUTOINCREMENT", Types.VARCHAR, "YES"));
result.add(new MetaDataColumnDescriptor("REMARKS", Types.VARCHAR));
return result;
}
/*
* Returns descriptors for the columns that shall be read from the result
* set when reading the meta data for primary keys. Note that the columns
* are read in the order defined by this list. Redefine this method if
* you want more columns or a different order.
*
* @return The map column name -> descriptor for the result set columns
*/
protected List initColumnsForPK() {
List result = new ArrayList();
result.add(new MetaDataColumnDescriptor("COLUMN_NAME", Types.VARCHAR));
// we're also reading the table name so that a model reader impl can
// filter manually
result.add(new MetaDataColumnDescriptor("TABLE_NAME", Types.VARCHAR));
// the name of the primary key is currently only interesting to the pk
// index name resolution
result.add(new MetaDataColumnDescriptor("PK_NAME", Types.VARCHAR));
return result;
}
/*
* Returns descriptors for the columns that shall be read from the result
* set when reading the meta data for foreign keys originating from a table.
* Note that the columns are read in the order defined by this list.
* Redefine this method if you want more columns or a different order.
*
* @return The map column name -> descriptor for the result set columns
*/
protected List initColumnsForFK() {
List result = new ArrayList();
result.add(new MetaDataColumnDescriptor("PKTABLE_NAME", Types.VARCHAR));
// we're also reading the table name so that a model reader impl can
// filter manually
result.add(new MetaDataColumnDescriptor("FKTABLE_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("KEY_SEQ", Types.TINYINT, new Short((short) 0)));
result.add(new MetaDataColumnDescriptor("FK_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("PKCOLUMN_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("FKCOLUMN_NAME", Types.VARCHAR));
return result;
}
/*
* Returns descriptors for the columns that shall be read from the result
* set when reading the meta data for indices. Note that the columns are
* read in the order defined by this list. Redefine this method if you
* want more columns or a different order.
*
* @return The map column name -> descriptor for the result set columns
*/
protected List initColumnsForIndex() {
List result = new ArrayList();
result.add(new MetaDataColumnDescriptor("INDEX_NAME", Types.VARCHAR));
// we're also reading the table name so that a model reader impl can
// filter manually
result.add(new MetaDataColumnDescriptor("TABLE_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("NON_UNIQUE", Types.BIT, Boolean.TRUE));
result.add(new MetaDataColumnDescriptor("ORDINAL_POSITION", Types.TINYINT, new Short(
(short) 0)));
result.add(new MetaDataColumnDescriptor("COLUMN_NAME", Types.VARCHAR));
result.add(new MetaDataColumnDescriptor("TYPE", Types.TINYINT));
return result;
}
/*
* Returns the catalog(s) in the database to read per default.
*
* @return The default catalog(s)
*/
public String getDefaultCatalogPattern() {
return _defaultCatalogPattern;
}
/*
* Sets the catalog(s) in the database to read per default.
*
* @param catalogPattern The catalog(s)
*/
public void setDefaultCatalogPattern(String catalogPattern) {
_defaultCatalogPattern = catalogPattern;
}
/*
* Returns the schema(s) in the database to read per default.
*
* @return The default schema(s)
*/
public String getDefaultSchemaPattern() {
return _defaultSchemaPattern;
}
/*
* Sets the schema(s) in the database to read per default.
*
* @param schemaPattern The schema(s)
*/
public void setDefaultSchemaPattern(String schemaPattern) {
_defaultSchemaPattern = schemaPattern;
}
/*
* Returns the default pattern to read the relevant tables from the
* database.
*
* @return The table pattern
*/
public String getDefaultTablePattern() {
return _defaultTablePattern;
}
/*
* Sets the default pattern to read the relevant tables from the database.
*
* @param tablePattern The table pattern
*/
public void setDefaultTablePattern(String tablePattern) {
_defaultTablePattern = tablePattern;
}
/*
* Returns the default pattern to read the relevant columns from the
* database.
*
* @return The column pattern
*/
public String getDefaultColumnPattern() {
return _defaultColumnPattern;
}
/*
* Sets the default pattern to read the relevant columns from the database.
*
* @param columnPattern The column pattern
*/
public void setDefaultColumnPattern(String columnPattern) {
_defaultColumnPattern = columnPattern;
}
/*
* Returns the table types to recognize per default.
*
* @return The default table types
*/
public String[] getDefaultTableTypes() {
return _defaultTableTypes;
}
/*
* Sets the table types to recognize per default. Typical types are "TABLE",
* "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
* "SYNONYM".
*
* @param types The table types
*/
public void setDefaultTableTypes(String[] types) {
_defaultTableTypes = types;
}
/*
* Returns the descriptors for the columns to be read from the table meta
* data result set.
*
* @return The column descriptors
*/
protected List getColumnsForTable() {
return _columnsForTable;
}
/*
* Returns the descriptors for the columns to be read from the column meta
* data result set.
*
* @return The column descriptors
*/
protected List getColumnsForColumn() {
return _columnsForColumn;
}
/*
* Returns the descriptors for the columns to be read from the primary key
* meta data result set.
*
* @return The column descriptors
*/
protected List getColumnsForPK() {
return _columnsForPK;
}
/*
* Returns the descriptors for the columns to be read from the foreign key
* meta data result set.
*
* @return The column descriptors
*/
protected List getColumnsForFK() {
return _columnsForFK;
}
/*
* Returns the descriptors for the columns to be read from the index meta
* data result set.
*
* @return The column descriptors
*/
protected List getColumnsForIndex() {
return _columnsForIndex;
}
/*
* Reads the database model from the given connection.
*
* @param connection The connection
*
* @param name The name of the resulting database; null when
* the default name (the catalog) is desired which might be
* null itself though
*
* @return The database model
*/
public Database getDatabase(Connection connection) throws SQLException {
return readTables(null, null, null);
}
protected String getResultSetSchemaName() {
return "TABLE_SCHEM";
}
protected String getResultSetCatalogName() {
return "TABLE_CAT";
}
/*
* Reads the database model from the given connection.
*
* @param catalog The catalog to access in the database; use
* null for the default value
*
* @param schema The schema to access in the database; use null
* for the default value
*
* @param tableTypes The table types to process; use null or an
* empty list for the default ones
*
* @return The database model
*/
public Database readTables(final String catalog, final String schema, final String[] tableTypes) {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return postprocessModelFromDatabase(sqlTemplate
.execute(new IConnectionCallback() {
public Database execute(Connection connection) throws SQLException {
Database db = new Database();
db.setName(Table.getQualifiedTablePrefix(catalog, schema));
db.addTables(readTables(connection, catalog, schema, tableTypes));
db.initialize();
return db;
}
}));
}
/*
* Allows the platform to postprocess the model just read from the database.
*
* @param model The model
*/
protected Database postprocessModelFromDatabase(Database model) {
// Default values for CHAR/VARCHAR/LONGVARCHAR columns have quotation
// marks around them which we'll remove now
for (int tableIdx = 0; tableIdx < model.getTableCount(); tableIdx++) {
postprocessTableFromDatabase(model.getTable(tableIdx));
}
return model;
}
/*
* Reads the tables from the database metadata.
*
* @param catalog The catalog to acess in the database; use
* null for the default value
*
* @param schemaPattern The schema(s) to acess in the database; use
* null for the default value
*
* @param tableTypes The table types to process; use null or an
* empty list for the default ones
*
* @return The tables
*/
protected Collection
();
while (tableData.next()) {
Map values = readMetaData(tableData, getColumnsForTable());
Table table = readTable(connection, metaData, values);
if (table != null) {
tables.add(table);
}
}
final Collator collator = Collator.getInstance();
Collections.sort(tables, new Comparator
() {
public int compare(Table obj1, Table obj2) {
return collator.compare(obj1.getName().toUpperCase(), obj2.getName()
.toUpperCase());
}
});
return tables;
} finally {
if (tableData != null) {
tableData.close();
}
}
}
public Table readTable(final String catalog, final String schema, final String table) {
try {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return postprocessTableFromDatabase(sqlTemplate.execute(new IConnectionCallback
() {
public Table execute(Connection connection) throws SQLException {
DatabaseMetaDataWrapper metaData = new DatabaseMetaDataWrapper();
metaData.setMetaData(connection.getMetaData());
metaData.setCatalog(catalog);
metaData.setSchemaPattern(schema);
metaData.setTableTypes(null);
ResultSet tableData = null;
try {
tableData = metaData.getTables(getTableNamePattern(table));
if (tableData != null && tableData.next()) {
Map values = readMetaData(tableData, initColumnsForTable());
return readTable(connection, metaData, values);
} else {
return null;
}
} finally {
close(tableData);
}
}
}));
} catch (SqlException e) {
if (e.getMessage()!=null && StringUtils.containsIgnoreCase(e.getMessage(), "does not exist")) {
return null;
} else {
throw e;
}
}
}
protected Table postprocessTableFromDatabase(Table table) {
if (table != null) {
for (int columnIdx = 0; columnIdx < table.getColumnCount(); columnIdx++) {
Column column = table.getColumn(columnIdx);
if (TypeMap.isTextType(column.getMappedTypeCode())
|| TypeMap.isDateTimeType(column.getMappedTypeCode())) {
String defaultValue = column.getDefaultValue();
if ((defaultValue != null) && (defaultValue.length() >= 2)
&& defaultValue.startsWith("'") && defaultValue.endsWith("'")) {
defaultValue = defaultValue.substring(1, defaultValue.length() - 1);
column.setDefaultValue(defaultValue);
}
}
}
}
return table;
}
protected void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
}
}
}
protected void close(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex) {
}
}
}
protected String getTableNamePattern(String tableName) {
return tableName;
}
/*
* Reads the next table from the meta data.
*
* @param metaData The database meta data
*
* @param values The table metadata values as defined by {@link
* #getColumnsForTable()}
*
* @return The table or null if the result set row did not
* contain a valid table
*/
protected Table readTable(Connection connection, DatabaseMetaDataWrapper metaData,
Map values) throws SQLException {
String tableName = (String) values.get("TABLE_NAME");
Table table = null;
if ((tableName != null) && (tableName.length() > 0)) {
String type = (String) values.get("TABLE_TYPE");
String[] unsupportedTableTypes = getUnsupportedTableTypes();
for (String unsupportedTableType : unsupportedTableTypes) {
if (StringUtils.isNotBlank(type) && type.equals(unsupportedTableType)) {
return null;
}
}
table = new Table();
table.setName(tableName);
table.setType(type);
String catalog = (String) values.get(getResultSetCatalogName());
table.setCatalog(catalog);
metaData.setCatalog(catalog);
String schema = (String) values.get(getResultSetSchemaName());
table.setSchema(schema);
metaData.setSchemaPattern(schema);
table.setDescription((String) values.get("REMARKS"));
table.addColumns(readColumns(metaData, tableName));
if (table.getColumnCount() > 0) {
table.addForeignKeys(readForeignKeys(connection, metaData, tableName));
table.addIndices(readIndices(connection, metaData, tableName));
Collection primaryKeys = readPrimaryKeyNames(metaData, tableName);
for (Iterator it = primaryKeys.iterator(); it.hasNext();) {
table.findColumn(it.next(), true).setPrimaryKey(true);
}
if (getPlatformInfo().isSystemIndicesReturned()) {
removeSystemIndices(connection, metaData, table);
}
} else {
table = null;
}
}
return table;
}
protected String[] getUnsupportedTableTypes() {
return new String[0];
}
/*
* Removes system indices (generated by the database for primary and foreign
* keys) from the table.
*
* @param metaData The database meta data
*
* @param table The table
*/
protected void removeSystemIndices(Connection connection, DatabaseMetaDataWrapper metaData,
Table table) throws SQLException {
removeInternalPrimaryKeyIndex(connection, metaData, table);
for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++) {
removeInternalForeignKeyIndex(connection, metaData, table, table.getForeignKey(fkIdx));
}
}
/*
* Tries to remove the internal index for the table's primary key.
*
* @param metaData The database meta data
*
* @param table The table
*/
protected void removeInternalPrimaryKeyIndex(Connection connection,
DatabaseMetaDataWrapper metaData, Table table) throws SQLException {
Column[] pks = table.getPrimaryKeyColumns();
List columnNames = new ArrayList();
for (int columnIdx = 0; columnIdx < pks.length; columnIdx++) {
columnNames.add(pks[columnIdx].getName());
}
for (int indexIdx = 0; indexIdx < table.getIndexCount();) {
IIndex index = table.getIndex(indexIdx);
if (index.isUnique() && matches(index, columnNames)
&& isInternalPrimaryKeyIndex(connection, metaData, table, index)) {
table.removeIndex(indexIdx);
} else {
indexIdx++;
}
}
}
/*
* Tries to remove the internal index for the given foreign key.
*
* @param metaData The database meta data
*
* @param table The table where the table is defined
*
* @param fk The foreign key
*/
protected void removeInternalForeignKeyIndex(Connection connection,
DatabaseMetaDataWrapper metaData, Table table, ForeignKey fk) throws SQLException {
List columnNames = new ArrayList();
boolean mustBeUnique = !getPlatformInfo().isSystemForeignKeyIndicesAlwaysNonUnique();
for (int columnIdx = 0; columnIdx < fk.getReferenceCount(); columnIdx++) {
String name = fk.getReference(columnIdx).getLocalColumnName();
Column localColumn = table.findColumn(name, getPlatform().getDdlBuilder()
.isDelimitedIdentifierModeOn());
if (mustBeUnique && !localColumn.isPrimaryKey()) {
mustBeUnique = false;
}
columnNames.add(name);
}
for (int indexIdx = 0; indexIdx < table.getIndexCount();) {
IIndex index = table.getIndex(indexIdx);
if ((mustBeUnique == index.isUnique()) && matches(index, columnNames)
&& isInternalForeignKeyIndex(connection, metaData, table, fk, index)) {
fk.setAutoIndexPresent(true);
table.removeIndex(indexIdx);
} else {
indexIdx++;
}
}
}
/*
* Checks whether the given index matches the column list.
*
* @param index The index
*
* @param columnsToSearchFor The names of the columns that the index should
* be for
*
* @return true if the index matches the columns
*/
protected boolean matches(IIndex index, List columnsToSearchFor) {
for (String column : columnsToSearchFor) {
boolean found = false;
for (int i = 0; i < index.getColumnCount(); i++) {
if (column != null && column.equals(index.getColumn(i).getName())) {
found = true;
}
}
if (!found) {
return false;
}
}
return true;
}
/*
* Tries to determine whether the index is the internal database-generated
* index for the given table's primary key. Note that only unique indices
* with the correct columns are fed to this method. Redefine this method for
* specific platforms if there are better ways to determine internal
* indices.
*
* @param metaData The database meta data
*
* @param table The table owning the index
*
* @param index The index to check
*
* @return true if the index seems to be an internal primary
* key one
*/
protected boolean isInternalPrimaryKeyIndex(Connection connection,
DatabaseMetaDataWrapper metaData, Table table, IIndex index) throws SQLException {
return false;
}
/*
* Tries to determine whether the index is the internal database-generated
* index for the given foreign key. Note that only non-unique indices with
* the correct columns are fed to this method. Redefine this method for
* specific platforms if there are better ways to determine internal
* indices.
*
* @param metaData The database meta data
*
* @param table The table owning the index and foreign key
*
* @param fk The foreign key
*
* @param index The index to check
*
* @return true if the index seems to be an internal primary
* key one
*/
protected boolean isInternalForeignKeyIndex(Connection connection,
DatabaseMetaDataWrapper metaData, Table table, ForeignKey fk, IIndex index)
throws SQLException {
return false;
}
/*
* Reads the column definitions for the indicated table.
*
* @param metaData The database meta data
*
* @param tableName The name of the table
*
* @return The columns
*/
protected Collection readColumns(DatabaseMetaDataWrapper metaData, String tableName)
throws SQLException {
ResultSet columnData = null;
try {
Set columnNames = new HashSet();
columnData = metaData.getColumns(getTableNamePattern(tableName),
getDefaultColumnPattern());
List columns = new ArrayList();
while (columnData.next()) {
Map values = readMetaData(columnData, getColumnsForColumn());
Column column = readColumn(metaData, values);
if (!columnNames.contains(column.getName())) {
columnNames.add(column.getName());
columns.add(column);
}
}
return columns;
} finally {
close(columnData);
}
}
protected Integer mapUnknownJdbcTypeForColumn(Map values) {
return null;
}
/*
* Extracts a column definition from the result set.
*
* @param metaData The database meta data
*
* @param values The column meta data values as defined by {@link
* #getColumnsForColumn()}
*
* @return The column
*/
protected Column readColumn(DatabaseMetaDataWrapper metaData, Map values)
throws SQLException {
Column column = new Column();
PlatformColumn platformColumn = new PlatformColumn();
platformColumn.setName(platform.getName());
column.setName((String) values.get("COLUMN_NAME"));
String defaultValue = (String) values.get("COLUMN_DEF");
if (defaultValue == null) {
defaultValue = (String) values.get("COLUMN_DEFAULT");
}
if (defaultValue != null) {
column.setDefaultValue(defaultValue.trim());
}
Integer mappedType = mapUnknownJdbcTypeForColumn(values);
if (mappedType != null) {
column.setMappedTypeCode(mappedType);
} else {
column.setMappedTypeCode((Integer) values.get("DATA_TYPE"));
}
column.setJdbcTypeCode((Integer) values.get("DATA_TYPE"));
column.setPrecisionRadix(((Integer) values.get("NUM_PREC_RADIX")).intValue());
String typeName = (String) values.get("TYPE_NAME");
String columnSize = (String) values.get("COLUMN_SIZE");
int decimalDigits = ((Integer) values.get("DECIMAL_DIGITS")).intValue();
try {
platformColumn.setType(typeName);
if (isNotBlank(columnSize)) {
platformColumn.setSize(Integer.parseInt(columnSize));
}
platformColumn.setDecimalDigits(decimalDigits);
column.addPlatformColumn(platformColumn);
} catch (Exception ex) {
log.warn(ex.getMessage(), ex);
}
column.setJdbcTypeName(typeName);
if (columnSize == null) {
columnSize = (String) _defaultSizes.get(new Integer(column.getMappedTypeCode()));
}
// we're setting the size after the precision and radix in case
// the database prefers to return them in the size value
column.setSize(columnSize);
if (decimalDigits != 0) {
// if there is a scale value, set it after the size (which probably
// did not contain
// a scale specification)
column.setScale(decimalDigits);
}
column.setRequired("NO".equalsIgnoreCase(((String) values.get("IS_NULLABLE")).trim()));
column.setDescription((String) values.get("REMARKS"));
return column;
}
/*
* Retrieves the names of the columns that make up the primary key for a
* given table.
*
* @param metaData The database meta data
*
* @param tableName The name of the table from which to retrieve PK
* information
*
* @return The primary key column names
*/
protected Collection readPrimaryKeyNames(DatabaseMetaDataWrapper metaData,
String tableName) throws SQLException {
List pks = new ArrayList();
ResultSet pkData = null;
try {
pkData = metaData.getPrimaryKeys(getTableNamePattern(tableName));
while (pkData.next()) {
Map values = readMetaData(pkData, getColumnsForPK());
pks.add(readPrimaryKeyName(metaData, values));
}
} finally {
close(pkData);
}
return pks;
}
/*
* Extracts a primary key name from the result set.
*
* @param metaData The database meta data
*
* @param values The primary key meta data values as defined by {@link
* #getColumnsForPK()}
*
* @return The primary key name
*/
protected String readPrimaryKeyName(DatabaseMetaDataWrapper metaData, Map values)
throws SQLException {
return (String) values.get("COLUMN_NAME");
}
/*
* Retrieves the foreign keys of the indicated table.
*
* @param metaData The database meta data
*
* @param tableName The name of the table from which to retrieve FK
* information
*
* @return The foreign keys
*/
protected Collection readForeignKeys(Connection connection,
DatabaseMetaDataWrapper metaData, String tableName) throws SQLException {
Map fks = new LinkedHashMap();
ResultSet fkData = null;
try {
fkData = metaData.getForeignKeys(tableName);
while (fkData.next()) {
Map values = readMetaData(fkData, getColumnsForFK());
readForeignKey(metaData, values, fks);
}
} finally {
close(fkData);
}
return fks.values();
}
/*
* Reads the next foreign key spec from the result set.
*
* @param metaData The database meta data
*
* @param values The foreign key meta data as defined by {@link
* #getColumnsForFK()}
*
* @param knownFks The already read foreign keys for the current table
*/
protected void readForeignKey(DatabaseMetaDataWrapper metaData, Map values,
Map knownFks) throws SQLException {
String fkName = (String) values.get("FK_NAME");
ForeignKey fk = (ForeignKey) knownFks.get(fkName);
if (fk == null) {
fk = new ForeignKey(fkName);
fk.setForeignTableName((String) values.get("PKTABLE_NAME"));
knownFks.put(fkName, fk);
}
Reference ref = new Reference();
ref.setForeignColumnName((String) values.get("PKCOLUMN_NAME"));
ref.setLocalColumnName((String) values.get("FKCOLUMN_NAME"));
if (values.containsKey("KEY_SEQ")) {
ref.setSequenceValue(((Short) values.get("KEY_SEQ")).intValue());
}
fk.addReference(ref);
}
/*
* Determines the indices for the indicated table.
*
* @param metaData The database meta data
*
* @param tableName The name of the table
*
* @return The list of indices
*/
protected Collection readIndices(Connection connection,
DatabaseMetaDataWrapper metaData, String tableName) throws SQLException {
Map indices = new LinkedHashMap();
ResultSet indexData = null;
try {
indexData = metaData.getIndices(getTableNamePattern(tableName), false, false);
while (indexData.next()) {
Map values = readMetaData(indexData, getColumnsForIndex());
readIndex(metaData, values, indices);
}
} finally {
close(indexData);
}
return indices.values();
}
/*
* Reads the next index spec from the result set.
*
* @param metaData The database meta data
*
* @param values The index meta data as defined by {@link
* #getColumnsForIndex()}
*
* @param knownIndices The already read indices for the current table
*/
protected void readIndex(DatabaseMetaDataWrapper metaData, Map values,
Map knownIndices) throws SQLException {
Short indexType = (Short) values.get("TYPE");
// we're ignoring statistic indices
if ((indexType != null) && (indexType.shortValue() == DatabaseMetaData.tableIndexStatistic)) {
return;
}
String indexName = (String) values.get("INDEX_NAME");
if (indexName != null) {
IIndex index = (IIndex) knownIndices.get(indexName);
if (index == null) {
if (((Boolean) values.get("NON_UNIQUE")).booleanValue()) {
index = new NonUniqueIndex();
} else {
index = new UniqueIndex();
}
index.setName(indexName);
knownIndices.put(indexName, index);
}
IndexColumn indexColumn = new IndexColumn();
String columnName = (String) values.get("COLUMN_NAME");
if (columnName.startsWith("\"") && columnName.endsWith("\"")) {
columnName = columnName.substring(1, columnName.length() - 1);
}
indexColumn.setName(columnName);
if (values.containsKey("ORDINAL_POSITION")) {
indexColumn.setOrdinalPosition(((Short) values.get("ORDINAL_POSITION")).intValue());
}
index.addColumn(indexColumn);
}
}
/*
* Reads the indicated columns from the result set.
*
* @param resultSet The result set
*
* @param columnDescriptors The descriptors of the columns to read
*
* @return The read values keyed by the column name
*/
protected Map readMetaData(ResultSet resultSet,
List columnDescriptors) throws SQLException {
HashMap values = new HashMap();
ResultSetMetaData meta = resultSet.getMetaData();
int columnCount = meta.getColumnCount();
Set processed = new HashSet(columnCount);
for (int i = 1; i <= columnCount; i++) {
boolean foundMetaDataDescriptor = false;
String columnName = meta.getColumnName(i);
for (MetaDataColumnDescriptor metaDataColumnDescriptor : columnDescriptors) {
if (metaDataColumnDescriptor.getName().equals(columnName)) {
foundMetaDataDescriptor = true;
values.put(metaDataColumnDescriptor.getName(),
metaDataColumnDescriptor.readColumn(resultSet));
processed.add(columnName);
break;
}
}
/*
* Put all metadata values into the map for easy debugging
* of drivers that return nonstandard names
*/
if (!foundMetaDataDescriptor) {
values.put(columnName, resultSet.getObject(i));
}
}
for (MetaDataColumnDescriptor metaDataColumnDescriptor : columnDescriptors) {
if (!processed.contains(metaDataColumnDescriptor.getName())) {
values.put(metaDataColumnDescriptor.getName(),
metaDataColumnDescriptor.readColumn(resultSet));
}
}
return values;
}
protected void determineAutoIncrementFromResultSetMetaData(Connection conn, Table table,
final Column columnsToCheck[]) throws SQLException {
determineAutoIncrementFromResultSetMetaData(conn, table, columnsToCheck, ".");
}
/*
* Helper method that determines the auto increment status for the given
* columns via the {@link ResultSetMetaData#isAutoIncrement(int)} method.
*
* Fix problems following problems: 1) identifiers that use keywords 2)
* different catalog and schema 3) different catalog separator character *
*
* @param table The table
*
* @param columnsToCheck The columns to check (e.g. the primary key columns)
*/
protected void determineAutoIncrementFromResultSetMetaData(Connection conn, Table table,
final Column columnsToCheck[], String catalogSeparator) throws SQLException {
StringBuilder query = new StringBuilder();
try {
if (columnsToCheck == null || columnsToCheck.length == 0) {
return;
}
query.append("SELECT ");
for (int idx = 0; idx < columnsToCheck.length; idx++) {
if (idx > 0) {
query.append(",");
}
query.append("t.");
appendIdentifier(query, columnsToCheck[idx].getName());
}
query.append(" FROM ");
if (table.getCatalog() != null && !table.getCatalog().trim().equals("")) {
appendIdentifier(query, table.getCatalog());
query.append(catalogSeparator);
}
if (table.getSchema() != null && !table.getSchema().trim().equals("")) {
appendIdentifier(query, table.getSchema()).append(".");
}
appendIdentifier(query, table.getName()).append(" t WHERE 1 = 0");
Statement stmt = null;
try {
stmt = conn.createStatement();
if (log.isDebugEnabled()) {
log.debug(
"Running the following query to get metadata about whether a column is an auto increment column: \n{}",
query);
}
ResultSet rs = null;
try {
rs = stmt.executeQuery(query.toString());
ResultSetMetaData rsMetaData = rs.getMetaData();
for (int idx = 0; idx < columnsToCheck.length; idx++) {
if (log.isDebugEnabled()) {
log.debug(columnsToCheck[idx] + " is auto increment? "
+ rsMetaData.isAutoIncrement(idx + 1));
}
if (rsMetaData.isAutoIncrement(idx + 1)) {
columnsToCheck[idx].setAutoIncrement(true);
}
}
} finally {
close(rs);
}
} finally {
close(stmt);
}
} catch (SQLException ex) {
StringBuilder msg = new StringBuilder(
"Failed to determine auto increment columns using this query: '" + query
+ "'. This is probably not harmful, but should be fixed. ");
msg.append("\n");
msg.append(table.toString());
if (columnsToCheck != null) {
for (Column col : columnsToCheck) {
msg.append("\n");
msg.append(col.toString());
}
}
log.warn(msg.toString(), ex);
}
}
public StringBuilder appendIdentifier(StringBuilder query, String identifier) {
if (getPlatform().getDdlBuilder().isDelimitedIdentifierModeOn()) {
query.append(getPlatformInfo().getDelimiterToken());
}
query.append(identifier);
if (getPlatform().getDdlBuilder().isDelimitedIdentifierModeOn()) {
query.append(getPlatformInfo().getDelimiterToken());
}
return query;
}
/*
* Replaces a specific character sequence in the given text with the
* character sequence whose escaped version it is.
*
* @param text The text
*
* @param unescaped The unescaped string, e.g. "'"
*
* @param escaped The escaped version, e.g. "''"
*
* @return The resulting text
*/
protected String unescape(String text, String unescaped, String escaped) {
String result = text;
// we need special handling if the single quote is escaped via a double
// single quote
if (result != null) {
if (escaped.equals("''")) {
if ((result.length() > 2) && result.startsWith("'") && result.endsWith("'")) {
result = "'"
+ StringUtils.replace(result.substring(1, result.length() - 1),
escaped, unescaped) + "'";
} else {
result = StringUtils.replace(result, escaped, unescaped);
}
} else {
result = StringUtils.replace(result, escaped, unescaped);
}
}
return result;
}
public List getTableTypes() {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return sqlTemplate.execute(new IConnectionCallback>() {
public List execute(Connection connection) throws SQLException {
ArrayList types = new ArrayList();
DatabaseMetaData meta = connection.getMetaData();
ResultSet rs = null;
try {
rs = meta.getTableTypes();
while (rs.next()) {
types.add(rs.getString(1));
}
return types;
} finally {
JdbcSqlTemplate.close(rs);
}
}
});
}
public List getCatalogNames() {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return sqlTemplate.execute(new IConnectionCallback>() {
public List execute(Connection connection) throws SQLException {
ArrayList catalogs = new ArrayList();
DatabaseMetaData meta = connection.getMetaData();
ResultSet rs = null;
try {
rs = meta.getCatalogs();
while (rs.next()) {
catalogs.add(rs.getString(1));
}
return catalogs;
} finally {
JdbcSqlTemplate.close(rs);
}
}
});
}
public List getSchemaNames(final String catalog) {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return sqlTemplate.execute(new IConnectionCallback>() {
public List execute(Connection connection) throws SQLException {
ArrayList schemas = new ArrayList();
DatabaseMetaData meta = connection.getMetaData();
ResultSet rs = null;
try {
rs = meta.getSchemas();
while (rs.next()) {
int columnCount = rs.getMetaData().getColumnCount();
String schema = rs.getString(1);
String schemaCatalog = null;
if (columnCount > 1) {
schemaCatalog = rs.getString(2);
}
if ((StringUtils.isBlank(schemaCatalog) || StringUtils.isBlank(catalog)) && !schemas.contains(schema)) {
schemas.add(schema);
} else if (StringUtils.isNotBlank(schemaCatalog)
&& schemaCatalog.equals(catalog)) {
schemas.add(schema);
}
}
return schemas;
} finally {
close(rs);
}
}
});
}
public List getTableNames(final String catalog, final String schema,
final String[] tableTypes) {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return sqlTemplate.execute(new IConnectionCallback>() {
public List execute(Connection connection) throws SQLException {
ArrayList list = new ArrayList();
DatabaseMetaData meta = connection.getMetaData();
ResultSet rs = null;
try {
rs = meta.getTables(catalog, schema, null, tableTypes);
while (rs.next()) {
String tableName = rs.getString("TABLE_NAME");
list.add(tableName);
}
return list;
} finally {
close(rs);
}
}
});
}
public List getColumnNames(final String catalog, final String schema, final String tableName) {
JdbcSqlTemplate sqlTemplate = (JdbcSqlTemplate) platform.getSqlTemplate();
return sqlTemplate.execute(new IConnectionCallback>() {
public List execute(Connection connection) throws SQLException {
ArrayList list = new ArrayList();
DatabaseMetaData meta = connection.getMetaData();
ResultSet rs = null;
try {
rs = meta.getColumns(catalog, schema, tableName, null);
while (rs.next()) {
String tableName = rs.getString("COLUMN_NAME");
list.add(tableName);
}
return list;
} finally {
close(rs);
}
}
});
}
}