Per the recent discussion there's been some code changes in JDBC's

DatabaseMetaData.getColumn(). I proposed a patch that would change the
number of queries to find out all columns in a table from 2 * N + 1 to 1 (N
being the number of columns reported) by using some outer joins. I also
fixed the fact that getColumns() only returned columns that had a default
defined. OTOH, I did not use to change the code required for obtaining a
column's remarks (by using col_description() for 7.2  and requested by Tom
Lane).

Finally, I have found a way to get all the column details in a single query
*and* use col_description() for 7.2 servers. A patch is attached. It
overrules Ren? Pijlman's fix for this that was committed just today, but
still used N + 1 queries (sorry Ren? ;-) )

I also fixed the return values for TABLE_CAT and TABLE_SCHEM from "" to
null, to be more standard compliant (and requested in Ren?'s mail found at
http://fts.postgresql.org/db/mw/msg.html?mid=1034253).

As always, the JDBC1 version has not been tested as I have no JDK 1.1

Jeroen van Vianen
This commit is contained in:
Bruce Momjian 2001-09-29 03:08:01 +00:00
parent 5b328502b5
commit 07ce9fe61d
2 changed files with 196 additions and 176 deletions

View File

@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException;
/** /**
* This class provides information about the database as a whole. * This class provides information about the database as a whole.
* *
* $Id: DatabaseMetaData.java,v 1.32 2001/09/10 14:55:08 momjian Exp $ * $Id: DatabaseMetaData.java,v 1.33 2001/09/29 03:08:01 momjian Exp $
* *
* <p>Many of the methods here return lists of information in ResultSets. You * <p>Many of the methods here return lists of information in ResultSets. You
* can use the normal ResultSet methods such as getString and getInt to * can use the normal ResultSet methods such as getString and getInt to
@ -1895,21 +1895,19 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
*/ */
public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException
{ {
// the field descriptors for the new ResultSet
Field f[] = new Field[18];
java.sql.ResultSet r; // ResultSet for the SQL query that we need to do
Vector v = new Vector(); // The new ResultSet tuple stuff Vector v = new Vector(); // The new ResultSet tuple stuff
Field f[] = new Field[18]; // The field descriptors for the new ResultSet
f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32);
f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32);
f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32);
f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2);
f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32);
f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4);
f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32); f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32);
f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4);
f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4);
f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4);
f[11] = new Field(connection, "REMARKS", iVarcharOid, 32); f[11] = new Field(connection, "REMARKS", iVarcharOid, 32);
f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32); f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32);
@ -1919,93 +1917,105 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4); f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4);
f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32); f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32);
// Added by Stefan Andreasen <stefan@linux.kapow.dk> StringBuffer sql = new StringBuffer(512);
// If the pattern are null then set them to %
if (tableNamePattern == null) tableNamePattern="%";
if (columnNamePattern == null) columnNamePattern="%";
// Now form the query /* Build a >= 7.1 SQL statement to list all columns */
String query = sql.append("select " +
"select " + (connection.haveMinimumServerVersion("7.2") ? "a.attrelid, " : "a.oid, ") +
(connection.haveMinimumServerVersion("7.2") ? "a.attrelid" : "a.oid") + " c.relname, " +
",c.relname,a.attname,a.atttypid," + " a.attname, " +
"a.attnum,a.attnotnull,a.attlen,a.atttypmod,d.adsrc " + " a.atttypid, " +
"from (pg_class c inner join pg_attribute a " + " a.attnum, " +
"on (c.oid=a.attrelid) ) " + " a.attnotnull, " +
"left outer join pg_attrdef d " + " a.attlen, " +
"on (c.oid=d.adrelid and d.adnum=a.attnum) " + " a.atttypmod, " +
"where " + " d.adsrc, " +
"c.relname like '"+tableNamePattern.toLowerCase()+"' and " + " t.typname, " +
"a.attname like '"+columnNamePattern.toLowerCase()+"' and " + /* Use the new col_description in 7.2 or an additional outer join in 7.1 */
"a.attnum>0 " + (connection.haveMinimumServerVersion("7.2") ? "col_description(a.attrelid, a.attnum) " : "e.description ") +
"order by c.relname,a.attnum"; "from" +
" (" +
" (pg_class c inner join pg_attribute a on" +
" (" +
" a.attrelid=c.oid");
r = connection.ExecSQL(query); if ((tableNamePattern != null) && ! tableNamePattern.equals("%")) {
sql.append(" and c.relname like \'" + tableNamePattern + "\'");
}
while(r.next()) { if ((columnNamePattern != null) && ! columnNamePattern.equals("%")) {
byte[][] tuple = new byte[18][0]; sql.append(" and a.attname like \'" + columnNamePattern + "\'");
}
// Fetch the description for the table (if any) sql.append(
String getDescriptionStatement = " and a.attnum > 0" +
connection.haveMinimumServerVersion("7.2") ? " )" +
"select col_description(" + r.getInt(1) + "," + r.getInt(5) + ")" : " ) inner join pg_type t on" +
"select description from pg_description where objoid=" + r.getInt(1); " (" +
" t.oid = a.atttypid" +
" )" +
" )" +
" left outer join pg_attrdef d on" +
" (" +
" c.oid = d.adrelid" +
" and a.attnum = d.adnum" +
" ) ");
java.sql.ResultSet dr = connection.ExecSQL(getDescriptionStatement); if (!connection.haveMinimumServerVersion("7.2")) {
/* Only for 7.1 */
sql.append(
" left outer join pg_description e on" +
" (" +
" e.objoid = a.oid" +
" ) ");
}
if(((org.postgresql.ResultSet)dr).getTupleCount()==1) { sql.append("order by" +
dr.next(); " c.relname, a.attnum");
tuple[11] = dr.getBytes(1);
} else
tuple[11] = null;
dr.close();
tuple[0] = "".getBytes(); // Catalog name java.sql.ResultSet r = connection.ExecSQL(sql.toString());
tuple[1] = "".getBytes(); // Schema name while (r.next()) {
tuple[2] = r.getBytes(2); // Table name byte[][] tuple = new byte[18][0];
tuple[3] = r.getBytes(3); // Column name
dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4)); String nullFlag = r.getString(6);
dr.next(); String typname = r.getString(10);
String typname=dr.getString(1);
dr.close();
tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type
tuple[5] = typname.getBytes(); // Type name
// Column size tuple[0] = null; // Catalog name, not supported
// Looking at the psql source, tuple[1] = null; // Schema name, not supported
// I think the length of a varchar as specified when the table was created tuple[2] = r.getBytes(2); // Table name
// should be extracted from atttypmod which contains this length + sizeof(int32) tuple[3] = r.getBytes(3); // Column name
if (typname.equals("bpchar") || typname.equals("varchar")) { tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type
int atttypmod = r.getInt(8); tuple[5] = typname.getBytes(); // Type name
tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
} else
tuple[6] = r.getBytes(7);
tuple[7] = null; // Buffer length // Column size
// Looking at the psql source,
// I think the length of a varchar as specified when the table was created
// should be extracted from atttypmod which contains this length + sizeof(int32)
if (typname.equals("bpchar") || typname.equals("varchar")) {
int atttypmod = r.getInt(8);
tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
} else {
tuple[6] = r.getBytes(7);
}
tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? tuple[7] = null; // Buffer length
tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal tuple[8] = "0".getBytes(); // Decimal Digits - how to get this?
tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal
tuple[10] = Integer.toString(nullFlag.equals("f") ?
java.sql.DatabaseMetaData.columnNullable :
java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
tuple[11] = r.getBytes(11); // Description (if any)
tuple[12] = r.getBytes(9); // Column default
tuple[13] = null; // sql data type (unused)
tuple[14] = null; // sql datetime sub (unused)
tuple[15] = tuple[6]; // char octet length
tuple[16] = r.getBytes(5); // ordinal position
tuple[17] = (nullFlag.equals("f") ? "YES" : "NO").getBytes(); // Is nullable
// tuple[10] is below v.addElement(tuple);
// tuple[11] is above }
r.close();
tuple[12] = r.getBytes(9); // column default
tuple[13] = null; // sql data type (unused)
tuple[14] = null; // sql datetime sub (unused)
tuple[15] = tuple[6]; // char octet length
tuple[16] = r.getBytes(5); // ordinal position
String nullFlag = r.getString(6);
tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes(); // is nullable
v.addElement(tuple);
}
r.close();
return new ResultSet(connection, f, v, "OK", 1); return new ResultSet(connection, f, v, "OK", 1);
} }

