/**
   * Returns the fully-qualified name of the Java class whose instances are manufactured if the
   * method <code>ResultSet.getObject</code> is called to retrieve a value from the column. <code>
   * ResultSet.getObject</code> may return a subclass of the class returned by this method.
   *
   * @param column the first column is 1, the second is 2, ...
   * @return the fully-qualified name of the class in the Java programming language that would be
   *     used by the method <code>ResultSet.getObject</code> to retrieve the value in the specified
   *     column. This is the class name used for custom mapping.
   * @exception SQLException if a database access error occurs
   */
  public String getColumnClassName(int column) throws SQLException {
    Field field = getField(column);
    String result = connection.getTypeInfo().getJavaClass(field.getOID());

    if (result != null) return result;

    int sqlType = getSQLType(column);
    switch (sqlType) {
      case Types.ARRAY:
        return ("java.sql.Array");
      default:
        String type = getPGType(column);
        if ("unknown".equals(type)) {
          return ("java.lang.String");
        }
        return ("java.lang.Object");
    }
  }
 /*
  * Does a column's case matter? ASSUMPTION: Any field that is
  * not obviously case insensitive is assumed to be case sensitive
  *
  * @param column the first column is 1, the second is 2...
  * @return true if so
  * @exception SQLException if a database access error occurs
  */
 public boolean isCaseSensitive(int column) throws SQLException {
   Field field = getField(column);
   return connection.getTypeInfo().isCaseSensitive(field.getOID());
 }
 /*
  * What is a column's number of digits to the right of the
  * decimal point?
  *
  * @param column the first column is 1, the second is 2...
  * @return the scale
  * @exception SQLException if a database access error occurs
  */
 public int getScale(int column) throws SQLException {
   Field field = getField(column);
   return connection.getTypeInfo().getScale(field.getOID(), field.getMod());
 }
 protected int getSQLType(int columnIndex) throws SQLException {
   return connection.getTypeInfo().getSQLType(getField(columnIndex).getOID());
 }
  private void fetchFieldMetaData() throws SQLException {
    if (fieldInfoFetched) return;

    fieldInfoFetched = true;

    StringBuffer sql = new StringBuffer();
    sql.append("SELECT c.oid, a.attnum, a.attname, c.relname, n.nspname, ");
    sql.append("a.attnotnull OR (t.typtype = 'd' AND t.typnotnull), ");
    sql.append("pg_catalog.pg_get_expr(d.adbin, d.adrelid) LIKE '%nextval(%' ");
    sql.append("FROM pg_catalog.pg_class c ");
    sql.append("JOIN pg_catalog.pg_namespace n ON (c.relnamespace = n.oid) ");
    sql.append("JOIN pg_catalog.pg_attribute a ON (c.oid = a.attrelid) ");
    sql.append("JOIN pg_catalog.pg_type t ON (a.atttypid = t.oid) ");
    sql.append(
        "LEFT JOIN pg_catalog.pg_attrdef d ON (d.adrelid = a.attrelid AND d.adnum = a.attnum) ");
    sql.append("JOIN (");

    // 7.4 servers don't support row IN operations (a,b) IN ((c,d),(e,f))
    // so we've got to fake that with a JOIN here.
    //
    boolean hasSourceInfo = false;
    for (int i = 0; i < fields.length; i++) {
      if (fields[i].getTableOid() == 0) continue;

      if (hasSourceInfo) sql.append(" UNION ALL ");

      sql.append("SELECT ");
      sql.append(fields[i].getTableOid());
      if (!hasSourceInfo) sql.append(" AS oid ");
      sql.append(", ");
      sql.append(fields[i].getPositionInTable());
      if (!hasSourceInfo) sql.append(" AS attnum");

      if (!hasSourceInfo) hasSourceInfo = true;
    }
    sql.append(") vals ON (c.oid = vals.oid AND a.attnum = vals.attnum) ");

    if (!hasSourceInfo) return;

    Statement stmt = connection.createStatement();
    ResultSet rs = stmt.executeQuery(sql.toString());
    while (rs.next()) {
      int table = (int) rs.getLong(1);
      int column = (int) rs.getLong(2);
      String columnName = rs.getString(3);
      String tableName = rs.getString(4);
      String schemaName = rs.getString(5);
      int nullable =
          rs.getBoolean(6) ? ResultSetMetaData.columnNoNulls : ResultSetMetaData.columnNullable;
      boolean autoIncrement = rs.getBoolean(7);
      for (int i = 0; i < fields.length; i++) {
        if (fields[i].getTableOid() == table && fields[i].getPositionInTable() == column) {
          fields[i].setColumnName(columnName);
          fields[i].setTableName(tableName);
          fields[i].setSchemaName(schemaName);
          fields[i].setNullable(nullable);
          fields[i].setAutoIncrement(autoIncrement);
        }
      }
    }
    stmt.close();
  }