Index: core/src/org/apache/ibatis/abator/internal/db/DatabaseIntrospector.java
===================================================================
--- core/src/org/apache/ibatis/abator/internal/db/DatabaseIntrospector.java	(revision 450542)
+++ core/src/org/apache/ibatis/abator/internal/db/DatabaseIntrospector.java	(working copy)
@@ -18,6 +18,8 @@
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.Statement;
 import java.sql.SQLException;
 import java.util.Collection;
 import java.util.HashMap;
@@ -47,82 +49,40 @@
         super();
     }
 
-    public static Collection introspectTables(Connection connection,
-            TableConfiguration tc, JavaTypeResolver javaTypeResolver,
-            List warnings) throws SQLException {
+    private static class Context {
+        Map introspectedTables;
+        Connection connection;
+        DatabaseMetaData dbmd;
+        TableConfiguration tc;
+        LocalNames names;
+        JavaTypeResolver javaTypeResolver;
+        List warnings;
+    }
 
-        Map introspectedTables = new HashMap();
-        DatabaseMetaData dbmd = connection.getMetaData();
-        
-        String localCatalog;
-        String localSchema;
-        String localTableName;
+    private static class LocalNames {
+        String catalog;
+        String schema;
+        String table;
+    }
 
-        if (dbmd.storesLowerCaseIdentifiers()) {
-            localCatalog = tc.getCatalog() == null ? null : tc.getCatalog()
-                    .toLowerCase();
-            localSchema = tc.getSchema() == null ? null : tc.getSchema()
-                    .toLowerCase();
-            localTableName = tc.getTableName() == null ? null : tc
-                    .getTableName().toLowerCase();
-        } else if (dbmd.storesUpperCaseIdentifiers()) {
-            localCatalog = tc.getCatalog() == null ? null : tc.getCatalog()
-                    .toUpperCase();
-            localSchema = tc.getSchema() == null ? null : tc.getSchema()
-                    .toUpperCase();
-            localTableName = tc.getTableName() == null ? null : tc
-                    .getTableName().toUpperCase();
-        } else {
-            localCatalog = tc.getCatalog();
-            localSchema = tc.getSchema();
-            localTableName = tc.getTableName();
-        }
+    private static void introspectTablesDatabaseMetaData(Context ctx)
+        throws SQLException
+    {
+        ResultSet rs = ctx.dbmd.getColumns(ctx.names.catalog,
+                                           ctx.names.schema,
+                                           ctx.names.table,
+                                           null);
 
-        if (tc.isWildcardEscapingEnabled()) {
-            String escapeString = dbmd.getSearchStringEscape();
-            
-            StringBuffer sb = new StringBuffer();
-            StringTokenizer st;
-            if (localSchema != null) {
-                st = new StringTokenizer(localSchema, "_%", true); //$NON-NLS-1$
-                while (st.hasMoreTokens()) {
-                    String token = st.nextToken();
-                    if (token.equals("_") //$NON-NLS-1$
-                            || token.equals("%")) { //$NON-NLS-1$
-                        sb.append(escapeString);
-                    }
-                    sb.append(token);
-                }
-                localSchema = sb.toString();
-            }
-            
-            sb.setLength(0);
-            st = new StringTokenizer(localTableName, "_%", true); //$NON-NLS-1$
-            while (st.hasMoreTokens()) {
-                String token = st.nextToken();
-                if (token.equals("_") //$NON-NLS-1$
-                        || token.equals("%")) { //$NON-NLS-1$
-                    sb.append(escapeString);
-                }
-                sb.append(token);
-            }
-            localTableName = sb.toString();
-        }
-
-        ResultSet rs = dbmd.getColumns(localCatalog, localSchema,
-                localTableName, null);
-
         int returnedColumns = 0;
         while (rs.next()) {
             returnedColumns++;
             
-            ColumnDefinition cd = new ColumnDefinition(tc.getAlias());
+            ColumnDefinition cd = new ColumnDefinition(ctx.tc.getAlias());
 
             cd.setJdbcType(rs.getInt("DATA_TYPE")); //$NON-NLS-1$
             cd.setLength(rs.getInt("COLUMN_SIZE")); //$NON-NLS-1$
             cd.setColumnName(rs.getString("COLUMN_NAME")); //$NON-NLS-1$
-            cd
-                    .setNullable(rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable); //$NON-NLS-1$
+            cd.setNullable(rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable); //$NON-NLS-1$
             cd.setScale(rs.getInt("DECIMAL_DIGITS")); //$NON-NLS-1$
             cd.setTypeName(rs.getString("TYPE_NAME")); //$NON-NLS-1$
             
@@ -135,99 +95,149 @@
             // from the DB for these fields, but nothing was specified on the table
             // configuration, then some sort of DB default is being returned
             // and we don't want that in our SQL
-            FullyQualifiedTable table = new FullyQualifiedTable(
-                    StringUtility.stringHasValue(tc.getCatalog()) ? catalog : null,
-                    StringUtility.stringHasValue(tc.getSchema()) ? schema : null,
-                    tableName, tc.getDomainObjectName(), tc.getAlias());
-            
-            ColumnOverride columnOverride = tc.getColumnOverride(cd
-                    .getColumnName());
+            FullyQualifiedTable table = new FullyQualifiedTable
+                (StringUtility.stringHasValue(ctx.tc.getCatalog()) ? catalog : null,
+                 StringUtility.stringHasValue(ctx.tc.getSchema()) ? schema : null,
+                 tableName, ctx.tc.getDomainObjectName(), ctx.tc.getAlias());
 
-            if (columnOverride == null
-                    || !StringUtility.stringHasValue(columnOverride
-                            .getJavaProperty())) {
-                if ("true".equals(tc.getProperties().get("useActualColumnNames"))) { //$NON-NLS-1$ //$NON-NLS-2$
-                    cd.setJavaProperty(JavaBeansUtil.getValidPropertyName(cd
-                            .getColumnName()));
-                } else {
-                    cd.setJavaProperty(JavaBeansUtil.getCamelCaseString(cd
-                            .getColumnName(), false));
-                }
-            } else {
-                cd.setJavaProperty(columnOverride.getJavaProperty());
-            }
+            processColumn(ctx, table, cd);
+        }
 
-            try {
-                javaTypeResolver.initializeResolvedJavaType(cd);
-            } catch (UnsupportedDataTypeException e) {
-                // if the type is not supported, then we'll report a warning and
-                // ignore the column
-                warnings.add(Messages.getString("Warning.14", //$NON-NLS-1$
-                        table.getFullyQualifiedTableName(),
-                        cd.getColumnName()));
-                continue;
-            }
+        rs.close();
+        
+        if (returnedColumns == 0) {
+            ctx.warnings.add(Messages.getString("Warning.19", ctx.tc.getCatalog(), //$NON-NLS-1$
+                                                ctx.tc.getSchema(), ctx.tc.getTableName()));
+        }
+    }
+    
+    private static void introspectTablesResultSetMetaData(Context ctx)
+        throws SQLException
+    {
+        Statement stmt = ctx.connection.createStatement();
 
-            if (columnOverride != null
-                    && StringUtility.stringHasValue(columnOverride
-                            .getJavaType())) {
-                cd.getResolvedJavaType()
-                        .setFullyQualifiedJavaType(
-                                new FullyQualifiedJavaType(columnOverride
-                                        .getJavaType()));
-            }
+        ResultSet rs = ctx.dbmd.getTables(ctx.names.catalog,
+                                          ctx.names.schema,
+                                          ctx.names.table,
+                                          null);
 
-            if (columnOverride != null
-                    && StringUtility.stringHasValue(columnOverride
-                            .getJdbcType())) {
-                cd.getResolvedJavaType().setJdbcTypeName(
-                        columnOverride.getJdbcType());
-            }
+        while (rs.next()) {
+            String tableName = rs.getString("TABLE_NAME"); //$NON-NLS-1$
 
-            if (columnOverride != null
-                    && StringUtility.stringHasValue(columnOverride
-                            .getTypeHandler())) {
-                cd.setTypeHandler(columnOverride.getTypeHandler());
-            }
+            String sql = "select * from " +
+                StringUtility.composeFullyQualifiedTableName(ctx.names.catalog,
+                                                             ctx.names.schema,
+                                                             tableName);
 
-            if (tc.getGeneratedKey() != null
-                    && tc.getGeneratedKey().isIdentity()
-                    && cd.getColumnName().equalsIgnoreCase(
-                            tc.getGeneratedKey().getColumn())) {
-                cd.setIdentity(true);
-            } else {
-                cd.setIdentity(false);
+            ResultSet rs2 = stmt.executeQuery(sql);
+            ResultSetMetaData rsmd = rs2.getMetaData();
+
+            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
+                ColumnDefinition cd = new ColumnDefinition(ctx.tc.getAlias());
+
+                cd.setJdbcType(rsmd.getColumnType(i));
+                cd.setLength(rsmd.getPrecision(i));
+                cd.setColumnName(rsmd.getColumnName(i));
+                cd.setNullable(rsmd.isNullable(i) == DatabaseMetaData.columnNullable);
+                cd.setScale(rsmd.getScale(i));
+                cd.setTypeName(rsmd.getColumnTypeName(i));
+
+                String catalog = rsmd.getCatalogName(i);
+                String schema = rsmd.getSchemaName(i);
+
+            
+                // we only use the returned catalog and schema if something was actually
+                // specified on the table configuration.  If something was returned
+                // from the DB for these fields, but nothing was specified on the table
+                // configuration, then some sort of DB default is being returned
+                // and we don't want that in our SQL
+                FullyQualifiedTable table = new FullyQualifiedTable
+                    (StringUtility.stringHasValue(ctx.tc.getCatalog()) ? catalog : null,
+                     StringUtility.stringHasValue(ctx.tc.getSchema()) ? schema : null,
+                     tableName, ctx.tc.getDomainObjectName(), ctx.tc.getAlias());
+
+                processColumn(ctx, table, cd);
             }
+        
+            if (rsmd.getColumnCount() == 0) {
+                ctx.warnings.add(Messages.getString("Warning.19", ctx.tc.getCatalog(), //$NON-NLS-1$
+                                                    ctx.tc.getSchema(), ctx.tc.getTableName()));
+            }
 
-            if (!tc.isColumnIgnored(cd.getColumnName())) {
-                IntrospectedTableImpl introspectedTable =
-                    (IntrospectedTableImpl) introspectedTables.get(table.getFullyQualifiedTableName());
-                if (introspectedTable == null) {
-                    introspectedTable = new IntrospectedTableImpl(tc, new ColumnDefinitions(), table);
-                    introspectedTables.put(table.getFullyQualifiedTableName(), introspectedTable);
-                }
-                
-                introspectedTable.getColumnDefinitions().addColumn(cd);
-            }
+            rs2.close();
         }
 
         rs.close();
-        
-        if (returnedColumns == 0) {
-            warnings.add(Messages.getString("Warning.19", tc.getCatalog(), //$NON-NLS-1$
-                    tc.getSchema(), tc.getTableName()));
+        stmt.close();
+    }
+
+    public static Collection introspectTables(Connection connection,
+                                              TableConfiguration tc,
+                                              JavaTypeResolver javaTypeResolver,
+                                              List warnings)
+        throws SQLException
+    {
+        Context ctx = new Context();
+        ctx.connection = connection;
+        ctx.dbmd = connection.getMetaData();
+        ctx.introspectedTables = new HashMap();
+        ctx.tc = tc;
+        ctx.javaTypeResolver = javaTypeResolver;
+        ctx.warnings = warnings;
+
+        String localCatalog;
+        String localSchema;
+        String localTableName;
+
+        if (ctx.dbmd.storesLowerCaseIdentifiers()) {
+            localCatalog = tc.getCatalog() == null ? null : tc.getCatalog()
+                .toLowerCase();
+            localSchema = tc.getSchema() == null ? null : tc.getSchema()
+                .toLowerCase();
+            localTableName = tc.getTableName() == null ? null : tc
+                .getTableName().toLowerCase();
+        } else if (ctx.dbmd.storesUpperCaseIdentifiers()) {
+            localCatalog = tc.getCatalog() == null ? null : tc.getCatalog()
+                .toUpperCase();
+            localSchema = tc.getSchema() == null ? null : tc.getSchema()
+                .toUpperCase();
+            localTableName = tc.getTableName() == null ? null : tc
+                .getTableName().toUpperCase();
+        } else {
+            localCatalog = tc.getCatalog();
+            localSchema = tc.getSchema();
+            localTableName = tc.getTableName();
         }
+
+        if (tc.isWildcardEscapingEnabled()) {
+            String escapeString = ctx.dbmd.getSearchStringEscape();
+            localSchema = escapeString(localSchema, escapeString);
+            localTableName = escapeString(localTableName, escapeString);
+        }
+
+        LocalNames names = new LocalNames();
+        names.catalog = localCatalog;
+        names.schema = localSchema;
+        names.table = localTableName;
+
+        ctx.names = names;
+
+        if ("true".equals(tc.getProperties().get("useResultSetMetaDataIntrospector"))) { //$NON-NLS-1$ //$NON-NLS-2$
+            introspectTablesResultSetMetaData(ctx);
+        } else {
+            introspectTablesDatabaseMetaData(ctx);
+        }
         
-        Iterator iter = introspectedTables.values().iterator();
+        Iterator iter = ctx.introspectedTables.values().iterator();
         while (iter.hasNext()) {
             IntrospectedTableImpl it = (IntrospectedTableImpl) iter.next();
-            calculatePrimaryKey(dbmd, it, warnings);
+            calculatePrimaryKey(ctx.dbmd, it, warnings);
         }
         
         // now introspectedTables has all the columns from all the 
         // tables in the configuration.  Do some validation...
 
-        iter = introspectedTables.entrySet().iterator();
+        iter = ctx.introspectedTables.entrySet().iterator();
         while (iter.hasNext()) {
             Map.Entry entry = (Map.Entry) iter.next();
             
@@ -241,7 +251,7 @@
                 warnings.add(Messages.getString("Warning.1", introspectedTable.getTable().getFullyQualifiedTableName())); //$NON-NLS-1$
                 iter.remove();
             } else if (!cds.hasPrimaryKeyColumns()
-                    && !cds.hasBaseColumns()) {
+                       && !cds.hasBaseColumns()) {
                 // add warning that the table has only BLOB columns, remove from the list
                 warnings.add(Messages.getString("Warning.18", introspectedTable.getTable().getFullyQualifiedTableName())); //$NON-NLS-1$
                 iter.remove();
@@ -252,7 +262,7 @@
             }
         }
 
-        return introspectedTables.values();
+        return ctx.introspectedTables.values();
     }
 
     private static void calculatePrimaryKey(DatabaseMetaData dbmd,
@@ -293,6 +303,24 @@
         }
     }
 
