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