getTypeInfo()'s javadoc claims the returned results should be ordered by java.sql.Types value (DATA_TYPE column), then by how well the underlying type fits the Types.* type. The postgresql driver doesn't seem to impose any ordering at all on its results. This patch orders the resultset by Types.* value, then by a manually specified ordering within each type. This also removes some of the redundancy between the JDBC1 and JDBC2 implementations of getSQLType(). The sorting of the resultset is pretty gross but I couldn't see a minimal cleaner solution. Index: src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java,v retrieving revision 1.13 diff -u -u -r1.13 AbstractJdbc1Connection.java --- src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java 2002/11/14 05:35:45 1.13 +++ src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java 2002/12/17 03:15:24 @@ -1362,21 +1362,39 @@ /* * This implemetation uses the jdbc1Types array to support the jdbc1 - * datatypes. Basically jdbc1 and jdbc2 are the same, except that - * jdbc2 adds the Array types. + * datatypes. The JDBC2 implementation overrides this method to add + * the Array types. */ public int getSQLType(String pgTypeName) { - int sqlType = Types.OTHER; // default value for (int i = 0;i < jdbc1Types.length;i++) { if (pgTypeName.equals(jdbc1Types[i])) { - sqlType = jdbc1Typei[i]; - break; + return jdbc1Typei[i]; } } - return sqlType; + + return Types.OTHER; // default value + } + + /* + * This method returns a sort key for a postgres datatype name. Lower sort keys + * indicate a better mapping to the JDBC type (within a particular java.sql.Types + * range). This is used to order the resultset returned by + * DatabaseMetaData.getTypeInfo(). + */ + public int getSQLTypePreference(String pgTypeName) + { + for (int i = 0;i < jdbc1Types.length;i++) + { + if (pgTypeName.equals(jdbc1Types[i])) + { + return i; + } + } + + return jdbc1Types.length; } /* @@ -1385,49 +1403,47 @@ * They default automatically to Types.OTHER * * Note: This must be in the same order as below. - * - * Tip: keep these grouped together by the Types. value + * Note: The ordering of these types reflects the ordering of the returned + * resultset from DatabaseMetaData.getTypeInfo(). The JDBC spec requires + * this to be ordered by Types.* value, then by how well the db type + * corresponds to the JDBC type (best mappings first). */ private static final String jdbc1Types[] = { - "int2", - "int4", "oid", - "int8", - "cash", "money", - "numeric", - "float4", - "float8", - "bpchar", "char", "char2", "char4", "char8", "char16", - "varchar", "text", "name", "filename", - "bytea", - "bool", - "date", - "time", - "abstime", "timestamp", "timestamptz" - }; + "int2", + "int4", "oid", + "int8", + "float8", "cash", "money", + "numeric", + "float4", + "char", "char2", "char4", "char8", "char16", "bpchar", + "varchar", "text", "name", "filename", + "bytea", + "bool", + "date", + "time", + "abstime", "timestamp", "timestamptz" + }; /* * This table holds the JDBC type for each entry above. * * Note: This must be in the same order as above - * - * Tip: keep these grouped together by the Types. value */ private static final int jdbc1Typei[] = { - Types.SMALLINT, - Types.INTEGER, Types.INTEGER, - Types.BIGINT, - Types.DOUBLE, Types.DOUBLE, - Types.NUMERIC, - Types.REAL, - Types.DOUBLE, - Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, - Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, - Types.BINARY, - Types.BIT, - Types.DATE, - Types.TIME, - Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP - }; + Types.SMALLINT, + Types.INTEGER, Types.INTEGER, + Types.BIGINT, + Types.DOUBLE, Types.DOUBLE, Types.DOUBLE, + Types.NUMERIC, + Types.REAL, + Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, + Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, + Types.VARBINARY, + Types.BIT, + Types.DATE, + Types.TIME, + Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP + }; //Methods to support postgres notifications public void addNotification(org.postgresql.PGNotification p_notification) Index: src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java,v retrieving revision 1.13 diff -u -u -r1.13 AbstractJdbc1DatabaseMetaData.java --- src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java 2002/12/11 21:02:58 1.13 +++ src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1DatabaseMetaData.java 2002/12/17 05:45:33 @@ -3492,6 +3495,32 @@ v.addElement(tuple); } rs.close(); + + // Sort on type preference to get the correct ordering. + // A bit gross as we have to resurrect typname from its encoding. + Object[] contents = v.toArray(); + Arrays.sort(contents, + new Comparator() + { + public int compare(Object o1, Object o2) + { + byte[][] tuple1 = (byte[][])o1; + byte[][] tuple2 = (byte[][])o2; + String typname1 = new String(tuple1[0]); + String typname2 = new String(tuple2[0]); + + int type1 = connection.getSQLType(typname1); + int type2 = connection.getSQLType(typname2); + int pref1 = connection.getSQLTypePreference(typname1); + int pref2 = connection.getSQLTypePreference(typname2); + + return type1 < type2 ? -1 : type1 > type2 ? 1 : pref1 < pref2 ? -1 : pref1 > pref2 ? 1 : 0; + } + }); + + v.clear(); + v.addAll(Arrays.asList(contents)); + return connection.getResultSet(null, f, v, "OK", 1); } Index: src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java,v retrieving revision 1.2 diff -u -u -r1.2 AbstractJdbc2Connection.java --- src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java 2002/09/06 21:23:06 1.2 +++ src/interfaces/jdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java 2002/12/17 05:45:33 @@ -131,85 +131,69 @@ /* - * This implemetation uses the jdbc2Types array to support the jdbc2 - * datatypes. Basically jdbc1 and jdbc2 are the same, except that - * jdbc2 adds the Array types. + * This implementation uses the jdbc2Types array to support the jdbc2 + * datatypes. Only JDBC2-specific types are handled here; all others are + * delegated to the JDBC1 implementation. */ public int getSQLType(String pgTypeName) { - int sqlType = Types.OTHER; // default value for (int i = 0;i < jdbc2Types.length;i++) { if (pgTypeName.equals(jdbc2Types[i])) { - sqlType = jdbc2Typei[i]; - break; + return jdbc2Typei[i]; } } - return sqlType; + + return super.getSQLType(pgTypeName); } /* + * This implementation uses the jdbc2Types array to support the jdbc2 + * datatypes. Only JDBC2-specific types are handled here; all others are + * delegated to the JDBC1 implementation. + */ + public int getSQLTypePreference(String pgTypeName) + { + for (int i = 0; i < jdbc2Types.length;i++) + { + if (pgTypeName.equals(jdbc2Types[i])) + { + return i; + } + } + + return jdbc2Types.length + super.getSQLTypePreference(pgTypeName); + } + + /* * This table holds the org.postgresql names for the types supported. - * Any types that map to Types.OTHER (eg POINT) don't go into this table. - * They default automatically to Types.OTHER + * Only JDBC2-specific types are specified here. * * Note: This must be in the same order as below. - * - * Tip: keep these grouped together by the Types. value + * Note: The ordering of these types reflects the ordering of the returned + * resultset from DatabaseMetaData.getTypeInfo(). The JDBC spec requires + * this to be ordered by Types.* value, then by how well the db type + * corresponds to the JDBC type (best mappings first). */ private static final String jdbc2Types[] = { - "int2", - "int4", "oid", - "int8", - "cash", "money", - "numeric", - "float4", - "float8", - "bpchar", "char", "char2", "char4", "char8", "char16", - "varchar", "text", "name", "filename", - "bytea", - "bool", - "date", - "time", - "abstime", "timestamp", "timestamptz", - "_bool", "_char", "_int2", "_int4", "_text", - "_oid", "_varchar", "_int8", "_float4", "_float8", - "_abstime", "_date", "_time", "_timestamp", "_numeric", - "_bytea" - }; + "_bool", "_char", "_int2", "_int4", "_text", + "_oid", "_varchar", "_int8", "_float4", "_float8", + "_abstime", "_date", "_time", "_timestamp", "_numeric", + "_bytea" + }; /* * This table holds the JDBC type for each entry above. * * Note: This must be in the same order as above - * - * Tip: keep these grouped together by the Types. value */ private static final int jdbc2Typei[] = { - Types.SMALLINT, - Types.INTEGER, Types.INTEGER, - Types.BIGINT, - Types.DOUBLE, Types.DOUBLE, - Types.NUMERIC, - Types.REAL, - Types.DOUBLE, - Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, - Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, - Types.BINARY, - Types.BIT, - Types.DATE, - Types.TIME, - Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP, - Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, - Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, - Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, - Types.ARRAY - }; - - - - + Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, + Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, + Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, + Types.ARRAY + }; }