Author: tomdz Date: Tue Dec 27 09:16:30 2005 New Revision: 359242 URL: http://svn.apache.org/viewcvs?rev=359242&view=rev Log: Started restructuring of the jdbc model reader to make it easier to sub class it and redefine portions for specific platforms
Added: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Database.java db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DerbyPlatform.java db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Database.java URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Database.java?rev=359242&r1=359241&r2=359242&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Database.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Database.java Tue Dec 27 09:16:30 2005 @@ -19,6 +19,7 @@ import java.io.Serializable; import java.sql.Types; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -199,6 +200,19 @@ if (table != null) { _tables.add(idx, table); + } + } + + /** + * Adds the given tables. + * + * @param tables The tables to add + */ + public void addTables(Collection tables) + { + for (Iterator it = tables.iterator(); it.hasNext();) + { + addTable((Table)it.next()); } } Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java?rev=359242&r1=359241&r2=359242&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java Tue Dec 27 09:16:30 2005 @@ -214,6 +214,19 @@ } /** + * Adds the given columns. + * + * @param columns The columns + */ + public void addColumns(Collection columns) + { + for (Iterator it = columns.iterator(); it.hasNext();) + { + addColumn((Column)it.next()); + } + } + + /** * Removes the given column. * * @param column The column to remove @@ -295,6 +308,19 @@ } /** + * Adds the given foreign keys. + * + * @param foreignKeys The foreign keys + */ + public void addForeignKeys(Collection foreignKeys) + { + for (Iterator it = foreignKeys.iterator(); it.hasNext();) + { + addForeignKey((ForeignKey)it.next()); + } + } + + /** * Removes the given foreign key. * * @param foreignKey The foreign key to remove @@ -362,6 +388,19 @@ if (index != null) { _indices.add(idx, index); + } + } + + /** + * Adds the given indices. + * + * @param indices The indices + */ + public void addIndices(Collection indices) + { + for (Iterator it = indices.iterator(); it.hasNext();) + { + addIndex((Index)it.next()); } } Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DerbyPlatform.java URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DerbyPlatform.java?rev=359242&r1=359241&r2=359242&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DerbyPlatform.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DerbyPlatform.java Tue Dec 27 09:16:30 2005 @@ -27,6 +27,10 @@ /** * The platform implementation for Derby. * + * TODO: Add jdbc model reader refinement for auto-increment columns: + * * COLUMN_DEF = GENERATED_BY_DEFAULT -> 'GENERATED BY DEFAULT AS IDENTITY' + * * COLUMN_DEF = AUTOINCREMENT: start 1 increment 1 -> 'GENERATED ALWAYS AS IDENTITY' + * * @author Thomas Dudziak * @version $Revision: 231306 $ */ Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java?rev=359242&r1=359241&r2=359242&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java Tue Dec 27 09:16:30 2005 @@ -18,20 +18,16 @@ import java.sql.Connection; 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.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.commons.collections.map.LinkedMap; +import org.apache.commons.collections.map.ListOrderedMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ddlutils.model.Column; @@ -56,6 +52,17 @@ /** The Log to which logging calls will be made. */ private final Log _log = LogFactory.getLog(JdbcModelReader.class); + /** 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; + /** Contains default column sizes (minimum sizes that a JDBC-compliant db must support). */ private HashMap _defaultSizes = new HashMap(); /** The default database catalog to read. */ @@ -64,8 +71,6 @@ private String _defaultSchemaPattern = "%"; /** The table types to recognize per default. */ private String[] _defaultTableTypes = { "TABLE" }; - /** The pattern to recognize when parsing a default value. */ - private Pattern _defaultPattern = Pattern.compile("\\(\\'?(.*?)\\'?\\)"); /** * Creates a new model reader instance. @@ -85,6 +90,119 @@ _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 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.<br/> + * 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("TABLE_CAT", Types.VARCHAR)); + result.add(new MetaDataColumnDescriptor("TABLE_SCHEM", 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.<br/> + * 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_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("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.<br/> + * 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)); + + 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.<br/> + * 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)); + 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.<br/> + * 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)); + 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)); + + + return result; } /** @@ -149,6 +267,56 @@ } /** + * 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 @@ -195,23 +363,20 @@ { db.setName(name); } - for (Iterator it = getTables(connection, catalog, schema, tableTypes).iterator(); it.hasNext();) - { - db.addTable((Table)it.next()); - } + db.addTables(readTables(connection, catalog, schema, tableTypes)); return db; } /** - * Returns a list of [EMAIL PROTECTED] Table} instances for the tables in the database. + * Reads the tables from the database metadata. * * @param connection The connection * @param catalog The catalog to acess in the database; use <code>null</code> for the default value * @param schemaPattern The schema(s) to acess in the database; use <code>null</code> for the default value * @param tableTypes The table types to process; use <code>null</code> or an empty list for the default ones - * @return The list of tables + * @return The tables */ - private List getTables(Connection connection, String catalog, String schemaPattern, String[] tableTypes) throws SQLException + protected Collection readTables(Connection connection, String catalog, String schemaPattern, String[] tableTypes) throws SQLException { ResultSet tableData = null; @@ -226,41 +391,15 @@ tableData = metaData.getTables("%"); - Set availableColumns = determineAvailableColumns(tableData); - List tables = new ArrayList(); + List tables = new ArrayList(); while (tableData.next()) { - String tableName = getValueAsString(tableData, "TABLE_NAME", availableColumns, null); - - if ((tableName != null) && (tableName.length() > 0)) - { - Table table = new Table(); - - table.setName(tableName); - table.setType(getValueAsString(tableData, "TABLE_TYPE", availableColumns, "UNKNOWN")); - table.setCatalog(getValueAsString(tableData, "TABLE_CAT", availableColumns, null)); - table.setSchema(getValueAsString(tableData, "TABLE_SCHEM", availableColumns, null)); - table.setDescription(getValueAsString(tableData, "REMARKS", availableColumns, "")); - tables.add(table); - } - } - - for (Iterator it = tables.iterator(); it.hasNext();) - { - Table table = (Table)it.next(); + Table table = readTable(metaData, tableData); - for (Iterator columnIt = getColumnsForTable(metaData, table.getName()).iterator(); columnIt.hasNext();) - { - table.addColumn((Column)columnIt.next()); - } - for (Iterator fkIt = getForeignKeysForTable(metaData, table.getName()).iterator(); fkIt.hasNext();) + if (table != null) { - table.addForeignKey((ForeignKey)fkIt.next()); - } - for (Iterator idxIt = getIndicesForTable(metaData, table.getName()).iterator(); idxIt.hasNext();) - { - table.addIndex((Index)idxIt.next()); + tables.add(table); } } return tables; @@ -275,13 +414,50 @@ } /** - * Returns a list of [EMAIL PROTECTED] Column} instances for the indicated table. + * Reads the next table from the meta data. + * + * @param metaData The database meta data + * @param tableMetaData The result set containing the table metadata + * @return The table or <code>null</code> if the result set row did not contain a valid table + */ + protected Table readTable(DatabaseMetaDataWrapper metaData, ResultSet tableMetaData) throws SQLException + { + Map values = readColumns(tableMetaData, getColumnsForTable()); + String tableName = (String)values.get("TABLE_NAME"); + Table table = null; + + if ((tableName != null) && (tableName.length() > 0)) + { + table = new Table(); + + table.setName(tableName); + table.setType((String)values.get("TABLE_TYPE")); + table.setCatalog((String)values.get("TABLE_CAT")); + table.setSchema((String)values.get("TABLE_SCHEM")); + table.setDescription((String)values.get("REMARKS")); + + table.addColumns(readColumns(metaData, tableName)); + table.addForeignKeys(readForeignKeys(metaData, tableName)); + table.addIndices(readIndices(metaData, tableName)); + + Collection primaryKeys = readPrimaryKeyNames(metaData, tableName); + + for (Iterator it = primaryKeys.iterator(); it.hasNext();) + { + table.findColumn((String)it.next(), true).setPrimaryKey(true); + } + } + return table; + } + + /** + * Reads the column definitions for the indicated table. * * @param metaData The database meta data * @param tableName The name of the table - * @return The list of columns + * @return The columns */ - private List getColumnsForTable(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException + private Collection readColumns(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException { ResultSet columnData = null; @@ -289,57 +465,11 @@ { columnData = metaData.getColumns(tableName, null); - Set availableColumns = determineAvailableColumns(columnData); - List columns = new ArrayList(); - List primaryKeys = getPrimaryKeysForTable(metaData, tableName); + List columns = new ArrayList(); while (columnData.next()) { - Column col = new Column(); - - // 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 - String columnDefaultValue = getValueAsString(columnData, "COLUMN_DEF", availableColumns, null); - - if (columnDefaultValue != null) - { - // Sometimes the default comes back with parenthesis around it (jTDS/mssql) - Matcher m = _defaultPattern.matcher(columnDefaultValue); - - if (m.matches()) - { - columnDefaultValue = m.group(1); - } - col.setDefaultValue(columnDefaultValue); - } - col.setName(getValueAsString(columnData, "COLUMN_NAME", availableColumns, "UNKNOWN")); - col.setTypeCode(getValueAsInt(columnData, "DATA_TYPE", availableColumns, java.sql.Types.OTHER)); - col.setPrecisionRadix(getValueAsInt(columnData, "NUM_PREC_RADIX", availableColumns, 10)); - - int scale = getValueAsInt(columnData, "DECIMAL_DIGITS", availableColumns, 0); - - // we're setting the size after the precision and radix in case - // the database prefers to return them in the size value - col.setSize(getValueAsString(columnData, "COLUMN_SIZE", availableColumns, (String)_defaultSizes.get(new Integer(col.getTypeCode())))); - if (scale != 0) - { - // if there is a scale value, set it after the size (which probably did not contain - // a scale specification) - col.setScale(scale); - } - col.setRequired("NO".equalsIgnoreCase(getValueAsString(columnData, "IS_NULLABLE", availableColumns, "YES").trim())); - col.setDescription(getValueAsString(columnData, "REMARKS", availableColumns, null)); - if (primaryKeys.contains(col.getName())) - { - col.setPrimaryKey(true); - } - else - { - col.setPrimaryKey(false); - } - - columns.add(col); + columns.add(readColumn(metaData, columnData)); } return columns; } @@ -353,14 +483,51 @@ } /** - * Retrieves a list of the columns composing the primary key for a given - * table. + * Extracts a column definition from the result set. + * + * @param metaData The database meta data + * @param columnData The column meta data result set + * @return The column + */ + protected Column readColumn(DatabaseMetaDataWrapper metaData, ResultSet columnData) throws SQLException + { + Column column = new Column(); + Map values = readColumns(columnData, getColumnsForColumn()); + + column.setName((String)values.get("COLUMN_NAME")); + column.setDefaultValue((String)values.get("COLUMN_DEF")); + column.setTypeCode(((Integer)values.get("DATA_TYPE")).intValue()); + column.setPrecisionRadix(((Integer)values.get("NUM_PREC_RADIX")).intValue()); + + String size = (String)values.get("COLUMN_SIZE"); + int scale = ((Integer)values.get("DECIMAL_DIGITS")).intValue(); + + if (size == null) + { + size = (String)_defaultSizes.get(new Integer(column.getTypeCode())); + } + // we're setting the size after the precision and radix in case + // the database prefers to return them in the size value + column.setSize(size); + if (scale != 0) + { + // if there is a scale value, set it after the size (which probably did not contain + // a scale specification) + column.setScale(scale); + } + column.setRequired("NO".equalsIgnoreCase(((String)values.get("IS_NULLABLE")).trim())); + column.setDescription((String)values.get("REMAKRS")); + 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 list of the primary key column names + * @return The primary key column names */ - private List getPrimaryKeysForTable(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException + protected Collection readPrimaryKeyNames(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException { List pks = new ArrayList(); ResultSet pkData = null; @@ -370,13 +537,9 @@ pkData = metaData.getPrimaryKeys(tableName); while (pkData.next()) { - pks.add(pkData.getString("COLUMN_NAME")); + pks.add(readPrimaryKeyName(metaData, pkData)); } } - catch (SQLException ex) - { - _log.warn("Could not determine the primary keys of table "+tableName, ex); - } finally { if (pkData != null) @@ -388,56 +551,39 @@ } /** - * Retrieves a list of the foreign keys of the indicated table. + * Extracts a primary key name from the result set. + * + * @param metaData The database meta data + * @param pkData The result set containing the meta data for the current pk definition + * @return The primary key name + */ + protected String readPrimaryKeyName(DatabaseMetaDataWrapper metaData, ResultSet pkData) throws SQLException + { + Map values = readColumns(pkData, getColumnsForPK()); + + 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 list of foreign keys + * @return The foreign keys */ - private List getForeignKeysForTable(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException + protected Collection readForeignKeys(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException { - List fks = new ArrayList(); + Map fks = new ListOrderedMap(); ResultSet fkData = null; try { fkData = metaData.getForeignKeys(tableName); - Set availableColumns = determineAvailableColumns(fkData); - String prevPkTable = null; - ForeignKey currFk = null; - while (fkData.next()) { - String pkTable = getValueAsString(fkData, "PKTABLE_NAME", availableColumns, null); - short keySequence = getValueAsShort(fkData, "KEY_SEQ", availableColumns, (short)0); - - // a new foreign key definition can only be identified by the changed referenced table - if (!pkTable.equals(prevPkTable) || (keySequence == 1)) - { - if (currFk != null) - { - fks.add(currFk); - } - currFk = new ForeignKey(getValueAsString(fkData, "FK_NAME", availableColumns, null)); - currFk.setForeignTableName(pkTable); - prevPkTable = pkTable; - } - Reference ref = new Reference(); - - ref.setForeignColumnName(getValueAsString(fkData, "PKCOLUMN_NAME", availableColumns, null)); - ref.setLocalColumnName(getValueAsString(fkData, "FKCOLUMN_NAME", availableColumns, null)); - currFk.addReference(ref); + readForeignKey(metaData, fkData, fks); } - if (currFk != null) - { - fks.add(currFk); - currFk = null; - } - } - catch (SQLException ex) - { - _log.warn("Could not determine the foreignkeys of table "+tableName, ex); } finally { @@ -446,7 +592,36 @@ fkData.close(); } } - return fks; + return fks.values(); + } + + /** + * Reads the next foreign key spec from the result set. + * + * @param metaData The database meta data + * @param fkData The foreign key meta data + * @param lastFk The foreign key that was read last + */ + protected void readForeignKey(DatabaseMetaDataWrapper metaData, ResultSet fkData, Map knownFks) throws SQLException + { + Map values = readColumns(fkData, getColumnsForFK()); + 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")); + } + + Reference ref = new Reference(); + short position = ((Short)values.get("KEY_SEQ")).shortValue(); + + ref.setForeignColumnName((String)values.get("PKCOLUMN_NAME")); + ref.setLocalColumnName((String)values.get("FKCOLUMN_NAME")); + + // TODO: use position + fk.addReference(ref); } /** @@ -456,51 +631,19 @@ * @param tableName The name of the table * @return The list of indices */ - private List getIndicesForTable(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException + protected Collection readIndices(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException { + Map indices = new ListOrderedMap(); ResultSet indexData = null; - List indices = new ArrayList(); try { indexData = metaData.getIndices(tableName, false, false); - Set availableColumns = determineAvailableColumns(indexData); - Map indicesByName = new LinkedMap(); - while (indexData.next()) { - String indexName = getValueAsString(indexData, "INDEX_NAME", availableColumns, null); - boolean isUnique = !getValueAsBoolean(indexData, "NON_UNIQUE", availableColumns, true); - Index index = (Index)indicesByName.get(indexName); - - if ((index == null) && (indexName != null)) - { - if (isUnique) - { - index = new UniqueIndex(); - } - else - { - index = new NonUniqueIndex(); - } - - index.setName(indexName); - indicesByName.put(indexName, index); - } - if (index != null) - { - IndexColumn ic = new IndexColumn(); - - ic.setName(getValueAsString(indexData, "COLUMN_NAME", availableColumns, null)); - index.addColumn(ic); - } + readIndex(metaData, indexData, indices); } - indices.addAll(indicesByName.values()); - } - catch (SQLException ex) - { - _log.trace("Could determine the indices for the table "+tableName, ex); } finally { @@ -509,85 +652,62 @@ indexData.close(); } } - return indices; + return indices.values(); } /** - * Determines the columns available in the given result set, and returns them (in upper case) - * in a set. - * - * @param data The result set - * @return The columns present in the result set + * Reads the next index spec from the result set. + * + * @param metaData The database meta data + * @param indexData The index meta data + * @param knownIndices The already known indices */ - private Set determineAvailableColumns(ResultSet data) throws SQLException + protected void readIndex(DatabaseMetaDataWrapper metaData, ResultSet indexData, Map knownIndices) throws SQLException { - Set result = new HashSet(); - ResultSetMetaData metaData = data.getMetaData(); + Map values = readColumns(indexData, getColumnsForIndex()); + String indexName = (String)values.get("INDEX_NAME"); + Index index = (Index)knownIndices.get(indexName); - for (int idx = 1; idx <= metaData.getColumnCount(); idx++) + if ((index == null) && (indexName != null)) { - result.add(metaData.getColumnName(idx).toUpperCase()); + if (((Boolean)values.get("NON_UNIQUE")).booleanValue()) + { + index = new NonUniqueIndex(); + } + else + { + index = new UniqueIndex(); + } + + index.setName(indexName); + knownIndices.put(indexName, index); } - return result; - } - /** - * Retrieves the value of the specified column as a string. If the column is not present, then - * the default value is returned. - * - * @param data The data - * @param columnName The name of the column - * @param availableColumns The available columns - * @param defaultValue The default value to use if the column is not present - * @return The value - */ - private String getValueAsString(ResultSet data, String columnName, Set availableColumns, String defaultValue) throws SQLException - { - return availableColumns.contains(columnName) ? data.getString(columnName) : defaultValue; - } + IndexColumn ic = new IndexColumn(); + short position = ((Short)values.get("ORDINAL_POSITION")).shortValue(); - /** - * Retrieves the value of the specified column as an integer. If the column is not present, then - * the default value is returned. - * - * @param data The data - * @param columnName The name of the column - * @param availableColumns The available columns - * @param defaultValue The default value to use if the column is not present - * @return The value - */ - private int getValueAsInt(ResultSet data, String columnName, Set availableColumns, int defaultValue) throws SQLException - { - return availableColumns.contains(columnName) ? data.getInt(columnName) : defaultValue; + ic.setName((String)values.get("COLUMN_NAME")); + // TODO: use position + index.addColumn(ic); } /** - * Retrieves the value of the specified column as a short integer. If the column is not present, then - * the default value is returned. + * Reads the indicated columns from the result set. * - * @param data The data - * @param columnName The name of the column - * @param availableColumns The available columns - * @param defaultValue The default value to use if the column is not present - * @return The value + * @param resultSet The result set + * @param columnDescriptors The dscriptors of the columns to read + * @return The read values keyed by the column name */ - private short getValueAsShort(ResultSet data, String columnName, Set availableColumns, short defaultValue) throws SQLException + protected Map readColumns(ResultSet resultSet, List columnDescriptors) throws SQLException { - return availableColumns.contains(columnName) ? data.getShort(columnName) : defaultValue; - } + HashMap values = new HashMap(); - /** - * Retrieves the value of the specified column as a boolean value. If the column is not present, then - * the default value is returned. - * - * @param data The data - * @param columnName The name of the column - * @param availableColumns The available columns - * @param defaultValue The default value to use if the column is not present - * @return The value - */ - private boolean getValueAsBoolean(ResultSet data, String columnName, Set availableColumns, boolean defaultValue) throws SQLException - { - return availableColumns.contains(columnName) ? data.getBoolean(columnName) : defaultValue; + for (Iterator it = columnDescriptors.iterator(); it.hasNext();) + { + MetaDataColumnDescriptor descriptor = (MetaDataColumnDescriptor)it.next(); + + values.put(descriptor.getName(), descriptor.readColumn(resultSet)); + } + return values; } } Added: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java URL: http://svn.apache.org/viewcvs/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java?rev=359242&view=auto ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java (added) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/MetaDataColumnDescriptor.java Tue Dec 27 09:16:30 2005 @@ -0,0 +1,117 @@ +package org.apache.ddlutils.platform; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; + +/** + * Describes a column in a metadata result set. + * + * @author tomdz + * @version $Revision: $ + */ +public class MetaDataColumnDescriptor +{ + /** The name of the column. */ + private String _columnName; + /** The jdbc type to read from the result set. */ + private int _jdbcType; + /** The default value if the column is not present in the result set. */ + private Object _defaultValue; + + /** + * Creates a new descriptor instance. + * + * @param columnName The name of the column + * @param jdbcType The jdbc type for reading from the result set, one of + * VARCHAR, INTEGER, TINYINT, BIT + */ + public MetaDataColumnDescriptor(String columnName, int jdbcType) + { + this(columnName, jdbcType, null); + } + + /** + * Creates a new descriptor instance. + * + * @param columnName The name of the column + * @param jdbcType The jdbc type for reading from the result set, one of + * VARCHAR, INTEGER, TINYINT, BIT + * @param defaultValue The default value if the column is not present in the result set + */ + public MetaDataColumnDescriptor(String columnName, int jdbcType, Object defaultValue) + { + _columnName = columnName.toUpperCase(); + _jdbcType = jdbcType; + _defaultValue = defaultValue; + } + + /** + * Returns the name. + * + * @return The name + */ + public String getName() + { + return _columnName; + } + + /** + * Returns the default value. + * + * @return The default value + */ + public Object getDefaultValue() + { + return _defaultValue; + } + + /** + * Returns the jdbc type to read from the result set. + * + * @return The jdbc type + */ + public int getJdbcType() + { + return _jdbcType; + } + + /** + * Reads the column from the result set. + * + * @param resultSet The result set + * @return The column value or the default value if the column is not present in the result set + */ + public Object readColumn(ResultSet resultSet) throws SQLException + { + ResultSetMetaData metaData = resultSet.getMetaData(); + int foundIdx = -1; + + for (int idx = 1; (foundIdx < 0) && (idx <= metaData.getColumnCount()); idx++) + { + if (_columnName.equals(metaData.getColumnName(idx).toUpperCase())) + { + foundIdx = idx; + } + } + if (foundIdx > 0) + { + switch (_jdbcType) + { + case Types.BIT: + return new Boolean(resultSet.getBoolean(foundIdx)); + case Types.INTEGER: + return new Integer(resultSet.getInt(foundIdx)); + case Types.TINYINT: + return new Short(resultSet.getShort(foundIdx)); + default: + return resultSet.getString(foundIdx); + } + } + else + { + return _defaultValue; + } + } +}