001    /*
002    // $Id: //open/mondrian/src/main/mondrian/gui/JDBCMetaData.java#9 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2006-2008 Julian Hyde and others
007    // Copyright (C) 2006-2007 Cincom Systems, Inc.
008    // Copyright (C) 2006-2007 JasperSoft
009    // All Rights Reserved.
010    // You must accept the terms of that agreement to use this software.
011    //
012    // Created on April 28, 2006, 2:43 PM
013     */
014    package mondrian.gui;
015    
016    import java.util.*;
017    import java.sql.*;
018    
019    import org.apache.log4j.Logger;
020    
021    /**
022     *
023     * @version $Id: //open/mondrian/src/main/mondrian/gui/JDBCMetaData.java#9 $
024     */
025    public class JDBCMetaData {
026    
027        private static final Logger LOGGER = Logger.getLogger(JDBCMetaData.class);
028    
029        String jdbcDriverClassName = null; //"org.postgresql.Driver"
030        String jdbcConnectionUrl = null; // "jdbc:postgresql://localhost:5432/hello?user=postgres&password=post"
031        String jdbcUsername = null;
032        String jdbcPassword = null;
033    
034        Connection conn = null;
035        DatabaseMetaData md = null;
036    
037        Workbench workbench;
038    
039        /* Map of Schema and its fact tables ::
040         * allFactTableDimensions = [Schema1, Schema2] -> [FactTableT8, FactTable9] -> [ForeignKeys -> PrimaryKeyTable]
041         *
042         * Map of Schema, its tables and their Primary Keys ::
043         * allTablesPKs = [Schema1, Schema2] -> [Tables -> PrimaryKey]
044         *
045         * Map of Schemas, its tables and their columns with their data types
046         * allTablesCols = [Schema1, Schema2] -> [Table1, Table2] -> [Columns -> DataType]
047         *
048         * Map of schemas and their tables
049         * allSchemasMap = [Schema1, Schema2] -> [Table1, Table2]
050         *
051         */
052        private Map allFactTableDimensions = new HashMap(); //unsynchronized, permits null values and null key
053        private Map allTablesPKs        = new HashMap();
054        private Map allTablesCols       = new HashMap();
055        private Map allSchemasMap       = new HashMap();
056    
057        private Vector allSchemas = new Vector();   // Vector of all schemas in the connected database
058    
059        private String errMsg = null;
060        private Database db = new Database();
061    
062        public JDBCMetaData(Workbench wb, String jdbcDriverClassName, String jdbcConnectionUrl, String jdbcUsername, String jdbcPassword) {
063            this.workbench = wb;
064            this.jdbcConnectionUrl = jdbcConnectionUrl;
065            this.jdbcDriverClassName = jdbcDriverClassName;
066            this.jdbcUsername = jdbcUsername;
067            this.jdbcPassword = jdbcPassword;
068    
069            if (initConnection() == null) {
070                setAllSchemas();
071                closeConnection();
072            }
073        }
074    
075        /**
076         * @return the workbench i18n converter
077         */
078        public I18n getResourceConverter() {
079            return workbench.getResourceConverter();
080        }
081    
082        /* Creates a database connection and initializes the meta data details */
083        public String initConnection() {
084            LOGGER.debug("JDBCMetaData: initConnection");
085    
086            try {
087                if (jdbcDriverClassName == null || jdbcDriverClassName.trim().length() == 0 ||
088                    jdbcConnectionUrl == null || jdbcConnectionUrl.trim().length() == 0)
089                {
090                    errMsg = getResourceConverter().getFormattedString("jdbcMetaData.blank.exception",
091                            "Driver={0}\nConnection URL={1}\nUse Preferences to set Database Connection parameters first and then open a Schema",
092                            new String[] { jdbcDriverClassName, jdbcConnectionUrl });
093                    return errMsg;
094                }
095    
096                Class.forName(jdbcDriverClassName);
097    
098                if (jdbcUsername != null && jdbcUsername.length() > 0 &&
099                    jdbcPassword != null && jdbcPassword.length() > 0) {
100                    conn = DriverManager.getConnection(jdbcConnectionUrl, jdbcUsername, jdbcPassword);
101                } else {
102    
103                    conn = DriverManager.getConnection(jdbcConnectionUrl);
104                }
105    
106                LOGGER.debug("JDBC connection OPEN");
107                md = conn.getMetaData();
108    
109                db.productName      = md.getDatabaseProductName();
110                db.productVersion   = md.getDatabaseProductVersion();
111                db.catalogName      = conn.getCatalog();
112    
113                LOGGER.debug("Catalog name = " + db.catalogName);
114                /*
115                ResultSet rsd = md.getSchemas();
116                while (rsd.next())
117                {    System.out.println("   Schema ="+rsd.getString("TABLE_SCHEM"));
118                     System.out.println("   Schema ="+rsd.getString("TABLE_CATALOG"));
119                }
120                rsd = md.getCatalogs();
121                while (rsd.next())
122                    System.out.println("   Catalog ="+rsd.getString("TABLE_CAT"));
123                 */
124                LOGGER.debug("Database Product Name: " + db.productName);
125                LOGGER.debug("Database Product Version: " + db.productVersion);
126    
127                /*
128                Class.forName("org.postgresql.Driver");
129                conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/demo","admin","admin");
130                 */
131                LOGGER.debug("JDBCMetaData: initConnection - no error");
132                return null;
133            } catch (Exception e) {
134                errMsg = e.getClass().getSimpleName() + " : " + e.getLocalizedMessage();
135                LOGGER.error("Database connection exception : " + errMsg, e);
136                return errMsg;
137                //e.printStackTrace();
138            }
139        }
140    
141        public void closeConnection() {
142            try {
143                conn.close();
144                LOGGER.debug("JDBC connection CLOSE");
145            } catch (Exception e) {
146                LOGGER.error(e);
147            }
148        }
149    
150        /* set all schemas in the currently connected database */
151        private void setAllSchemas() {
152            LOGGER.debug("JDBCMetaData: setAllSchemas");
153    
154            ResultSet rs = null;
155            boolean gotSchema = false;
156    
157            try {
158                rs = md.getSchemas();
159                /*
160                if (true)
161                throw new Exception("Schema concept not found in database");
162                 */
163    
164                while (rs.next()) {
165                    DbSchema dbs = new DbSchema();
166                    dbs.name = rs.getString("TABLE_SCHEM");
167                    LOGGER.debug("JDBCMetaData: setAllTables - " + dbs.name);
168                    setAllTables(dbs);
169                    db.addDbSchema(dbs);
170                    gotSchema = true;
171                }
172                rs.close();
173            } catch (Exception e) {
174                LOGGER.debug("Exception : Database does not support schemas." + e.getMessage());
175            }
176    
177            if (!gotSchema) {
178                LOGGER.debug("JDBCMetaData: setAllSchemas - tables with no schema name");
179                DbSchema dbs = new DbSchema();
180                dbs.name = null;    //tables with no schema name
181                setAllTables(dbs);
182                db.addDbSchema(dbs);
183            }
184        }
185    
186        /* set all tables in the currently connected database */
187        private void setAllTables(DbSchema dbs) {
188            LOGGER.debug("JDBCMetaData: Loading schema: '" + dbs.name + "'");
189            ResultSet rs = null;
190            try {
191                // Tables and views can be used
192                rs = md.getTables(null, dbs.name, null, new String[]{"TABLE", "VIEW"});
193                while (rs.next()) {
194                    String tbname = rs.getString("TABLE_NAME");
195                    DbTable dbt;
196    
197                    /* Note  : Imported keys are foreign keys which are primary keys of in some other tables
198                     *       : Exported keys are primary keys which are referenced as foreign keys in other tables.
199                     */
200                    ResultSet rs_fks = md.getImportedKeys(null, dbs.name, tbname);
201                    if (rs_fks.next()) {
202                        dbt = new FactTable();
203                        do  {
204                            ((FactTable) dbt).addFks(rs_fks.getString("FKCOLUMN_NAME"),rs_fks.getString("pktable_name"));
205                        } while (rs_fks.next());
206    
207                    } else {
208                        dbt = new DbTable();
209                    }
210                    rs_fks.close();
211    
212                    dbt.schemaName = dbs.name;
213                    dbt.name = tbname;
214                    setPKey(dbt);
215                    setColumns(dbt);
216                    dbs.addDbTable(dbt);
217                    db.addDbTable(dbt);
218                }
219                rs.close();
220            } catch (Exception e) {
221                LOGGER.error("setAllTables", e);
222            }
223        }
224    
225        /* get the Primary key name for a given table name
226         * This key may be a  composite key made of multiple columns.
227         */
228        private void setPKey(DbTable dbt) {
229            ResultSet rs = null;
230            try {
231                rs = md.getPrimaryKeys(null, dbt.schemaName, dbt.name);
232                /*
233                while(rs.next()) {
234                    primKeys.add(rs.getString("COLUMN_NAME"));
235                }
236                 **/
237                if (rs.next()) {
238                    //===dbt.pk = rs.getString("PK_NAME");  // a column may have been given a primary key name
239                    dbt.pk = rs.getString("column_name");   // we need the column name which is primary key for the given table.
240                }
241                rs.close();
242            } catch (Exception e) {
243                LOGGER.error("setPKey", e);
244            }
245        }
246    
247        /* get all columns for a given table name */
248        private void setColumns(DbTable dbt) {
249            ResultSet rs = null;
250            try {
251                rs = md.getColumns(null, dbt.schemaName, dbt.name, null);
252                while (rs.next()) {
253                    dbt.addColsDataType(rs.getString("COLUMN_NAME"), rs.getString("DATA_TYPE"));
254                }
255                rs.close();
256            } catch (Exception e) {
257                LOGGER.error("setColumns", e);
258            }
259        }
260    
261    /* ===================================================================================================
262     *  The following functions provide an interface to JDBCMetaData class to retrieve the meta data details
263     * =================================================================================================== */
264    
265        public Vector<String> getAllSchemas() {
266            return db.getAllSchemas();
267        }
268    
269    
270        /* get all tables in a given schema */
271        public Vector<String> getAllTables(String schemaName) {
272            return db.getAllTables(schemaName);
273        }
274    
275        /* get all tables in given schema minus the given table name */
276        public Vector<String> getAllTables(String schemaName, String minusTable) {
277    
278            if (minusTable == null) {
279                return getAllTables(schemaName);
280            } else {
281                Vector<String> allTablesMinusOne = new Vector<String>();
282                for (String s : getAllTables(schemaName)) {
283                    if (s
284                        .endsWith(minusTable)) {   // startsWith and endsWith cannot be compared with null argument, throws exception
285                        if ((schemaName == null) || s.startsWith(schemaName)) {
286                            continue;
287                        }
288                    }
289                    allTablesMinusOne.add(s);
290                }
291                return allTablesMinusOne;
292            }
293        }
294    
295        /* get all possible cases of fact tables in a schema */
296        public Vector<String> getFactTables(String schemaName) {
297            return db.getFactTables(schemaName);
298        }
299    
300        /* get all possible cases of dimension tables which are linked to given fact table by foreign keys */
301        public Vector<String> getDimensionTables(String schemaName, String factTable) {
302            Vector<String> dimeTables = new Vector<String>();
303    
304            if (factTable == null) {
305                return dimeTables;
306            } else {
307                return db.getDimensionTables(schemaName, factTable);
308            }
309        }
310    
311        public boolean isTableExists(String schemaName, String tableName) {
312            if (tableName == null) {
313                return true;
314            } else {
315                return db.tableExists(schemaName, tableName);
316            }
317        }
318    
319        public boolean isColExists(String schemaName, String tableName, String colName) {
320            if (tableName == null || colName == null) {
321                return true;
322            } else {
323                return db.colExists(schemaName, tableName, colName);
324            }
325        }
326    
327        /* get all foreign keys in given fact table */
328        public Vector<String> getFactTableFKs(String schemaName, String factTable) {
329            Vector<String> fks = new Vector<String>();
330    
331            if (factTable == null) {
332                return fks;
333            } else {
334                return db.getFactTableFKs(schemaName, factTable);
335            }
336        }
337    
338        public String getTablePK(String schemaName, String tableName) {
339    
340            if (tableName == null) {
341                return null;
342            } else {
343                return db.getTablePK(schemaName, tableName);
344            }
345        }
346    
347        /* get all columns of given table in schema */
348        public Vector<String> getAllColumns(String schemaName, String tableName) {
349            Vector<String> allcols = new Vector<String>();
350    
351            if (tableName == null) {
352                    Vector<String> allTables = getAllTables(schemaName);
353    
354                    for (int i = 0; i < allTables.size(); i++) {
355                        String tab = allTables.get(i);
356                        Vector<String> cols;
357                        if (tab.indexOf("->") == -1) {
358                            cols = getAllColumns(schemaName, tab);
359                        } else {
360                            String [] names = tab.split("->");
361                            cols = getAllColumns(names[0], names[1]);
362                        }
363                        for (int j = 0; j < cols.size(); j++) {
364                            String col = cols.get(j);
365                            allcols.add(tab + "->" + col);
366                        }
367                    }
368                return allcols;
369            } else {
370                return db.getAllColumns(schemaName, tableName);
371            }
372        }
373    
374        // get column data type of given table and its col
375        public int getColumnDataType(String schemaName, String tableName, String colName) {
376            if (tableName == null || colName == null) {
377                return -1;
378            } else {
379                return db.getColumnDataType(schemaName, tableName, colName);
380            }
381    
382        }
383        public String getDbCatalogName() {
384            return db.catalogName;
385        }
386    
387        public String getDatabaseProductName() {
388            return db.productName;
389        }
390    
391        public String getErrMsg() {
392            return errMsg;
393        }
394    
395        public static void main(String[] args) {
396            /*
397            JDBCMetaData sb = new JDBCMetaData("org.postgresql.Driver","jdbc:postgresql://localhost:5432/testdb?user=admin&password=admin");
398            System.out.println("allSchemas="+sb.allSchemas);
399            System.out.println("allSchemasMap="+sb.allSchemasMap);
400            System.out.println("allTablesCols="+sb.allTablesCols);
401            System.out.println("allTablesPKs="+sb.allTablesPKs);
402            System.out.println("allFactTableDimensions="+sb.allFactTableDimensions);
403            System.out.println("getAllTables(null, part)="+sb.getAllTables(null, "part"));
404            System.out.println("sb.getColumnDataType(null, part,part_nbr)="+sb.getColumnDataType(null, "part","part_nbr"));
405             */
406            String s = "somita->namita";
407            String [] p = s.split("->");
408            if (LOGGER.isDebugEnabled()) {
409                if (p.length >= 2) {
410                    LOGGER.debug("p0=" + p[0] + ", p1=" + p[1]);
411                }
412            }
413        }
414    
415    /* ===================================================================================================
416     *  class structure for storing database metadata
417     * =================================================================================================== */
418        class Database {
419            String catalogName = ""; // database name.
420            String productName = "Unknown";
421            String productVersion =    "";
422    
423            // list of all schemas in database
424            List<DbSchema> schemas = new ArrayList<DbSchema>(); //ordered collection, allows duplicates and null
425            List<DbTable> tables  = new ArrayList<DbTable>(); // list of all tables in all schemas in database
426            Map<String, Integer> tablesCount = new TreeMap<String, Integer>(); // map of table names and the count of tables with this name in the database.
427    
428            Vector<String> allSchemas ;
429    
430            private void addDbSchema(DbSchema dbs) {
431                schemas.add(dbs);
432            }
433    
434            private void addDbTable(DbTable dbs) {
435                tables.add(dbs);
436                Integer count = tablesCount.get(dbs.name);
437                if (count == null) {
438                    count = Integer.valueOf(1);
439                } else {
440                    count = Integer.valueOf(count.intValue() + 1);
441                }
442                tablesCount.put(dbs.name, count);
443            }
444    
445            private Vector<String> getAllSchemas() {
446                if (allSchemas == null) {
447                    allSchemas = new Vector<String>();
448                    if (schemas.size() > 0) {
449                        for (DbSchema s : schemas) {
450                            allSchemas.add((s).name);
451                        }
452                    }
453                }
454                return allSchemas;
455            }
456    
457            private boolean tableExists(String sname, String tableName) {
458                if (sname == null || sname.equals("")) {
459                    return tablesCount.containsKey(tableName);
460                } else {
461                    for (DbSchema s : schemas) {
462                        if (s.name.equals(sname)) {
463                            for (DbTable d : s.tables) {
464                                if (d.name.equals(tableName)) {
465                                    return true;
466                                }
467                            }
468                            break;
469                        }
470                    }
471                }
472                return false;
473            }
474    
475            private boolean colExists(String sname, String tableName, String colName) {
476                if (sname == null || sname.equals("")) {
477                    Iterator<DbTable> ti = tables.iterator();
478                    for (DbTable t : tables) {
479                        if (t.name.equals(tableName)) {
480                            return t.colsDataType.containsKey(colName);
481                        }
482                    }
483                } else {
484                    // return a vector of "fk col name" string objects if schema is given
485                    for (DbSchema s : schemas) {
486                        if (s.name.equals(sname)) {
487                            for (DbTable t : s.tables) {
488                                if (t.name.equals(tableName)) {
489                                    return t.colsDataType.containsKey(colName);
490                                }
491                            }
492                            break;
493                        }
494                    }
495                }
496    
497                return false;
498            }
499    
500            private Vector<String> getAllTables(String sname) {
501                Vector<String> v = new Vector<String>();
502    
503                if (sname == null || sname.equals("")) {
504                    // return a vector of "schemaname -> table name" string objects
505                    for (DbTable d : tables) {
506                        if (d.schemaName == null) {
507                            v.add(d.name);
508                        } else {
509                            v.add(d.schemaName + "->" + d.name);
510                        }
511                    }
512    
513                } else {
514                    // return a vector of "tablename" string objects
515                    for (DbSchema s : schemas) {
516                        if (s.name.equals(sname)) {
517                            for (DbTable d : s.tables) {
518                                v.add(d.name);
519                            }
520                            break;
521                        }
522                    }
523                }
524                return v;
525            }
526    
527            private Vector<String> getFactTables(String sname) {
528                Vector<String> f = new Vector<String>();
529    
530                if (sname == null || sname.equals("")) {
531                    // return a vector of "schemaname -> table name" string objects if schema is not given
532                    Iterator<DbTable> ti = tables.iterator();
533                    for (DbTable t : tables) {
534                        if (t instanceof FactTable) {
535                            if (t.schemaName == null) {
536                                f.add(t.name);
537                            } else {
538                                f.add(t.schemaName + "->" + t.name);
539                            }
540                        }
541                    }
542                } else {
543                    // return a vector of "fact tablename" string objects if schema is given
544                    for (DbSchema s : schemas) {
545                        if (s.name.equals(sname)) {
546                            for (DbTable t : s.tables) {
547                                if (t instanceof FactTable) {
548                                    f.add(((FactTable) t).name);
549                                }
550                            }
551                            break;
552                        }
553                    }
554                }
555    
556                return f;
557            }
558    
559            /* get all foreign keys in given fact table */
560            private Vector<String> getFactTableFKs(String sname, String factTable) {
561                Vector<String> f = new Vector<String>();
562    
563                if (sname == null || sname.equals("")) {
564                    // return a vector of "schemaname -> table name -> fk col" string objects if schema is not given
565                    boolean duplicate = (tablesCount.containsKey(factTable)) && ((tablesCount.get(factTable)).intValue() > 1);
566    
567                    for (DbTable t : tables) {
568                        if (t instanceof FactTable && t.name.equals(factTable)) {
569                            if (duplicate) {
570                                for (String fk : ((FactTable) t).fks.keySet()) {
571                                    if (t.schemaName == null) {
572                                        f.add(t.name + "->" + fk);
573                                    } else {
574                                        f.add(
575                                            t.schemaName
576                                                + "->"
577                                                + t.name
578                                                + "->"
579                                                + fk);
580                                    }
581                                }
582                            } else {
583                                f.addAll(((FactTable) t).fks.keySet());
584                            }
585                        }
586                    }
587                } else {
588                    // return a vector of "fk col name" string objects if schema is given
589                    for (DbSchema s : schemas) {
590                        if (s.name.equals(sname)) {
591                            for (DbTable t : s.tables) {
592                                if (t instanceof FactTable && t.name
593                                    .equals(factTable)) {
594                                    f.addAll(((FactTable) t).fks.keySet());
595                                    break;
596                                }
597                            }
598                            break;
599                        }
600                    }
601                }
602                return f;
603            }
604    
605            private Vector<String> getDimensionTables(String sname, String factTable) {
606                Vector<String> f = new Vector<String>();
607    
608                if (sname == null || sname.equals("")) {
609                    // return a vector of "schemaname -> table name -> dimension table name" string objects if schema is not given
610                    boolean duplicate =  (tablesCount.containsKey(factTable))  &&  ((tablesCount.get(factTable)).intValue() > 1);
611    
612                    for (DbTable t : tables) {
613                        if (t instanceof FactTable && t.name.equals(factTable)) {
614                            if (duplicate) {
615                                Iterator<String> fki =
616                                    ((FactTable) t).fks.values().iterator();
617                                for (String fkt : ((FactTable) t).fks.values()) {
618                                    if (t.schemaName == null) {
619                                        f.add(t.name + "->" + fkt);
620                                    } else {
621                                        f.add(
622                                            t
623                                                .schemaName
624                                                + "->"
625                                                + t
626                                                .name
627                                                + "->"
628                                                + fkt);
629                                    }
630                                }
631                            } else {
632                                f.addAll(((FactTable) t).fks.values());
633                                break;
634                            }
635                        }
636                    }
637                } else {
638                    // return a vector of "fk col name" string objects if schema is given
639                    for (DbSchema s : schemas) {
640                        if (s.name.equals(sname)) {
641                            for (DbTable t : s.tables) {
642                                if (t instanceof FactTable && t.name
643                                    .equals(factTable)) {
644                                    f.addAll(((FactTable) t).fks.values());
645                                    break;
646                                }
647                            }
648                            break;
649                        }
650                    }
651                }
652                return f;
653            }
654    
655            private String getTablePK(String sname, String tableName) {
656    
657                if (sname == null || sname.equals("")) {
658                    // return a vector of "schemaname -> table name -> dimension table name" string objects if schema is not given
659                    for (DbTable t : tables) {
660                        if (t.name.equals(tableName)) {
661                            return t.pk;
662                        }
663                    }
664                } else {
665                    // return a vector of "fk col name" string objects if schema is given
666                    for (DbSchema s : schemas) {
667                        if (s.name.equals(sname)) {
668                            for (DbTable t : s.tables) {
669                                if (t.name.equals(tableName)) {
670                                    return t.pk;
671                                }
672                            }
673                            break;
674                        }
675                    }
676                }
677                return null;
678            }
679    
680            private Vector<String> getAllColumns(String sname, String tableName) {
681                Vector<String> f = new Vector<String>();
682    
683                if (sname == null || sname.equals("")) {
684                    // return a vector of "schemaname -> table name -> cols" string objects if schema is not given
685                    boolean duplicate =  (tablesCount.containsKey(tableName))  && ((tablesCount.get(tableName)).intValue() > 1);
686    
687                    for (DbTable t : tables) {
688                        if (t.name.equals(tableName)) {
689                            if (duplicate) {
690                                for (String c : t.colsDataType.keySet()) {
691                                    if (t.schemaName == null) {
692                                        f.add(t.name + "->" + c);
693                                    } else {
694                                        f.add(
695                                            t.schemaName
696                                                + "->"
697                                                + t.name
698                                                + "->"
699                                                + c);
700                                    }
701                                }
702                            } else {
703                                f.addAll(t.colsDataType.keySet());      //display only col names
704                                break;
705                            }
706                        }
707                    }
708                } else {
709                    // return a vector of "col name" string objects if schema is given
710                    for (DbSchema s : schemas) {
711                        if (s.name.equals(sname)) {
712                            for (DbTable t : s.tables) {
713                                if (t.name.equals(tableName)) {
714                                    f.addAll(t.colsDataType.keySet());
715                                    break;
716                                }
717                            }
718                            break;
719                        }
720                    }
721                }
722                return f;
723            }
724    
725            private int getColumnDataType(String sname, String tableName, String colName) {
726    
727                if (sname == null || sname.equals("")) {
728                    for (DbTable t : tables) {
729                        if (t.name.equals(tableName)) {
730                            return Integer.parseInt(t.colsDataType.get(colName));
731                        }
732                    }
733                } else {
734                    // return a vector of "fk col name" string objects if schema is given
735                    for (DbSchema s : schemas) {
736                        if (s.name.equals(sname)) {
737                            for (DbTable t : s.tables) {
738                                if (t.name.equals(tableName)) {
739                                    return Integer.parseInt(
740                                        t.colsDataType.get(colName));
741                                }
742                            }
743                            break;
744                        }
745                    }
746                }
747    
748                return -1;
749            }
750        }
751    
752        class DbSchema {
753            String name;
754            /** ordered collection, allows duplicates and null */
755            final List<DbTable> tables = new ArrayList<DbTable>();
756    
757            private void addDbTable(DbTable dbt) {
758                tables.add(dbt);
759            }
760        }
761    
762        class DbTable {
763            String schemaName;
764            String name;
765            String pk;
766            /** sorted map key=column, value=data type of column */
767            final Map<String, String> colsDataType = new TreeMap<String, String>();
768    
769            private void addColsDataType(String col, String dataType) {
770                colsDataType.put(col, dataType);
771            }
772    
773        }
774    
775        class FactTable extends DbTable {
776            /** sorted map key = foreign key col, value=primary key table associated with this fk */
777            final Map<String, String> fks = new TreeMap<String, String>();
778    
779            private void addFks(String fk, String pkt) {
780                fks.put(fk, pkt);
781            }
782        }
783    }
784    
785    
786    // End JDBCMetaData.java