View File

@ -13,7 +13,7 @@ import org.postgresql.util.PSQLException;
/** /**
* This class provides information about the database as a whole. * This class provides information about the database as a whole.
* *
* $Id: DatabaseMetaData.java,v 1.36 2001/09/10 14:55:08 momjian Exp $ * $Id: DatabaseMetaData.java,v 1.37 2001/09/29 03:08:01 momjian Exp $
* *
* <p>Many of the methods here return lists of information in ResultSets. You * <p>Many of the methods here return lists of information in ResultSets. You
* can use the normal ResultSet methods such as getString and getInt to * can use the normal ResultSet methods such as getString and getInt to
@ -1895,21 +1895,19 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
*/ */
public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException
{ {
// the field descriptors for the new ResultSet
Field f[] = new Field[18];
java.sql.ResultSet r; // ResultSet for the SQL query that we need to do
Vector v = new Vector(); // The new ResultSet tuple stuff Vector v = new Vector(); // The new ResultSet tuple stuff
Field f[] = new Field[18]; // The field descriptors for the new ResultSet
f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32); f[0] = new Field(connection, "TABLE_CAT", iVarcharOid, 32);
f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32); f[1] = new Field(connection, "TABLE_SCHEM", iVarcharOid, 32);
f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32); f[2] = new Field(connection, "TABLE_NAME", iVarcharOid, 32);
f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32); f[3] = new Field(connection, "COLUMN_NAME", iVarcharOid, 32);
f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2); f[4] = new Field(connection, "DATA_TYPE", iInt2Oid, 2);
f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32); f[5] = new Field(connection, "TYPE_NAME", iVarcharOid, 32);
f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4); f[6] = new Field(connection, "COLUMN_SIZE", iInt4Oid, 4);
f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32); f[7] = new Field(connection, "BUFFER_LENGTH", iVarcharOid, 32);
f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4); f[8] = new Field(connection, "DECIMAL_DIGITS", iInt4Oid, 4);
f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4); f[9] = new Field(connection, "NUM_PREC_RADIX", iInt4Oid, 4);
f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4); f[10] = new Field(connection, "NULLABLE", iInt4Oid, 4);
f[11] = new Field(connection, "REMARKS", iVarcharOid, 32); f[11] = new Field(connection, "REMARKS", iVarcharOid, 32);
f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32); f[12] = new Field(connection, "COLUMN_DEF", iVarcharOid, 32);
@ -1919,93 +1917,105 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4); f[16] = new Field(connection, "ORDINAL_POSITION", iInt4Oid,4);
f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32); f[17] = new Field(connection, "IS_NULLABLE", iVarcharOid, 32);
// Added by Stefan Andreasen <stefan@linux.kapow.dk> StringBuffer sql = new StringBuffer(512);
// If the pattern are null then set them to %
if (tableNamePattern == null) tableNamePattern="%";
if (columnNamePattern == null) columnNamePattern="%";
// Now form the query /* Build a >= 7.1 SQL statement to list all columns */
String query = sql.append("select " +
"select " + (connection.haveMinimumServerVersion("7.2") ? "a.attrelid, " : "a.oid, ") +
(connection.haveMinimumServerVersion("7.2") ? "a.attrelid" : "a.oid") + " c.relname, " +
",c.relname,a.attname,a.atttypid," + " a.attname, " +
"a.attnum,a.attnotnull,a.attlen,a.atttypmod,d.adsrc " + " a.atttypid, " +
"from (pg_class c inner join pg_attribute a " + " a.attnum, " +
"on (c.oid=a.attrelid) ) " + " a.attnotnull, " +
"left outer join pg_attrdef d " + " a.attlen, " +
"on (c.oid=d.adrelid and d.adnum=a.attnum) " + " a.atttypmod, " +
"where " + " d.adsrc, " +
"c.relname like '"+tableNamePattern.toLowerCase()+"' and " + " t.typname, " +
"a.attname like '"+columnNamePattern.toLowerCase()+"' and " + /* Use the new col_description in 7.2 or an additional outer join in 7.1 */
"a.attnum>0 " + (connection.haveMinimumServerVersion("7.2") ? "col_description(a.attrelid, a.attnum) " : "e.description ") +
"order by c.relname,a.attnum"; "from" +
" (" +
" (pg_class c inner join pg_attribute a on" +
" (" +
" a.attrelid=c.oid");
r = connection.ExecSQL(query); if ((tableNamePattern != null) && ! tableNamePattern.equals("%")) {
sql.append(" and c.relname like \'" + tableNamePattern + "\'");
}
while(r.next()) { if ((columnNamePattern != null) && ! columnNamePattern.equals("%")) {
byte[][] tuple = new byte[18][0]; sql.append(" and a.attname like \'" + columnNamePattern + "\'");
}
// Fetch the description for the table (if any) sql.append(
String getDescriptionStatement = " and a.attnum > 0" +
connection.haveMinimumServerVersion("7.2") ? " )" +
"select col_description(" + r.getInt(1) + "," + r.getInt(5) + ")" : " ) inner join pg_type t on" +
"select description from pg_description where objoid=" + r.getInt(1); " (" +
" t.oid = a.atttypid" +
" )" +
" )" +
" left outer join pg_attrdef d on" +
" (" +
" c.oid = d.adrelid" +
" and a.attnum = d.adnum" +
" ) ");
java.sql.ResultSet dr = connection.ExecSQL(getDescriptionStatement); if (!connection.haveMinimumServerVersion("7.2")) {
/* Only for 7.1 */
sql.append(
" left outer join pg_description e on" +
" (" +
" e.objoid = a.oid" +
" ) ");
}
if(((org.postgresql.ResultSet)dr).getTupleCount()==1) { sql.append("order by" +
dr.next(); " c.relname, a.attnum");
tuple[11] = dr.getBytes(1);
} else
tuple[11] = null;
dr.close();
tuple[0] = "".getBytes(); // Catalog name java.sql.ResultSet r = connection.ExecSQL(sql.toString());
tuple[1] = "".getBytes(); // Schema name while (r.next()) {
tuple[2] = r.getBytes(2); // Table name byte[][] tuple = new byte[18][0];
tuple[3] = r.getBytes(3); // Column name
dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4)); String nullFlag = r.getString(6);
dr.next(); String typname = r.getString(10);
String typname=dr.getString(1);
dr.close();
tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type
tuple[5] = typname.getBytes(); // Type name
// Column size tuple[0] = null; // Catalog name, not supported
// Looking at the psql source, tuple[1] = null; // Schema name, not supported
// I think the length of a varchar as specified when the table was created tuple[2] = r.getBytes(2); // Table name
// should be extracted from atttypmod which contains this length + sizeof(int32) tuple[3] = r.getBytes(3); // Column name
if (typname.equals("bpchar") || typname.equals("varchar")) { tuple[4] = Integer.toString(connection.getSQLType(typname)).getBytes(); // Data type
int atttypmod = r.getInt(8); tuple[5] = typname.getBytes(); // Type name
tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
} else
tuple[6] = r.getBytes(7);
tuple[7] = null; // Buffer length // Column size
// Looking at the psql source,
// I think the length of a varchar as specified when the table was created
// should be extracted from atttypmod which contains this length + sizeof(int32)
if (typname.equals("bpchar") || typname.equals("varchar")) {
int atttypmod = r.getInt(8);
tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes();
} else {
tuple[6] = r.getBytes(7);
}
tuple[8] = "0".getBytes(); // Decimal Digits - how to get this? tuple[7] = null; // Buffer length
tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal tuple[8] = "0".getBytes(); // Decimal Digits - how to get this?
tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal
tuple[10] = Integer.toString(nullFlag.equals("f") ?
java.sql.DatabaseMetaData.columnNullable :
java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
tuple[11] = r.getBytes(11); // Description (if any)
tuple[12] = r.getBytes(9); // Column default
tuple[13] = null; // sql data type (unused)
tuple[14] = null; // sql datetime sub (unused)
tuple[15] = tuple[6]; // char octet length
tuple[16] = r.getBytes(5); // ordinal position
tuple[17] = (nullFlag.equals("f") ? "YES" : "NO").getBytes(); // Is nullable
// tuple[10] is below v.addElement(tuple);
// tuple[11] is above }
r.close();
tuple[12] = r.getBytes(9); // column default
tuple[13] = null; // sql data type (unused)
tuple[14] = null; // sql datetime sub (unused)
tuple[15] = tuple[6]; // char octet length
tuple[16] = r.getBytes(5); // ordinal position
String nullFlag = r.getString(6);
tuple[10] = Integer.toString(nullFlag.equals("f")?java.sql.DatabaseMetaData.columnNullable:java.sql.DatabaseMetaData.columnNoNulls).getBytes(); // Nullable
tuple[17] = (nullFlag.equals("f")?"YES":"NO").getBytes(); // is nullable
v.addElement(tuple);
}
r.close();
return new ResultSet(connection, f, v, "OK", 1); return new ResultSet(connection, f, v, "OK", 1);
} }