1 package com.quantum.model;
 
   3 import java.sql.Connection;
 
   4 import java.sql.DatabaseMetaData;
 
   5 import java.sql.ResultSet;
 
   6 import java.sql.SQLException;
 
   7 import java.sql.Statement;
 
   8 import java.util.ArrayList;
 
   9 import java.util.Collections;
 
  10 import java.util.HashMap;
 
  11 import java.util.List;
 
  14 import com.quantum.adapters.AdapterFactory;
 
  15 import com.quantum.adapters.DatabaseAdapter;
 
  16 import com.quantum.sql.MultiSQLServer;
 
  17 import com.quantum.sql.SQLMetaDataResults;
 
  18 import com.quantum.util.sql.SQLStates;
 
  21  * This class models a table or view.
 
  25 abstract class EntityImpl implements Entity {
 
  27         // The JDBC-ODBC Driver is more happy if you look up metadata values
 
  28         // using the column number than if you use the column name
 
  30         private static final int INDEX_METADATA_INDEX_NAME = 6;
 
  31         private static final int INDEX_METADATA_COLUMN_NAME = 9;
 
  32         private static final int INDEX_METADATA_ASC_OR_DESC = 10;
 
  34         private static final int PRIMARY_KEYS_METADATA_COLUMN_NAME = 4;
 
  35         private static final int PRIMARY_KEYS_METADATA_KEY_SEQ = 5;
 
  37         private static final int COLUMN_METADATA_COLUMN_NAME = 4;
 
  38         private static final int COLUMN_METATDATA_DATA_TYPE = 5;
 
  39         private static final int COLUMN_METATDATA_TYPE_NAME = 6;
 
  40         private static final int COLUMN_METADATA_COLUMN_SIZE = 7;
 
  41         private static final int COLUMN_METADATA_DECIMAL_DIGITS = 9;
 
  42         private static final int COLUMN_METADATA_REMARKS = 12;
 
  43         private static final int COLUMN_METADATA_ORDINAL_POSITION = 17;
 
  44         private static final int COLUMN_METADATA_IS_NULLABLE = 18;
 
  46         private String schema;
 
  49     private Bookmark bookmark;
 
  50     private Boolean exists = Boolean.TRUE;
 
  51     private boolean isSynonym = false;  // Tells if its a synonym or not
 
  52     // Columns will be cached in this array, as sometimes asking for them to the JDBC driver is very costy
 
  53     // (for example in Oracle when synonyms and remarks are asked for )
 
  54     private Column[] columns = null; 
 
  56     public EntityImpl(Bookmark bookmark, String schema, String name, String type, boolean isSynonym) {
 
  60         this.bookmark = bookmark;
 
  61         this.isSynonym = isSynonym;
 
  63     public Bookmark getBookmark() {
 
  66     public String getName() {
 
  69     public String getSchema() {
 
  72     public String getType() {
 
  75     public String getQualifiedName() {
 
  76         return (this.schema == null || this.schema.length() == 0) ?
 
  77             this.name : this.schema + "." + this.name;
 
  79     public Column getColumn(String columnName) throws NotConnectedException, SQLException  {
 
  81         if (this.columns == null) this.columns = getColumns();
 
  82         for (int i = 0, length = (this.columns == null) ? 0 : this.columns.length;
 
  83             column == null && i < length;
 
  85             if (columnName != null && columnName.equals(this.columns[i].getName())) {
 
  86                 column = this.columns[i];
 
  91     public Column[] getColumns() throws NotConnectedException, SQLException {
 
  92         if (this.columns != null) return this.columns;
 
  93         Connection connection = this.bookmark.getConnection();
 
  95                 this.columns = getColumnsFromMetaData(connection);
 
  97         } catch (SQLException e) {
 
  98                 if (SQLStates.ODBC_DRIVER_NOT_CAPABLE.equals(e.getSQLState()) 
 
  99                                 && AdapterFactory.JDBC_ODBC_BRIDGE.equals(
 
 100                                                 getBookmark().getJDBCDriver().getType())) {
 
 101                         this.columns = getColumnsFromQuery(connection);
 
 113          * @throws SQLException
 
 115         private Column[] getColumnsFromMetaData(Connection connection) throws SQLException {
 
 116                 Map temp = new HashMap();
 
 117                 DatabaseMetaData metaData = connection.getMetaData();
 
 118                 ResultSet resultSet = metaData.getColumns(null, getSchema(), getName(), null);
 
 120                     while (resultSet.next()) {
 
 121                         ColumnImpl column = new ColumnImpl(
 
 123                             resultSet.getString(COLUMN_METADATA_COLUMN_NAME),
 
 124                             resultSet.getString(COLUMN_METATDATA_TYPE_NAME),
 
 125                             resultSet.getInt(COLUMN_METATDATA_DATA_TYPE),
 
 126                             resultSet.getInt(COLUMN_METADATA_COLUMN_SIZE),
 
 127                             resultSet.getInt(COLUMN_METADATA_DECIMAL_DIGITS),
 
 128                             "YES".equalsIgnoreCase(resultSet.getString(COLUMN_METADATA_IS_NULLABLE)),
 
 129                             resultSet.getInt(COLUMN_METADATA_ORDINAL_POSITION),
 
 130                                         resultSet.getString(COLUMN_METADATA_REMARKS)
 
 132                         temp.put(column.getName(), column);
 
 138                 resultSet = metaData.getPrimaryKeys(null, getSchema(), getName());
 
 140                     while (resultSet.next()) {
 
 141                         String name = resultSet.getString(PRIMARY_KEYS_METADATA_COLUMN_NAME);
 
 142                         short keySequence = resultSet.getShort(PRIMARY_KEYS_METADATA_KEY_SEQ);
 
 143                         ColumnImpl column = (ColumnImpl) temp.get(name);
 
 144                         if (column != null) {
 
 145                             column.setPrimaryKeyOrder(keySequence);
 
 150                     List columnList = Collections.synchronizedList(
 
 151                         new ArrayList(temp.values()));
 
 152                     Collections.sort(columnList);
 
 153                     return (Column[]) columnList.toArray(new Column[columnList.size()]);
 
 161          * Some databases, (in particular, MS Access under ODBC) aren't terribly friendly
 
 162          * about supporting metadata.  This method scrapes out the data the old-fashioned way.
 
 166          * @throws SQLException
 
 168         private Column[] getColumnsFromQuery(Connection connection) throws SQLException {
 
 169                 List temp = new ArrayList();
 
 170                 SQLMetaDataResults results = 
 
 171                                 (SQLMetaDataResults) MultiSQLServer.getInstance().getMetaData(
 
 173                 SQLMetaDataResults.Row[] rows = results.getRows();
 
 174                 for (int i = 0, length = results.getRowCount(); i < length; i++) {
 
 175                     ColumnImpl column = new ColumnImpl(
 
 177                             (String) rows[i].get(1),
 
 178                             (String) rows[i].get(2),
 
 179                             ((Integer) rows[i].get(7)).intValue(),
 
 180                             ((Long) rows[i].get(3)).longValue(),
 
 181                             ((Integer) rows[i].get(4)).intValue(),
 
 182                             "Nullable".equalsIgnoreCase((String) rows[i].get(5)),
 
 186                 return (Column[]) temp.toArray(new Column[temp.size()]);
 
 189      * Some JDBC drivers (Oracle for example) won't return the comments
 
 190      * We recheck with a custom query, if it's defined
 
 191          * @param iniComment The already got comment
 
 192          * @param tableName The fully qualified table name
 
 193          * @param columnName The column name
 
 195          *  NO LONGER USED, there is a parameter (remarksReporting) in the JDBC connection that makes ORACLE return the
 
 196          * remarks for tables and columns. Is slower, so an option will be used.
 
 198          * The function is kept in case other JDBC drivers have the same problem
 
 200         private String getComments( String iniComment, String tableName, String columnName) {
 
 201                 if (iniComment != null && iniComment.length() > 0) 
 
 205                         Connection con = this.bookmark.getConnection();
 
 206                         DatabaseAdapter adapter = this.bookmark.getAdapter();
 
 207                         Statement stmt = con.createStatement();
 
 209                                 if (adapter != null && stmt != null 
 
 210                                                 && adapter.getCommentsQuery(tableName, columnName) != null) {
 
 212                                         stmt.execute(adapter.getCommentsQuery(tableName, columnName));
 
 213                                         ResultSet set = stmt.getResultSet();
 
 216                                                         comment = set.getString(1);
 
 225                 } catch (NotConnectedException e) {
 
 226                 } catch (SQLException e) {
 
 232         public Index[] getIndexes() {
 
 234         List indexList = new ArrayList();
 
 235         Map temp = new HashMap();
 
 237             Connection connection = this.bookmark.getConnection();
 
 238             DatabaseMetaData metaData = connection.getMetaData();
 
 239             ResultSet resultSet = metaData.getIndexInfo(null, getSchema(), getName(), false, false);
 
 241             while (resultSet.next()) {
 
 242                 String indexName = resultSet.getString(INDEX_METADATA_INDEX_NAME);
 
 243                 IndexImpl index = (IndexImpl) temp.get(indexName);
 
 245                     index = new IndexImpl(this, indexName);
 
 246                     temp.put(indexName, index);
 
 248                 String columnName = resultSet.getString(INDEX_METADATA_COLUMN_NAME);
 
 249                 String ascending = resultSet.getString(INDEX_METADATA_ASC_OR_DESC);
 
 250                 index.addColumn(columnName, ascending == null 
 
 251                     ? null : (ascending.toUpperCase().startsWith("A") 
 
 252                         ? Boolean.TRUE : Boolean.FALSE));
 
 255             indexList.addAll(temp.values());
 
 257         } catch (NotConnectedException e) {
 
 258         } catch (SQLException e) {
 
 260         return (Index[]) indexList.toArray(new Index[indexList.size()]);
 
 263     public Boolean exists() {
 
 269      * @see com.quantum.model.Entity#getQuotedTableName()
 
 271     public String getQuotedTableName() {
 
 272         return getBookmark().getAdapter().filterTableName(getQualifiedName());
 
 275     public ForeignKey[] getExportedKeys() throws SQLException, NotConnectedException {
 
 276         return this.bookmark.getDatabase().getExportedKeys(getSchema(), getName());
 
 279     public ForeignKey[] getImportedKeys() throws SQLException, NotConnectedException {
 
 280         return this.bookmark.getDatabase().getImportedKeys(getSchema(), getName());
 
 282     public ForeignKey[] getReferences() throws SQLException, NotConnectedException {
 
 283         ForeignKey[] importedKeys = getImportedKeys();
 
 284         ForeignKey[] exportedKeys = getExportedKeys();
 
 286         List list = new ArrayList(); // if we could guarantee JDK 1.4, we'd use LinkedHashSet 
 
 287         for (int i = 0, length = importedKeys == null ? 0 : importedKeys.length; i < length; i++) {
 
 288                         list.add(importedKeys[i]);
 
 290         for (int i = 0, length = exportedKeys == null ? 0 : exportedKeys.length; i < length; i++) {
 
 291                 if (!list.contains(exportedKeys[i])) {
 
 292                         list.add(exportedKeys[i]);
 
 295         return (ForeignKey[]) list.toArray(new ForeignKey[list.size()]);
 
 298     public int compareTo(Object object) {
 
 299                 Entity that = (Entity) object;
 
 300                 if (that.getQualifiedName() == null && this.getQualifiedName() != null) {
 
 302                 } else if (this.getQualifiedName() == null && that.getQualifiedName() != null) {
 
 304                 } else if (this.getQualifiedName() == null && that.getQualifiedName() == null) {
 
 307                         return this.getQualifiedName().compareTo(that.getQualifiedName());
 
 312          * @return Returns the isSynonym.
 
 314         public boolean isSynonym() {