+    private static String escapeString(String s, String escapeString) {
+        if (s == null) {
+            return null;
+        }
+
+        StringBuffer sb = new StringBuffer();
+        StringTokenizer st = new StringTokenizer(s, "_%", true); //$NON-NLS-1$
+        while (st.hasMoreTokens()) {
+            String token = st.nextToken();
+            if (token.equals("_") //$NON-NLS-1$
+                || token.equals("%")) { //$NON-NLS-1$
+                sb.append(escapeString);
+            }
+            sb.append(token);
+        }
+        return sb.toString();
+    }
+
     private static void reportIntrospectionWarnings(
             ColumnDefinitions columnDefinitions,
             TableConfiguration tableConfiguration, 
@@ -331,4 +359,79 @@
             }
         }
     }
+
+    private static void processColumn(Context ctx,
+                                      FullyQualifiedTable table,
+                                      ColumnDefinition cd)
+    {
+        ColumnOverride columnOverride = ctx.tc.getColumnOverride(cd
+                                                             .getColumnName());
+
+        if (columnOverride == null
+            || !StringUtility.stringHasValue(columnOverride
+                                             .getJavaProperty())) {
+            if ("true".equals(ctx.tc.getProperties().get("useActualColumnNames"))) { //$NON-NLS-1$ //$NON-NLS-2$
+                cd.setJavaProperty(JavaBeansUtil.getValidPropertyName(cd
+                                                                      .getColumnName()));
+            } else {
+                cd.setJavaProperty(JavaBeansUtil.getCamelCaseString(cd
+                                                                    .getColumnName(), false));
+            }
+        } else {
+            cd.setJavaProperty(columnOverride.getJavaProperty());
+        }
+
+        try {
+            ctx.javaTypeResolver.initializeResolvedJavaType(cd);
+        } catch (UnsupportedDataTypeException e) {
+            // if the type is not supported, then we'll report a warning and
+            // ignore the column
+            ctx.warnings.add(Messages.getString("Warning.14", //$NON-NLS-1$
+                                            table.getFullyQualifiedTableName(),
+                                            cd.getColumnName()));
+            return;
+        }
+
+        if (columnOverride != null
+            && StringUtility.stringHasValue(columnOverride
+                                            .getJavaType())) {
+            cd.getResolvedJavaType()
+                .setFullyQualifiedJavaType(
+                                           new FullyQualifiedJavaType(columnOverride
+                                                                      .getJavaType()));
+        }
+
+        if (columnOverride != null
+            && StringUtility.stringHasValue(columnOverride
+                                            .getJdbcType())) {
+            cd.getResolvedJavaType().setJdbcTypeName(
+                                                     columnOverride.getJdbcType());
+        }
+
+        if (columnOverride != null
+            && StringUtility.stringHasValue(columnOverride
+                                            .getTypeHandler())) {
+            cd.setTypeHandler(columnOverride.getTypeHandler());
+        }
+
+        if (ctx.tc.getGeneratedKey() != null
+            && ctx.tc.getGeneratedKey().isIdentity()
+            && cd.getColumnName().equalsIgnoreCase(
+                                                   ctx.tc.getGeneratedKey().getColumn())) {
+            cd.setIdentity(true);
+        } else {
+            cd.setIdentity(false);
+        }
+
+        if (!ctx.tc.isColumnIgnored(cd.getColumnName())) {
+            IntrospectedTableImpl introspectedTable =
+                (IntrospectedTableImpl) ctx.introspectedTables.get(table.getFullyQualifiedTableName());
+            if (introspectedTable == null) {
+                introspectedTable = new IntrospectedTableImpl(ctx.tc, new ColumnDefinitions(), table);
+                ctx.introspectedTables.put(table.getFullyQualifiedTableName(), introspectedTable);
+            }
+                
+            introspectedTable.getColumnDefinitions().addColumn(cd);
+        }
+    }
 }
