From cd68ecfef628022de360b1839fcad0f13239b2cd Mon Sep 17 00:00:00 2001 From: Peter Mount Date: Wed, 15 Sep 1999 20:40:02 +0000 Subject: [PATCH] Some late patches from Jens Glaser (jens@jens.de). These upgrade the protocol to version 2, and fixes ResultSetMetaData.getColumnDisplaySize(). --- src/interfaces/jdbc/CHANGELOG | 11 +++++ .../jdbc/postgresql/Connection.java | 44 +++++++++++++++--- src/interfaces/jdbc/postgresql/Field.java | 19 +++++++- .../jdbc/postgresql/errors.properties | 1 + .../jdbc/postgresql/jdbc1/ResultSet.java | 4 +- .../postgresql/jdbc1/ResultSetMetaData.java | 45 +++++++++++++------ .../jdbc/postgresql/jdbc2/ResultSet.java | 4 +- .../postgresql/jdbc2/ResultSetMetaData.java | 45 +++++++++++++------ 8 files changed, 137 insertions(+), 36 deletions(-) diff --git a/src/interfaces/jdbc/CHANGELOG b/src/interfaces/jdbc/CHANGELOG index 68b2aa66fa..33305008df 100644 --- a/src/interfaces/jdbc/CHANGELOG +++ b/src/interfaces/jdbc/CHANGELOG @@ -10,6 +10,17 @@ Mon Sep 13 23:56:00 BST 1999 peter@retep.org.uk - Replaced $$(cmd...) with `cmd...` in the Makefile. This should allow the driver to compile when using shells other than Bash. +Thu Sep 9 01:18:39 MEST 1999 jens@jens.de + - fixed bug in handling of DECIMAL type + +Wed Aug 4 00:25:18 CEST 1999 jens@jens.de + - updated ResultSetMetaData.getColumnDisplaySize() to return + the actual display size + - updated driver to use postgresql FE/BE-protocol version 2 + +Mon Aug 2 03:29:35 CEST 1999 jens@jens.de + - fixed bug in DatabaseMetaData.getPrimaryKeys() + Sun Aug 1 18:05:42 CEST 1999 jens@jens.de - added support for getTransactionIsolation and setTransactionIsolation diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index f8e7c65e22..4d95a585f7 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -10,7 +10,7 @@ import postgresql.largeobject.*; import postgresql.util.*; /** - * $Id: Connection.java,v 1.19 1999/09/14 22:43:38 peter Exp $ + * $Id: Connection.java,v 1.20 1999/09/15 20:39:50 peter Exp $ * * This abstract class is used by postgresql.Driver to open either the JDBC1 or * JDBC2 versions of the Connection class. @@ -44,7 +44,7 @@ public abstract class Connection // These are new for v6.3, they determine the current protocol versions // supported by this version of the driver. They are defined in // src/include/libpq/pqcomm.h - protected static final int PG_PROTOCOL_LATEST_MAJOR = 1; + protected static final int PG_PROTOCOL_LATEST_MAJOR = 2; protected static final int PG_PROTOCOL_LATEST_MINOR = 0; private static final int SM_DATABASE = 64; private static final int SM_USER = 32; @@ -69,7 +69,11 @@ public abstract class Connection // Now handle notices as warnings, so things like "show" now work public SQLWarning firstWarning = null; - + + // The PID an cancellation key we get from the backend process + public int pid; + public int ckey; + /** * This is called by Class.forName() from within postgresql.Driver */ @@ -210,6 +214,33 @@ public abstract class Connection throw new PSQLException("postgresql.con.failed",e); } + + // As of protocol version 2.0, we should now receive the cancellation key and the pid + int beresp = pg_stream.ReceiveChar(); + switch(beresp) { + case 'K': + pid = pg_stream.ReceiveInteger(4); + ckey = pg_stream.ReceiveInteger(4); + break; + case 'E': + case 'N': + throw new SQLException(pg_stream.ReceiveString(4096)); + default: + throw new PSQLException("postgresql.con.setup"); + } + + // Expect ReadyForQuery packet + beresp = pg_stream.ReceiveChar(); + switch(beresp) { + case 'Z': + break; + case 'E': + case 'N': + throw new SQLException(pg_stream.ReceiveString(4096)); + default: + throw new PSQLException("postgresql.con.setup"); + } + // Originally we issued a SHOW DATESTYLE statement to find the databases default // datestyle. However, this caused some problems with timestamps, so in 6.5, we // went the way of ODBC, and set the connection to ISO. @@ -311,7 +342,7 @@ public abstract class Connection switch (c) { case 'A': // Asynchronous Notify - int pid = pg_stream.ReceiveInteger(4); + pid = pg_stream.ReceiveInteger(4); msg = pg_stream.ReceiveString(8192); break; case 'B': // Binary Data Transfer @@ -383,6 +414,8 @@ public abstract class Connection throw new PSQLException("postgresql.con.multres"); fields = ReceiveFields(); break; + case 'Z': // backend ready for query, ignore for now :-) + break; default: throw new PSQLException("postgresql.con.type",new Character((char)c)); } @@ -410,7 +443,8 @@ public abstract class Connection String typname = pg_stream.ReceiveString(8192); int typid = pg_stream.ReceiveIntegerR(4); int typlen = pg_stream.ReceiveIntegerR(2); - fields[i] = new Field(this, typname, typid, typlen); + int typmod = pg_stream.ReceiveIntegerR(4); + fields[i] = new Field(this, typname, typid, typlen, typmod); } return fields; } diff --git a/src/interfaces/jdbc/postgresql/Field.java b/src/interfaces/jdbc/postgresql/Field.java index 0d3c52adc5..ef2012ba60 100644 --- a/src/interfaces/jdbc/postgresql/Field.java +++ b/src/interfaces/jdbc/postgresql/Field.java @@ -14,6 +14,7 @@ public class Field { public int length; // Internal Length of this field public int oid; // OID of the type + public int mod; // type modifier of this field public String name; // Name of this field protected Connection conn; // Connection Instantation @@ -29,14 +30,28 @@ public class Field * @param oid the OID of the field * @param len the length of the field */ - public Field(Connection conn, String name, int oid, int length) + public Field(Connection conn, String name, int oid, int length,int mod) { this.conn = conn; this.name = name; this.oid = oid; this.length = length; + this.mod = mod; } + /** + * Constructor without mod parameter. + * + * @param conn the connection this field came from + * @param name the name of the field + * @param oid the OID of the field + * @param len the length of the field + */ + public Field(Connection conn, String name, int oid, int length) + { + this(conn,name,oid,length,0); + } + /** * @return the oid of this Field's data type */ @@ -103,6 +118,7 @@ public class Field "int4","oid", "int8", "cash","money", + "numeric", "float4", "float8", "bpchar","char","char2","char4","char8","char16", @@ -125,6 +141,7 @@ public class Field Types.INTEGER,Types.INTEGER, Types.BIGINT, Types.DECIMAL,Types.DECIMAL, + Types.NUMERIC, Types.REAL, Types.DOUBLE, Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR,Types.CHAR, diff --git a/src/interfaces/jdbc/postgresql/errors.properties b/src/interfaces/jdbc/postgresql/errors.properties index 1c295ccc6d..2de5802d5b 100644 --- a/src/interfaces/jdbc/postgresql/errors.properties +++ b/src/interfaces/jdbc/postgresql/errors.properties @@ -12,6 +12,7 @@ postgresql.con.kerb5:Kerberos 5 authentication is not supported by this driver. postgresql.con.multres:Cannot handle multiple result groups. postgresql.con.pass:The password property is missing. It is mandatory. postgresql.con.refused:Connection refused. Check that the hostname and port is correct, and that the postmaster is running with the -i flag, which enables TCP/IP networking. +postgresql.con.setup:Protocol error. Session setup failed. postgresql.con.strobj:The object could not be stored. Check that any tables required have already been created in the database. postgresql.con.strobjex:Failed to store object - {0} postgresql.con.toolong:The SQL Statement is too long - {0} diff --git a/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java b/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java index 1ee24a8e29..e28f5a6132 100644 --- a/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java +++ b/src/interfaces/jdbc/postgresql/jdbc1/ResultSet.java @@ -309,7 +309,7 @@ public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSe /** * Get the value of a column in the current row as a - * java.lang.BigDecimal object + * java.math.BigDecimal object * * @param columnIndex the first column is 1, the second is 2... * @param scale the number of digits to the right of the decimal @@ -709,7 +709,7 @@ public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSe case Types.BIGINT: return new Long(getLong(columnIndex)); case Types.NUMERIC: - return getBigDecimal(columnIndex, 0); + return getBigDecimal(columnIndex, ((field.mod-4) & 0xffff)); case Types.REAL: return new Float(getFloat(columnIndex)); case Types.DOUBLE: diff --git a/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java index 0502dcdc56..a5859e3dd8 100644 --- a/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java +++ b/src/interfaces/jdbc/postgresql/jdbc1/ResultSetMetaData.java @@ -188,19 +188,38 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData */ public int getColumnDisplaySize(int column) throws SQLException { - int max = getColumnLabel(column).length(); - int i; - - for (i = 0 ; i < rows.size(); ++i) - { - byte[][] x = (byte[][])(rows.elementAt(i)); - if(x[column-1]!=null) { - int xl = x[column - 1].length; - if (xl > max) - max = xl; - } - } - return max; + Field f = getField(column); + String type_name = f.getTypeName(); + int sql_type = f.getSQLType(); + int typmod = f.mod; + + // I looked at other JDBC implementations and couldn't find a consistent + // interpretation of the "display size" for numeric values, so this is our's + // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de + + // fixed length data types + if (type_name.equals( "int2" )) return 6; // -32768 to +32768 (5 digits and a sign) + if (type_name.equals( "int4" ) + || type_name.equals( "oid" )) return 11; // -2147483648 to +2147483647 + if (type_name.equals( "int8" )) return 20; // -9223372036854775808 to +9223372036854775807 + if (type_name.equals( "money" )) return 12; // MONEY = DECIMAL(9,2) + if (type_name.equals( "float4" )) return 11; // i checked it out ans wasn't able to produce more than 11 digits + if (type_name.equals( "float8" )) return 20; // dito, 20 + if (type_name.equals( "char" )) return 1; + if (type_name.equals( "bool" )) return 1; + if (type_name.equals( "date" )) return 14; // "01/01/4713 BC" - "31/12/32767 AD" + if (type_name.equals( "time" )) return 8; // 00:00:00-23:59:59 + if (type_name.equals( "timestamp" )) return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 + + // variable length fields + typmod -= 4; + if (type_name.equals( "bpchar" ) + || type_name.equals( "varchar" )) return typmod; // VARHDRSZ=sizeof(int32)=4 + if (type_name.equals( "numeric" )) return ( (typmod >>16) & 0xffff ) + + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) + + // if we don't know better + return f.length; } /** diff --git a/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java b/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java index 97fde4a78c..f41b8f0452 100644 --- a/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java +++ b/src/interfaces/jdbc/postgresql/jdbc2/ResultSet.java @@ -310,7 +310,7 @@ public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSe /** * Get the value of a column in the current row as a - * java.lang.BigDecimal object + * java.math.BigDecimal object * * @param columnIndex the first column is 1, the second is 2... * @param scale the number of digits to the right of the decimal @@ -723,7 +723,7 @@ public class ResultSet extends postgresql.ResultSet implements java.sql.ResultSe case Types.BIGINT: return new Long(getLong(columnIndex)); case Types.NUMERIC: - return getBigDecimal(columnIndex, 0); + return getBigDecimal(columnIndex, ((field.mod-4) & 0xffff)); case Types.REAL: return new Float(getFloat(columnIndex)); case Types.DOUBLE: diff --git a/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java index 84aeafec65..7956dea96f 100644 --- a/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java +++ b/src/interfaces/jdbc/postgresql/jdbc2/ResultSetMetaData.java @@ -183,19 +183,38 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData */ public int getColumnDisplaySize(int column) throws SQLException { - int max = getColumnLabel(column).length(); - int i; - - for (i = 0 ; i < rows.size(); ++i) - { - byte[][] x = (byte[][])(rows.elementAt(i)); - if(x[column-1]!=null) { - int xl = x[column - 1].length; - if (xl > max) - max = xl; - } - } - return max; + Field f = getField(column); + String type_name = f.getTypeName(); + int sql_type = f.getSQLType(); + int typmod = f.mod; + + // I looked at other JDBC implementations and couldn't find a consistent + // interpretation of the "display size" for numeric values, so this is our's + // FIXME: currently, only types with a SQL92 or SQL3 pendant are implemented - jens@jens.de + + // fixed length data types + if (type_name.equals( "int2" )) return 6; // -32768 to +32768 (5 digits and a sign) + if (type_name.equals( "int4" ) + || type_name.equals( "oid" )) return 11; // -2147483648 to +2147483647 + if (type_name.equals( "int8" )) return 20; // -9223372036854775808 to +9223372036854775807 + if (type_name.equals( "money" )) return 12; // MONEY = DECIMAL(9,2) + if (type_name.equals( "float4" )) return 11; // i checked it out ans wasn't able to produce more than 11 digits + if (type_name.equals( "float8" )) return 20; // dito, 20 + if (type_name.equals( "char" )) return 1; + if (type_name.equals( "bool" )) return 1; + if (type_name.equals( "date" )) return 14; // "01/01/4713 BC" - "31/12/32767 AD" + if (type_name.equals( "time" )) return 8; // 00:00:00-23:59:59 + if (type_name.equals( "timestamp" )) return 22; // hhmmm ... the output looks like this: 1999-08-03 22:22:08+02 + + // variable length fields + typmod -= 4; + if (type_name.equals( "bpchar" ) + || type_name.equals( "varchar" )) return typmod; // VARHDRSZ=sizeof(int32)=4 + if (type_name.equals( "numeric" )) return ( (typmod >>16) & 0xffff ) + + 1 + ( typmod & 0xffff ); // DECIMAL(p,s) = (p digits).(s digits) + + // if we don't know better + return f.length; } /** -- 2.40.0