Bug 135296 - opening a special java file results in an "out of memory" message
Summary: opening a special java file results in an "out of memory" message
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 critical (vote)
Target Milestone: 3.2 RC1   Edit
Assignee: Jerome Lanneluc CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 136096 (view as bug list)
Depends on:
Blocks:
 
Reported: 2006-04-06 11:18 EDT by Tobias Riemenschneider CLA
Modified: 2006-04-13 13:00 EDT (History)
1 user (show)

See Also:


Attachments
Proposed patch and regression test (4.67 KB, patch)
2006-04-11 07:37 EDT, Jerome Lanneluc CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Riemenschneider CLA 2006-04-06 11:18:14 EDT
I don't know exactly if this is the right place for this 'bug'.

3.2M6 is showing a strange behaviour when opening one of my java files. Everytime I open the file, the heap size panel at the bottom of the eclipse workbench starts to increase in 50M steps until eclipse pops up an 'out of memory' message. When increasing the maximum heap size to 512M, it's just the same. Note that eclipse does not run out of memory, when the java file is compiled (by building the whole project containing the java file).

The bug can be reproduced by creating a new java project and adding the following two java files to the default package.
<< MergedResultSetMetaData <
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Iterator;

/** 
 * This abstract class is a ResultSetMetaData skeleton that wraps a number of
 * given ResultSetMetaData objects to a single one.
 * 
 * This is useful especially for Joins. There are three abstract methods:
 * <ul>
 *     <li><code>int <B>getColumnCount</B>()</code></li>
 *     <li><code>Cursor <B>originalMetaDataIndices</B>(int column)</code></li>
 *     <li><code>int <B>originalColumnIndex</B>(int originalMetaData, int column)</code></li>
 * </ul>
 * 
 * These functions define a mapping between the original metadata objects and
 * an object of this class.
 * 
 * For each call to <code>getXXX(int column)</code> it is first called
 * <code>originalMetaDataIndices(column)</code> to determine the indices of the
 * ResultSetMetaData objects thie column belongs to.
 * 
 * Thereafter the <code>getXXX(int column)</code>-call is redirected to
 * <code>getXXX(originalColumnIndex(originalMetaData, column))</code> where
 * <code>originalMetaData</code> is the index obtained from the iteration
 * returned by the <code>originalMetaDataIndices(column)</code> method.<br />
 * For merged columns the behaviour of a method depends on the kind of
 * underlying column. For example <code>columnDisplaySize</code> is the maximum
 * of the <code>columnDisplaySizes</code> of the corresponding columns from all
 * underlying MetaData objects.
 */
public abstract class MergedResultSetMetaData implements ResultSetMetaData {
  
  /**
   * An array holding the ResultSetMetaData objects to be merged.
   */
  protected ResultSetMetaData[] metaData;
  
  /**
   * Constructs a MergedResultSetMetaData object that wraps the given
   * ResultSetMetaData objects. 
   *
   * @param metaData an array holding the ResultSetMetaData objects to be
   *        merged.
   */
  public MergedResultSetMetaData(ResultSetMetaData... metaData) {
    this.metaData = metaData;
  }

  /**
   * Constructs a MergedResultSetMetaData object that wraps the
   * ResultSetMetaData objects contained by the given iteration. 
   *
   * @param metaData an iteration holding the ResultSetMetaData objects to be
   *        merged.
   */
  public MergedResultSetMetaData(Iterator<? extends ResultSetMetaData> metaData) {
    this();
  }

  /**
   * Returns the number of columns of this ResultSetMetaData object.
   *
   * @return the number of columns of this ResultSetMetaData object
   * @throws SQLException if a database access error occurs.
   */
  public abstract int getColumnCount() throws SQLException;

  /**
   * Returns an iteration over the indices of the underlying
   * ResultSetMetaData objects the given column is originated in. If you
   * derive a class from this class, this method defines your mapping from
   * the columns of the ResultSetMetaData objects to the
   * MergedResultSetMetaData object.
   *
   * @param column number of the column: the first column is 1, the second is
   *        2, ...
   * @return returns an iteration over the indices of the underlying
   *         ResultSetMetaData objects the given column is originated in.
   * @throws SQLException if a database access error occurs.
   */
  public abstract Iterator<Integer> originalMetaDataIndices(int column) throws SQLException;
  
  /**
   * Determines the original column index from the underlying
   * ResultSetMetaData object with the given index, on which the specified
   * column of this object is based.
   *
   * @param originalMetaData the index of the underlying ResultSetMetaData
   *        object that should be tested for being the origin of the
   *        specified column.
   * @param column number of the column: the first column is 1, the second is
   *        2, ... If the given column does not originate in the specified
   *        ResultSetMetaData object, the return value has to be 0.
   * @return the original column number from the underlying ResultSetMetaData
   *         object with the given index, on which the specified column of
   *         this object is based.
   * @throws SQLException if a database access error occurs.
   */
  public abstract int originalColumnIndex(int originalMetaData, int column) throws SQLException;
  
  /**
   * Determines the original ResultSetMetaData objects and column indices, on
   * which the specified column of this object is based.
   *
   * @param column number of the column: the first column is 1, the second is
   *        2, ... If the given column does not originate in the specified
   *        ResultSetMetaData object, the return value has to be 0.
   * @return the original ResultSetMetaData objects and column indices, on
   *         which the specified column of this object is based. The returned
   *         iteration contains two-dimensional <code>int</code>-arrays
   *         holding the index of the original ResultSetMetaData object and
   *         the index of the original column.
   * @throws SQLException if a database access error occurs.
   */
  public Iterator<int[]> originalColumnIndices(final int column) throws SQLException {
    return null;
  }
  
  /**
   * Returns the name the catalog. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the name of the catalog.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getCatalogName(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getCatalogName(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the name the Java class that is associated with the column
   * type. The call is redirected to the ResultSetMetaData object the given
   * column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the name of the column class.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getColumnClassName(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getColumnClassName(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the display size of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in and the
   * maximum of all column display sizes is returned.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the display size of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public int getColumnDisplaySize(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int columnDiplaySize = Integer.MIN_VALUE;
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        columnDiplaySize = Math.max(columnDiplaySize, metaData[originalColumnIndex[0]].getColumnDisplaySize(originalColumnIndex[1]));
      }
      return columnDiplaySize;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the column label. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the name of the column label.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getColumnLabel(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getColumnLabel(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the name of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the name of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getColumnName(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getColumnName(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the type of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the type of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public int getColumnType(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getColumnType(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the type name of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the type name of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getColumnTypeName(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getColumnTypeName(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the precision of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the precision of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public int getPrecision(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getPrecision(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the scale of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the scale of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public int getScale(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getScale(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the schema name of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the schema name of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getSchemaName(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getSchemaName(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns the table name of the column. The call is redirected to the
   * ResultSetMetaData object the given column is originated in.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return the table name of the column.
   * @throws SQLException if a database access error occurs.
   */ 
  public String getTableName(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return metaData[originalColumnIndex[0]].getTableName(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is an auto increment column. The call is
   * redirected to the ResultSetMetaData object the given column is
   * originated in. If the column is originated in more than one
   * ResultSetMetaData object it cannot be an auto increment column.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column is an auto increment column.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isAutoIncrement(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int[] originalColumnIndex = originalColumnIndices.next();
      return !originalColumnIndices.hasNext() && metaData[originalColumnIndex[0]].isAutoIncrement(originalColumnIndex[1]);
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is case sensitive. The call is redirected to the
   * ResultSetMetaData object the given column is originated in. If the
   * column is originated in more than one metadata object, it is case
   * sensitive, if one of the original columns is case sensitive.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column is case sensitive.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isCaseSensitive(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (metaData[originalColumnIndex[0]].isCaseSensitive(originalColumnIndex[1]))
          return true;
      }
      return false;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column contains a currency value. The call is redirected
   * to the ResultSetMetaData object the given column is originated in. If
   * the column is originated in more than one metadata object, it is a
   * currency value if all original values are currency values.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column contains a currency value.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isCurrency(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (!metaData[originalColumnIndex[0]].isCurrency(originalColumnIndex[1]))
          return false;
      }
      return true;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is definitively writable. The call is redirected
   * to the ResultSetMetaData object the given column is originated in. If
   * the column is originated in more than one metadata object, it is
   * definitively writable if all original columns are definitively writable.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column is definitively writable.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isDefinitelyWritable(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (!metaData[originalColumnIndex[0]].isDefinitelyWritable(originalColumnIndex[1]))
          return false;
      }
      return true;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is nullable. The call is redirected to the
   * ResultSetMetaData object the given column is originated in. If the
   * column is originated in more than one metadata object, the column is
   * nullabe if one of the underlying columns is nullable. If all underlying
   * columns do not allow null values (<code>columnNoNulls</code>), the
   * column also does not allow null values. In any other case, the return
   * value is <code>columnNullableUnknown</code>.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return one of the constants defined in
   *         {@link java.sql.ResultSetMetaData}: <code>columnNoNulls</code>,
   *         <code>columnNullable</code> or
   *         <code>columnNullableUnknown</code>.
   * @throws SQLException if a database access error occurs.
   */ 
  public int isNullable(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      int isNullable = ResultSetMetaData.columnNullableUnknown;
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        int isNextNullable = metaData[originalColumnIndex[0]].isNullable(originalColumnIndex[1]);
        if (isNextNullable == ResultSetMetaData.columnNullable)
          return ResultSetMetaData.columnNullable;
        isNullable = (isNullable == ResultSetMetaData.columnNoNulls && isNextNullable == ResultSetMetaData.columnNoNulls) ?
          ResultSetMetaData.columnNoNulls :
          ResultSetMetaData.columnNullableUnknown;
      }
      return isNullable;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is read only. The call is redirected to the
   * ResultSetMetaData object the given column is originated in. If the
   * column is originated in more than one metadata object, it is read only,
   * if one of the original columns is read only.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column is read only.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isReadOnly(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (metaData[originalColumnIndex[0]].isReadOnly(originalColumnIndex[1]))
          return true;
      }
      return false;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is searchable. The call is redirected to the
   * ResultSetMetaData object the given column is originated in. If the
   * column is originated in more than one metadata objects, it is searchable
   * if all original columns are searchable.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column is searchable.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isSearchable(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (!metaData[originalColumnIndex[0]].isSearchable(originalColumnIndex[1]))
          return false;
      }
      return true;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is signed. The call is redirected to the
   * ResultSetMetaData object the given column is originated in. If the
   * column is originated in more than one metadata object, it is signed, if
   * one of the original columns is signed.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column signed.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isSigned(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (metaData[originalColumnIndex[0]].isSigned(originalColumnIndex[1]))
          return true;
      }
      return false;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }

  /**
   * Returns if the column is writable. The call is redirected to the
   * ResultSetMetaData object the given column is originated in. If the
   * column is originated in more than one metadata object, it is writable if
   * all original columns are writable.
   * 
     * @param column number of the column: the first column is 1, the second is
     *        2, ...
   * @return true if the column is writable.
   * @throws SQLException if a database access error occurs.
   */ 
  public boolean isWritable(int column) throws SQLException {
    Iterator<int[]> originalColumnIndices = originalColumnIndices(column);
    if (originalColumnIndices.hasNext()) {
      while (originalColumnIndices.hasNext()) {
        int[] originalColumnIndex = originalColumnIndices.next();
        if (!metaData[originalColumnIndex[0]].isWritable(originalColumnIndex[1]))
          return false;
      }
      return true;
    }
    throw new SQLException("the specified column " + column + " cannot be identified");
  }
  
  /**
   * Returns the metadata set that is specified by the given index.
   * 
   * @param input the number of the metadata set: the first metadata set is
   *        1, the second is 2, ...
   * @return the metadata object. 
   */ 
  protected ResultSetMetaData getMetaData(int input) {
    return metaData[input];
  }

  /**
   * Returns a string representation of this relational metadata.
   * 
   * @return a string representation of this relational metadata.
   */
  @Override
  public String toString() {
    try {
      if (getColumnCount() == 0)
        return "[]";
      
      StringBuffer string = new StringBuffer().append('[').append(getColumnTypeName(1)).append(' ').append(getColumnName(1));
      
      for (int column = 2; column <= getColumnCount(); column++)
        string.append(", ").append(getColumnTypeName(column)).append(' ').append(getColumnName(column));
      
      return string.append(']').toString();
    }
    catch (SQLException sqle) {
      throw new RuntimeException("sql exception occured during string construction: \'" + sqle.getMessage() + "\'");
    }
  }
  
  /**
   * Indicates whether some other object is "equal to" this relational
   * metadata.
   * 
   * <p>The <code>equals</code> method implements an equivalence relation: 
   * <ul>
   *     <li>
   *         It is <i>reflexive</i>: for any reference value <code>x</code>,
   *         <code>x.equals(x)</code> should return <code>true</code>.
   *     </li>
   *     <li>
   *         It is <i>symmetric</i>: for any reference values <code>x</code>
   *         and <code>y</code>, <code>x.equals(y)</code> should return
   *         <code>true</code> if and only if <code>y.equals(x)</code>
   *         returns <code>true</code>.
   *     </li>
   *     <li>
   *         It is <i>transitive</i>: for any reference values
   *         <code>x</code>, <code>y</code>, and <code>z</code>, if
   *         <code>x.equals(y)</code> returns <code>true</code> and
   *         <code>y.equals(z)</code> returns <code>true</code>, then
   *         <code>x.equals(z)</code> should return <code>true</code>.
   *     </li>
   *     <li>
   *         It is <i>consistent</i>: for any reference values <code>x</code>
   *         and <code>y</code>, multiple invocations of
   *         <code>x.equals(y)</code> consistently return <code>true</code>
   *         or consistently return <code>false</code>, provided no
   *         information used in <code>equals</code> comparisons on the
   *         object is modified.
   *     </li>
   *     <li>
   *         For any non-<code>null</code> reference value <code>x</code>,
   *         <code>x.equals(null)</code> should return <code>false</code>.
   *     </li>
   * </ul></p>
   * 
   * <p>The current <code>equals</code> method returns true if and only if
   * the given object:
   * <ul>
   *     <li>
   *         is this relational metadata or
   *     </li>
   *     <li>
   *         is an instance of the type <code>ResultSetMetaData</code> and a
   *         {@link ResultSetMetaDatas#RESULTSET_METADATA_COMPARATOR comparator}
   *         for relational metadata returns 0.
   *     </li>
   * </ul></p>
   * 
   * @param object the reference object with which to compare.
   * @return <code>true</code> if this object is the same as the specified
   *         object; <code>false</code> otherwise.
   * @see #hashCode()
   */
  @Override
  public boolean equals(Object object) {
    if (object == null)
      return false;
    if (this == object)
      return true;
    return object instanceof ResultSetMetaData;
  }
  
  /**
   * Returns the hash code value for this relational metadata using a
   * {@link ResultSetMetaDatas#RESULTSET_METADATA_HASH_FUNCTION hash function}
   * for relational metadata.
   *
   * @return the hash code value for this relational metadata.
   * @see Object#hashCode()
   * @see #equals(Object)
   */
  @Override
  public int hashCode() {
    return 0;
  }
  
//  /**
//   * Returns an object that implements the given interface to allow access to
//   * non-standard methods, or standard methods not exposed by the proxy. The
//   * result may be either the object found to implement the interface or a
//   * proxy for that object. If the receiver implements the interface then
//   * that is the object. If the receiver is a wrapper and the wrapped object
//   * implements the interface then that is the object. Otherwise the object
//   * is the result of calling <code>unwrap</code> recursively on the wrapped
//   * object. If the receiver is not a wrapper and does not implement the
//   * interface, then an <code>SQLException</code> is thrown.
//   *
//   * @param iface a class defining an interface that the result must
//   *        implement.
//   * @return an object that implements the interface. May be a proxy for the
//   *         actual implementing object.
//   * @throws SQLException if no object found that implements the interface.
//   */
//  public Object unwrap(java.lang.Class<?> iface) throws SQLException {
//    throw new UnsupportedOperationException("this method is not implemented yet.");
//  }
//  
//  /**
//   * Returns true if this either implements the interface argument or is
//   * directly or indirectly a wrapper for an object that does. Returns false
//   * otherwise. If this implements the interface then return true, else if
//   * this is a wrapper then return the result of recursively calling
//   * <code>isWrapperFor</code> on the wrapped object. If this does not
//   * implement the interface and is not a wrapper, return false. This method
//   * should be implemented as a low-cost operation compared to
//   * <code>unwrap</code> so that callers can use this method to avoid
//   * expensive <code>unwrap</code> calls that may fail. If this method
//   * returns true then calling <code>unwrap</code> with the same argument
//   * should succeed.
//   *
//   * @param iface a class defining an interface.
//   * @return true if this implements the interface or directly or indirectly
//   *         wraps an object that does.
//   * @throws SQLException if an error occurs while determining whether this
//   *         is a wrapper for an object with the given interface.
//   */
//  public boolean isWrapperFor(java.lang.Class<?> iface) throws SQLException {
//    throw new UnsupportedOperationException("this method is not implemented yet.");
//  }
  
}
>>>
<< UnifiedResultSetMetaData <
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * Appends a number of given ResultSetMetaData objects to each other and
 * returns a new unified ResultSetMetaData object such that all column names
 * are unique. That is when appending two ResultSetMetaData objects having
 * columns with equal names the resulting UnifiedResultSetMetaData object will
 * unify these columns and merge them to one column.
 */
public class UnifiedResultSetMetaData extends MergedResultSetMetaData {

  /**
   * An array representing the columns of this object. Every entry consists
   * of a list, holding the occurrences of the affected column in the wrapped
   * ResultSetMetaData objects.
   */
  protected List<List<int[]>> columns;
  
  /**
   * Constructs an UnifiedResultSetMetaData object that wraps the given
   * ResultSetMetaData objects. The constructor already performs the
   * calculation of the UnifiedResultSetMetaData object's columns by
   * determining he multiple occurrance of column names.
   *
   * @param metaData an array holding the ResultSetMetaData objects to be
   *        appended to one ResultSetMetaData object.
   */
  public UnifiedResultSetMetaData(ResultSetMetaData... metaData) {
    super(metaData);
    
    try {
      HashMap<String, List<int[]>> hashMap = new HashMap<String, List<int[]>>();
      for (int i = 0; i < this.metaData.length; i++)
        for (int j = 1; j <= this.metaData[i].getColumnCount(); j++) {
          String columnName = this.metaData[i].getColumnName(j).toUpperCase();
          List<int[]> list = hashMap.get(columnName);
          if (list == null) {
            list = new ArrayList<int[]>();
            hashMap.put(columnName, list);
          }
          list.add(new int[] {i, j});
        }
      columns = new ArrayList<List<int[]>>(hashMap.values());
      Collections.sort(columns, new Comparator<List<int[]>>() {
        public int compare(List<int[]> list1, List<int[]> list2) {
          int[] intArray1 = list1.get(0), intArray2 = list2.get(0);
          int index = intArray1[0] == intArray2[0] ? 1 : 0;
          return intArray1[index]-intArray2[index];
        }
      });
    }
    catch (SQLException sqle) {
      throw new RuntimeException("meta data cannot be constructed due to the following sql exception: " + sqle.getMessage());
    }
  }
  
  /**
   * Constructs an UnifiedResultSetMetaData object that wraps the
   * ResultSetMetaData objects contained by the given iteration. 
   *
   * @param metaData an iteration holding the ResultSetMetaData objects to be
   *        appended to one ResultSetMetaData object.
   */
  public UnifiedResultSetMetaData(Iterator<ResultSetMetaData> metaData) {
    this();
  }

  /**
   * Returns the number of columns. The number of columns of an
   * UnifiedResultSetMetaData object is equal to the number of the unique
   * column names of the appended ResultSetMetaData objects.
   *
   * @return the number of columns.
   * @throws SQLException if a database access error occurs.
   */ 
  @Override
  public int getColumnCount() throws SQLException {
    return columns.size();
  }

  /**
   * Returns an iteration over the indices of the underlying
   * ResultSetMetaData objects the given column is originated in.
   *
   * @param column number of the column: the first column is 1, the second is
   *        2, ...
   * @return returns an iteration over the indices of the underlying
   *         ResultSetMetaData objects the given column is originated in.
   * @throws SQLException if a database access error occurs.
   */
  @Override
  public Iterator<Integer> originalMetaDataIndices(int column) throws SQLException {
    return null;
  }

  /**
   * Determines the original column index from the underlying
   * ResultSetMetaData object with the given index, on which the specified
   * column of this object is based.
   *
   * @param originalMetaData the index of the underlying ResultSetMetaData
   *        object that should be tested for being the origin of the
   *        specified column.
   * @param column number of the column: the first column is 1, the second is
   *        2, ... If the given column does not originate in the specified
   *        ResultSetMetaData object, the return value has to be 0.
   * @return the original column number from the underlying ResultSetMetaData
   *         object with the given index, on which the specified column of
   *         this object is based.
   * @throws SQLException if a database access error occurs.
   */
  @Override
  public int originalColumnIndex(int originalMetaData, int column) throws SQLException {
    for (int[] i : columns.get(column-1))
      if (i[0] == originalMetaData)
        return i[1];
    throw new SQLException("column " + column + " is not based on original mata data " + originalMetaData);
  }

  /**
   * Determines the original ResultSetMetaData objects and column indices, on
   * which the specified column of this object is based.
   *
   * @param column number of the column: the first column is 1, the second is
   *        2, ... If the given column does not originate in the specified
   *        ResultSetMetaData object, the return value has to be 0.
   * @return the original ResultSetMetaData objects and column indices, on
   *         which the specified column of this object is based. The returned
   *         iteration contains two-dimensional <code>int</code>-arrays
   *         holding the index of the original ResultSetMetaData object and
   *         the index of the original column.
   * @throws SQLException if a database access error occurs.
   */
  @Override
  public Iterator<int[]> originalColumnIndices(int column) throws SQLException {
    return null;
  }
}
>>>
The second file (UnifiedResultSetMetaData) causes eclipse to run out of memory.

After running out of memory, eclipse's log file contains the following entry
<<<
!SESSION 2006-04-06 16:48:04.159 -----------------------------------------------
eclipse.buildId=I20060331-2000
java.version=1.5.0_06
java.vendor=Sun Microsystems Inc.
BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=de_DE
Command-line arguments:  -os win32 -ws win32 -arch x86

!ENTRY org.eclipse.ui 4 4 2006-04-06 16:48:50.610
!MESSAGE Unhandled event loop exception

!ENTRY org.eclipse.core.jobs 4 2 2006-04-06 16:48:50.610
!MESSAGE An internal error occurred during: "Decoration Calculation".
!STACK 0
java.lang.OutOfMemoryError: Java heap space

!ENTRY org.eclipse.ui 4 0 2006-04-06 16:48:50.610
!MESSAGE Java heap space
!STACK 0
java.lang.OutOfMemoryError: Java heap space
>>>
Comment 1 Jerome Lanneluc CLA 2006-04-11 07:37:05 EDT
Created attachment 38278 [details]
Proposed patch and regression test
Comment 2 Philipe Mulet CLA 2006-04-11 09:25:52 EDT
+1 for 3.2RC1
Comment 3 Jerome Lanneluc CLA 2006-04-11 09:40:22 EDT
Released patch and regression test.
Comment 4 Jerome Lanneluc CLA 2006-04-11 09:44:15 EDT
*** Bug 136096 has been marked as a duplicate of this bug. ***
Comment 5 David Audel CLA 2006-04-13 13:00:55 EDT
Verified for 3.2 RC1 using build I20060413-0010