diff options
author | Bruce Momjian <bruce@momjian.us> | 1998-09-03 02:31:38 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 1998-09-03 02:31:38 +0000 |
commit | 35a945f2b5b2e8f5ba8cbe6d70678c9799504c10 (patch) | |
tree | 63d675d8dd5cae2acb31fb7a5b6027b4e415e6a9 /src | |
parent | c37adac74e7aced5eb3e562e2fa05b4f07da41e0 (diff) | |
download | postgresql-35a945f2b5b2e8f5ba8cbe6d70678c9799504c10.tar.gz postgresql-35a945f2b5b2e8f5ba8cbe6d70678c9799504c10.zip |
New stuff from Peter Mount for jdbc.
Diffstat (limited to 'src')
-rw-r--r-- | src/interfaces/jdbc/example/psql.java | 2 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/Connection.java | 105 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/DatabaseMetaData.java | 172 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/Driver.java | 2 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/PG_Stream.java | 58 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/PreparedStatement.java | 23 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/ResultSet.java | 6 | ||||
-rw-r--r-- | src/interfaces/jdbc/postgresql/ResultSetMetaData.java | 28 |
8 files changed, 198 insertions, 198 deletions
diff --git a/src/interfaces/jdbc/example/psql.java b/src/interfaces/jdbc/example/psql.java index adad24f4d08..2ad18b198bf 100644 --- a/src/interfaces/jdbc/example/psql.java +++ b/src/interfaces/jdbc/example/psql.java @@ -123,7 +123,7 @@ public class psql if(rs.wasNull()) System.out.print("{null}"+(i<cols?"\t":"\n")); else - System.out.print(rs.getObject(i).toString()+(i<cols?"\t":"\n")); + System.out.print(o.toString()+(i<cols?"\t":"\n")); } } diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index 7c565e145fa..31f6e11980e 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -34,6 +34,9 @@ public class Connection implements java.sql.Connection // This is set by postgresql.Statement.setMaxRows() protected int maxrows = 0; // maximum no. of rows; 0 = unlimited + // This is a cache of the DatabaseMetaData instance for this connection + protected DatabaseMetaData metadata; + private String PG_HOST; private int PG_PORT; private String PG_USER; @@ -44,17 +47,6 @@ public class Connection implements java.sql.Connection public boolean CONNECTION_OK = true; public boolean CONNECTION_BAD = false; - //private static final int STARTUP_LEN = 288; // Length of a startup packet - - // These are defined in src/include/libpq/pqcomm.h - //private int STARTUP_CODE = STARTUP_USER; - //private static final int STARTUP_USER = 7; // User auth - //private static final int STARTUP_KRB4 = 10; // Kerberos 4 (unused) - //private static final int STARTUP_KRB5 = 11; // Kerberos 5 (unused) - //private static final int STARTUP_HBA = 12; // Host Based - //private static final int STARTUP_NONE = 13; // Unauthenticated (unused) - //private static final int STARTUP_PASS = 14; // Password auth - private boolean autoCommit = true; private boolean readOnly = false; @@ -88,12 +80,6 @@ public class Connection implements java.sql.Connection // be across all connections, which could be to different backends. protected Hashtable fieldCache = new Hashtable(); - // This is used by Field to cache oid -> names. - // It's here, because it's shared across this connection only. - // Hence it cannot be static within the Field class, because it would then - // be across all connections, which could be to different backends. - protected Hashtable fieldCache = new Hashtable(); - /** * This is the current date style of the backend */ @@ -150,8 +136,6 @@ public class Connection implements java.sql.Connection */ public Connection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException { - //int len = STARTUP_LEN; // Length of a startup packet - // Throw an exception if the user or password properties are missing // This occasionally occurs when the client uses the properties version // of getConnection(), and is a common question on the email lists @@ -169,30 +153,15 @@ public class Connection implements java.sql.Connection PG_HOST = new String(host); PG_STATUS = CONNECTION_BAD; - // Pre 6.3 code - // This handles the auth property. Any value begining with p enables - // password authentication, while anything begining with i enables - // ident (RFC 1413) authentication. Any other values default to trust. - // - // Also, the postgresql.auth system property can be used to change the - // local default, if the auth property is not present. - // - //String auth = info.getProperty("auth",System.getProperty("postgresql.auth","trust")).toLowerCase(); - //if(auth.startsWith("p")) { - //// Password authentication - //STARTUP_CODE=STARTUP_PASS; - //} else if(auth.startsWith("i")) { - //// Ident (RFC 1413) authentication - //STARTUP_CODE=STARTUP_HBA; - //} else { - //// Anything else defaults to trust authentication - //STARTUP_CODE=STARTUP_USER; - //} - // Now make the initial connection try { pg_stream = new PG_Stream(host, port); + } catch (ConnectException cex) { + // Added by Peter Mount <peter@retep.org.uk> + // ConnectException is thrown when the connection cannot be made. + // we trap this an return a more meaningful message for the end user + throw new SQLException ("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."); } catch (IOException e) { throw new SQLException ("Connection failed: " + e.toString()); } @@ -200,30 +169,17 @@ public class Connection implements java.sql.Connection // Now we need to construct and send a startup packet try { - // Pre 6.3 code - //pg_stream.SendInteger(len, 4); len -= 4; - //pg_stream.SendInteger(STARTUP_CODE, 4); len -= 4; - //pg_stream.Send(database.getBytes(), 64); len -= 64; - //pg_stream.Send(PG_USER.getBytes(), len); - // - //// Send the password packet if required - //if(STARTUP_CODE == STARTUP_PASS) { - //len=STARTUP_LEN; - //pg_stream.SendInteger(len, 4); len -= 4; - //pg_stream.SendInteger(STARTUP_PASS, 4); len -= 4; - //pg_stream.Send(PG_USER.getBytes(), PG_USER.length()); - //len-=PG_USER.length(); - //pg_stream.SendInteger(0,1); len -= 1; - //pg_stream.Send(PG_PASSWORD.getBytes(), len); - //} - // Ver 6.3 code pg_stream.SendInteger(4+4+SM_DATABASE+SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY,4); pg_stream.SendInteger(PG_PROTOCOL_LATEST_MAJOR,2); pg_stream.SendInteger(PG_PROTOCOL_LATEST_MINOR,2); pg_stream.Send(database.getBytes(),SM_DATABASE); + + // This last send includes the unused fields pg_stream.Send(PG_USER.getBytes(),SM_USER+SM_OPTIONS+SM_UNUSED+SM_TTY); - // The last send includes the unused fields + + // now flush the startup packets to the backend + pg_stream.flush(); // Now get the response from the backend, either an error message // or an authentication request @@ -233,6 +189,12 @@ public class Connection implements java.sql.Connection switch(beresp) { case 'E': + // An error occured, so pass the error message to the + // user. + // + // The most common one to be thrown here is: + // "User authentication failed" + // throw new SQLException(pg_stream.ReceiveString(4096)); case 'R': @@ -267,7 +229,7 @@ public class Connection implements java.sql.Connection pg_stream.SendInteger(5+PG_PASSWORD.length(),4); pg_stream.Send(PG_PASSWORD.getBytes()); pg_stream.SendInteger(0,1); - //pg_stream.SendPacket(PG_PASSWORD.getBytes()); + pg_stream.flush(); break; case AUTH_REQ_CRYPT: @@ -276,11 +238,11 @@ public class Connection implements java.sql.Connection pg_stream.SendInteger(5+crypted.length(),4); pg_stream.Send(crypted.getBytes()); pg_stream.SendInteger(0,1); - //pg_stream.SendPacket(UnixCrypt.crypt(salt,PG_PASSWORD).getBytes()); + pg_stream.flush(); break; default: - throw new SQLException("Authentication type "+areq+" not supported"); + throw new SQLException("Authentication type "+areq+" not supported. Check that you have configured the pg_hba.conf file to include the client's IP address or Subnet, and is using a supported authentication scheme."); } break; @@ -511,7 +473,9 @@ public class Connection implements java.sql.Connection */ public java.sql.DatabaseMetaData getMetaData() throws SQLException { - return new DatabaseMetaData(this); + if(metadata==null) + metadata = new DatabaseMetaData(this); + return metadata; } /** @@ -631,8 +595,6 @@ public class Connection implements java.sql.Connection */ public void addWarning(String msg) { - //PrintStream log = DriverManager.getLogStream(); - //if(log!=null) DriverManager.println(msg); // Add the warning to the chain @@ -691,6 +653,7 @@ public class Connection implements java.sql.Connection buf = sql.getBytes(); pg_stream.Send(buf); pg_stream.SendChar(0); + pg_stream.flush(); } catch (IOException e) { throw new SQLException("I/O Error: " + e.toString()); } @@ -726,6 +689,7 @@ public class Connection implements java.sql.Connection pg_stream.SendChar('Q'); pg_stream.SendChar(' '); pg_stream.SendChar(0); + pg_stream.flush(); } catch (IOException e) { throw new SQLException("I/O Error: " + e.toString()); } @@ -964,6 +928,8 @@ public class Connection implements java.sql.Connection return ((Serialize)o).fetch(Integer.parseInt(value)); } } catch(SQLException sx) { + // rethrow the exception. Done because we capture any others next + sx.fillInStackTrace(); throw sx; } catch(Exception ex) { throw new SQLException("Failed to create object for "+type+": "+ex); @@ -999,14 +965,17 @@ public class Connection implements java.sql.Connection // If so, then call it's fetch method. if(x instanceof Serialize) return ((Serialize)x).store(o); + + // Thow an exception because the type is unknown + throw new SQLException("The object could not be stored. Check that any tables required have already been created in the database."); + } catch(SQLException sx) { + // rethrow the exception. Done because we capture any others next + sx.fillInStackTrace(); throw sx; } catch(Exception ex) { throw new SQLException("Failed to store object: "+ex); } - - // should never be reached - return 0; } /** @@ -1045,10 +1014,12 @@ public class Connection implements java.sql.Connection private static final String defaultObjectTypes[][] = { {"box", "postgresql.geometric.PGbox"}, {"circle", "postgresql.geometric.PGcircle"}, + {"line", "postgresql.geometric.PGline"}, {"lseg", "postgresql.geometric.PGlseg"}, {"path", "postgresql.geometric.PGpath"}, {"point", "postgresql.geometric.PGpoint"}, - {"polygon", "postgresql.geometric.PGpolygon"} + {"polygon", "postgresql.geometric.PGpolygon"}, + {"money", "postgresql.util.PGmoney"} }; // This initialises the objectTypes hashtable diff --git a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java index bfcfc6f7cf6..da1cb52962d 100644 --- a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java +++ b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java @@ -37,6 +37,9 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData static final int iInt4Oid = 23; // OID for int4 static final int VARHDRSZ = 4; // length for int4 + // This is a default value for remarks + private static final byte defaultRemarks[]="no remarks".getBytes(); + public DatabaseMetaData(Connection conn) { this.connection = conn; @@ -170,7 +173,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData */ public String getDatabaseProductVersion() throws SQLException { - return ("6.3"); + return ("6.4"); } /** @@ -1473,39 +1476,37 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData Field f[] = new Field[8]; ResultSet r; // ResultSet for the SQL query that we need to do Vector v = new Vector(); // The new ResultSet tuple stuff - String remarks = new String("no remarks"); - f[0] = new Field(connection, new String("PROCEDURE_CAT"), iVarcharOid, 32); - f[1] = new Field(connection, new String("PROCEDURE_SCHEM"), iVarcharOid, 32); - f[2] = new Field(connection, new String("PROCEDURE_NAME"), iVarcharOid, 32); - f[3] = null; - f[4] = null; - f[5] = null; - f[6] = new Field(connection, new String("REMARKS"), iVarcharOid, 8192); - f[7] = new Field(connection, new String("PROCEDURE_TYPE"), iInt2Oid, 2); - r = connection.ExecSQL("select proname, proretset from pg_proc order by proname"); - if (r.getColumnCount() != 2 || r.getTupleCount() <= 1) - throw new SQLException("Unexpected return from query for procedure list"); + byte remarks[] = defaultRemarks; + + f[0] = new Field(connection, "PROCEDURE_CAT", iVarcharOid, 32); + f[1] = new Field(connection, "PROCEDURE_SCHEM", iVarcharOid, 32); + f[2] = new Field(connection, "PROCEDURE_NAME", iVarcharOid, 32); + f[3] = f[4] = f[5] = null; // reserved, must be null for now + f[6] = new Field(connection, "REMARKS", iVarcharOid, 8192); + f[7] = new Field(connection, "PROCEDURE_TYPE", iInt2Oid, 2); + + // If the pattern is null, then set it to the default + if(procedureNamePattern==null) + procedureNamePattern="%"; + + r = connection.ExecSQL("select proname, proretset from pg_proc where proname like '"+procedureNamePattern.toLowerCase()+"' order by proname"); + while (r.next()) { byte[][] tuple = new byte[8][0]; - String name = r.getString(1); - remarks = new String("no remarks"); - boolean retset = r.getBoolean(2); - tuple[0] = null; // Catalog name tuple[1] = null; // Schema name - tuple[2] = name.getBytes(); // Procedure name - tuple[3] = null; // Reserved - tuple[4] = null; // Reserved - tuple[5] = null; // Reserved - tuple[6] = remarks.getBytes(); // Remarks - tuple[7] = new byte[1]; - if (retset) - tuple[7][0] = (byte)java.sql.DatabaseMetaData.procedureReturnsResult; + tuple[2] = r.getBytes(1); // Procedure name + tuple[3] = tuple[4] = tuple[5] = null; // Reserved + tuple[6] = remarks; // Remarks + + if (r.getBoolean(2)) + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureReturnsResult).getBytes(); else - tuple[7][0] = (byte)java.sql.DatabaseMetaData.procedureNoResult; + tuple[7] = Integer.toString(java.sql.DatabaseMetaData.procedureNoResult).getBytes(); + v.addElement(tuple); } return new ResultSet(connection, f, v, "OK", 1); @@ -1559,6 +1560,12 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData // Implementation note: This is required for Borland's JBuilder to work public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { + if(procedureNamePattern==null) + procedureNamePattern="%"; + + if(columnNamePattern==null) + columnNamePattern="%"; + // for now, this returns an empty result set. Field f[] = new Field[13]; ResultSet r; // ResultSet for the SQL query that we need to do @@ -1578,6 +1585,8 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[11] = new Field(connection, new String("NULLABLE"), iInt2Oid, 2); f[12] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); + // add query loop here + return new ResultSet(connection, f, v, "OK", 1); } @@ -1612,7 +1621,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * @param types a list of table types to include; null returns * all types * @return each row is a table description - * @exception SQLException if a database-access error occurs. + * @exception SQLException if a database-access error occurs. */ public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException { @@ -1620,6 +1629,9 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData if(types==null) types = defaultTableTypes; + if(tableNamePattern==null) + tableNamePattern="%"; + // the field descriptors for the new ResultSet Field f[] = new Field[5]; ResultSet r; // ResultSet for the SQL query that we need to do @@ -1632,7 +1644,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[4] = new Field(connection, new String("REMARKS"), iVarcharOid, 32); // Now form the query - StringBuffer sql = new StringBuffer("select relname,oid from pg_class where "); + StringBuffer sql = new StringBuffer("select relname,oid from pg_class where ("); boolean notFirst=false; for(int i=0;i<types.length;i++) { if(notFirst) @@ -1644,32 +1656,35 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData } } + // Added by Stefan Andreasen <stefan@linux.kapow.dk> + // Now take the pattern into account + sql.append(") and relname like '"); + sql.append(tableNamePattern.toLowerCase()); + sql.append("'"); + // Now run the query r = connection.ExecSQL(sql.toString()); - if (r.getColumnCount() != 2) - throw new SQLException("Unexpected return from query for table list"); + byte remarks[]; while (r.next()) { byte[][] tuple = new byte[5][0]; - String name = r.getString(1); - String remarks = new String("no remarks"); - // Fetch the description for the table (if any) ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(2)); if(dr.getTupleCount()==1) { dr.next(); - remarks=dr.getString(1); - } + remarks = dr.getBytes(1); + } else + remarks = defaultRemarks; dr.close(); - tuple[0] = null; // Catalog name - tuple[1] = null; // Schema name - tuple[2] = name.getBytes(); // Table name - tuple[3] = null; // Table type - tuple[4] = remarks.getBytes(); // Remarks + tuple[0] = null; // Catalog name + tuple[1] = null; // Schema name + tuple[2] = r.getBytes(1); // Table name + tuple[3] = null; // Table type + tuple[4] = remarks; // Remarks v.addElement(tuple); } r.close(); @@ -1848,28 +1863,34 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[16] = new Field(connection, new String("ORDINAL_POSITION"), iInt4Oid,4); f[17] = new Field(connection, new String("IS_NULLABLE"), iVarcharOid, 32); + // Added by Stefan Andreasen <stefan@linux.kapow.dk> + // If the pattern are null then set them to % + if (tableNamePattern == null) tableNamePattern="%"; + if (columnNamePattern == null) columnNamePattern="%"; + // Now form the query - r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern+"' and a.attname like '"+columnNamePattern+"' and a.attnum>0 order by c.relname,a.attnum"); + // Modified by Stefan Andreasen <stefan@linux.kapow.dk> + r = connection.ExecSQL("select a.oid,c.relname,a.attname,a.atttypid,a.attnum,a.attnotnull,a.attlen,a.atttypmod from pg_class c, pg_attribute a where a.attrelid=c.oid and c.relname like '"+tableNamePattern.toLowerCase()+"' and a.attname like '"+columnNamePattern.toLowerCase()+"' and a.attnum>0 order by c.relname,a.attnum"); + + byte remarks[]; while(r.next()) { byte[][] tuple = new byte[18][0]; - String name = r.getString(1); - String remarks = new String("no remarks"); - String columnSize; - // Fetch the description for the table (if any) ResultSet dr = connection.ExecSQL("select description from pg_description where objoid="+r.getInt(1)); if(dr.getTupleCount()==1) { dr.next(); - remarks=dr.getString(1); - } + tuple[11] = dr.getBytes(1); + } else + tuple[11] = defaultRemarks; + dr.close(); tuple[0] = "".getBytes(); // Catalog name tuple[1] = "".getBytes(); // Schema name - tuple[2] = r.getString(2).getBytes(); // Table name - tuple[3] = r.getString(3).getBytes(); // Column name + tuple[2] = r.getBytes(2); // Table name + tuple[3] = r.getBytes(3); // Column name dr = connection.ExecSQL("select typname from pg_type where oid = "+r.getString(4)); dr.next(); @@ -1877,16 +1898,16 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData dr.close(); tuple[4] = Integer.toString(Field.getSQLType(typname)).getBytes(); // Data type tuple[5] = typname.getBytes(); // Type name - + + // 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); - columnSize = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0); + tuple[6] = Integer.toString(atttypmod != -1 ? atttypmod - VARHDRSZ : 0).getBytes(); } else - columnSize = r.getString(7); - tuple[6] = columnSize.getBytes(); // Column size + tuple[6] = r.getBytes(7); tuple[7] = null; // Buffer length @@ -1894,8 +1915,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData tuple[9] = "10".getBytes(); // Num Prec Radix - assume decimal // tuple[10] is below - - tuple[11] = remarks.getBytes(); // Remarks + // tuple[11] is above tuple[12] = null; // column default @@ -1904,7 +1924,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData tuple[15] = tuple[6]; // char octet length - tuple[16] = r.getString(5).getBytes(); // ordinal position + 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 @@ -1948,6 +1968,14 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData Field f[] = new Field[8]; Vector v = new Vector(); + if(table==null) + table="%"; + + if(columnNamePattern==null) + columnNamePattern="%"; + else + columnNamePattern=columnNamePattern.toLowerCase(); + f[0] = new Field(connection,new String("TABLE_CAT"),iVarcharOid,32); f[1] = new Field(connection,new String("TABLE_SCHEM"),iVarcharOid,32); f[2] = new Field(connection,new String("TABLE_NAME"),iVarcharOid,32); @@ -1958,11 +1986,13 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[7] = new Field(connection,new String("IS_GRANTABLE"),iVarcharOid,32); // This is taken direct from the psql source - ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner ORDER BY relname"); + ResultSet r = connection.ExecSQL("SELECT relname, relacl FROM pg_class, pg_user WHERE ( relkind = 'r' OR relkind = 'i') and relname !~ '^pg_' and relname !~ '^xin[vx][0-9]+' and usesysid = relowner and relname like '"+table.toLowerCase()+"' ORDER BY relname"); while(r.next()) { byte[][] tuple = new byte[8][0]; tuple[0] = tuple[1]= "".getBytes(); DriverManager.println("relname=\""+r.getString(1)+"\" relacl=\""+r.getString(2)+"\""); + + // For now, don't add to the result as relacl needs to be processed. //v.addElement(tuple); } @@ -2122,7 +2152,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData "'1' as KEY_SEQ,"+ // -- fake it as a String for now "t.typname as PK_NAME " + " FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a, pg_type t " + - " WHERE relkind = 'r' " + // -- not indices + " WHERE bc.relkind = 'r' " + // -- not indices " and bc.relname ~ '"+table+"'" + " and i.indrelid = bc.oid" + " and i.indexrelid = ic.oid" + @@ -2379,22 +2409,30 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[16] = new Field(connection, new String("SQL_DATETIME_SUB"), iInt4Oid, 4); f[17] = new Field(connection, new String("NUM_PREC_RADIX"), iInt4Oid, 4); + // cache some results, this will keep memory useage down, and speed + // things up a little. + byte b9[] = "9".getBytes(); + byte b10[] = "10".getBytes(); + byte bf[] = "f".getBytes(); + byte bnn[] = Integer.toString(typeNoNulls).getBytes(); + byte bts[] = Integer.toString(typeSearchable).getBytes(); + while(rs.next()) { byte[][] tuple = new byte[18][]; String typname=rs.getString(1); tuple[0] = typname.getBytes(); tuple[1] = Integer.toString(Field.getSQLType(typname)).getBytes(); - tuple[2] = "9".getBytes(); // for now - tuple[6] = Integer.toString(typeNoNulls).getBytes(); // for now - tuple[7] = "f".getBytes(); // false for now - not case sensitive - tuple[8] = Integer.toString(typeSearchable).getBytes(); - tuple[9] = "f".getBytes(); // false for now - it's signed - tuple[10] = "f".getBytes(); // false for now - must handle money - tuple[11] = "f".getBytes(); // false for now - handle autoincrement + tuple[2] = b9; // for now + tuple[6] = bnn; // for now + tuple[7] = bf; // false for now - not case sensitive + tuple[8] = bts; + tuple[9] = bf; // false for now - it's signed + tuple[10] = bf; // false for now - must handle money + tuple[11] = bf; // false for now - handle autoincrement // 12 - LOCAL_TYPE_NAME is null // 13 & 14 ? // 15 & 16 are unused so we return null - tuple[17] = "10".getBytes(); // everything is base 10 + tuple[17] = b10; // everything is base 10 v.addElement(tuple); } rs.close(); @@ -2431,7 +2469,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData * within index; zero when TYPE is tableIndexStatistic * <LI><B>COLUMN_NAME</B> String => column name; null when TYPE is * tableIndexStatistic - * <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending, + * <LI><B>ASC_OR_DESC</B> String => column sort sequence, "A" => ascending * "D" => descending, may be null if sort sequence is not supported; * null when TYPE is tableIndexStatistic * <LI><B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then diff --git a/src/interfaces/jdbc/postgresql/Driver.java b/src/interfaces/jdbc/postgresql/Driver.java index c8ef8b9a15a..90563d54607 100644 --- a/src/interfaces/jdbc/postgresql/Driver.java +++ b/src/interfaces/jdbc/postgresql/Driver.java @@ -27,7 +27,7 @@ public class Driver implements java.sql.Driver // These should be in sync with the backend that the driver was // distributed with static final int MAJORVERSION = 6; - static final int MINORVERSION = 3; + static final int MINORVERSION = 4; static { diff --git a/src/interfaces/jdbc/postgresql/PG_Stream.java b/src/interfaces/jdbc/postgresql/PG_Stream.java index f786b507d1b..9002ccda7bd 100644 --- a/src/interfaces/jdbc/postgresql/PG_Stream.java +++ b/src/interfaces/jdbc/postgresql/PG_Stream.java @@ -20,7 +20,16 @@ public class PG_Stream { private Socket connection; private InputStream pg_input; - private OutputStream pg_output; + private BufferedOutputStream pg_output; + + // This is the error message returned when an EOF occurs + private static final String EOF_MSG = "The backend has broken the connection. Possibly the action you have attempted has caused it to close."; + + // This is the error message returned when an IOException occurs + private static final String IOE_MSG = "IOError while reading from backend: "; + + // This is the error message returned when flushing the stream. + private static final String FLUSH_MSG = "Error flushing output: "; /** * Constructor: Connect to the PostgreSQL back end and return @@ -33,8 +42,13 @@ public class PG_Stream public PG_Stream(String host, int port) throws IOException { connection = new Socket(host, port); + + // Submitted by Jason Venner <jason@idiom.com> adds a 10x speed + // improvement on FreeBSD machines (caused by a bug in their TCP Stack) + connection.setTcpNoDelay(true); + pg_input = connection.getInputStream(); - pg_output = connection.getOutputStream(); + pg_output = new BufferedOutputStream(connection.getOutputStream()); } /** @@ -45,7 +59,6 @@ public class PG_Stream */ public void SendChar(int val) throws IOException { - //pg_output.write(val); byte b[] = new byte[1]; b[0] = (byte)val; pg_output.write(b); @@ -165,9 +178,9 @@ public class PG_Stream try { c = pg_input.read(); - if (c < 0) throw new IOException("EOF"); + if (c < 0) throw new IOException(EOF_MSG); } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); + throw new SQLException(IOE_MSG + e.toString()); } return c; } @@ -190,11 +203,11 @@ public class PG_Stream int b = pg_input.read(); if (b < 0) - throw new IOException("EOF"); + throw new IOException(EOF_MSG); n = n | (b << (8 * i)) ; } } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); + throw new SQLException(IOE_MSG + e.toString()); } return n; } @@ -217,11 +230,11 @@ public class PG_Stream int b = pg_input.read(); if (b < 0) - throw new IOException("EOF"); + throw new IOException(EOF_MSG); n = b | (n << 8); } } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); + throw new SQLException(IOE_MSG + e.toString()); } return n; } @@ -246,7 +259,7 @@ public class PG_Stream { int c = pg_input.read(); if (c < 0) - throw new IOException("EOF"); + throw new IOException(EOF_MSG); else if (c == 0) break; else @@ -255,7 +268,7 @@ public class PG_Stream if (s >= maxsiz) throw new IOException("Too Much Data"); } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); + throw new SQLException(IOE_MSG + e.toString()); } String v = new String(rst, 0, s); return v; @@ -314,21 +327,8 @@ public class PG_Stream private byte[] Receive(int siz) throws SQLException { byte[] answer = new byte[siz]; - int s = 0; - - try - { - while (s < siz) - { - int w = pg_input.read(answer, s, siz - s); - if (w < 0) - throw new IOException("EOF"); - s += w; - } - } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); - } - return answer; + Receive(answer,0,siz); + return answer; } /** @@ -349,11 +349,11 @@ public class PG_Stream { int w = pg_input.read(b, off+s, siz - s); if (w < 0) - throw new IOException("EOF"); + throw new IOException(EOF_MSG); s += w; } } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); + throw new SQLException(IOE_MSG + e.toString()); } } @@ -367,7 +367,7 @@ public class PG_Stream try { pg_output.flush(); } catch (IOException e) { - throw new SQLException("Error flushing output: " + e.toString()); + throw new SQLException(FLUSH_MSG + e.toString()); } } diff --git a/src/interfaces/jdbc/postgresql/PreparedStatement.java b/src/interfaces/jdbc/postgresql/PreparedStatement.java index 86121c57347..85ee2f67d96 100644 --- a/src/interfaces/jdbc/postgresql/PreparedStatement.java +++ b/src/interfaces/jdbc/postgresql/PreparedStatement.java @@ -308,24 +308,21 @@ public class PreparedStatement extends Statement implements java.sql.PreparedSta public void setDate(int parameterIndex, java.sql.Date x) throws SQLException { SimpleDateFormat df = new SimpleDateFormat("''"+connection.getDateStyle()+"''"); - - // Ideally the following should work: + + set(parameterIndex, df.format(x)); + + // The above is how the date should be handled. // - // set(parameterIndex, df.format(x)); + // However, in JDK's prior to 1.1.6 (confirmed with the + // Linux jdk1.1.3 and the Win95 JRE1.1.5), SimpleDateFormat seems + // to format a date to the previous day. So the fix is to add a day + // before formatting. // - // however, SimpleDateFormat seems to format a date to the previous - // day. So a fix (for now) is to add a day before formatting. - // This needs more people to confirm this is really happening, or - // possibly for us to implement our own formatting code. + // PS: 86400000 is one day // - // I've tested this with the Linux jdk1.1.3 and the Win95 JRE1.1.5 - // - set(parameterIndex, df.format(new java.util.Date(x.getTime()+DAY))); + //set(parameterIndex, df.format(new java.util.Date(x.getTime()+86400000))); } - // This equates to 1 day - private static final int DAY = 86400000; - /** * Set a parameter to a java.sql.Time value. The driver converts * this to a SQL TIME value when it sends it to the database. diff --git a/src/interfaces/jdbc/postgresql/ResultSet.java b/src/interfaces/jdbc/postgresql/ResultSet.java index ba98bd97c91..12dcc476af4 100644 --- a/src/interfaces/jdbc/postgresql/ResultSet.java +++ b/src/interfaces/jdbc/postgresql/ResultSet.java @@ -714,6 +714,12 @@ public class ResultSet implements java.sql.ResultSet throw new SQLException("Column index out of range"); field = fields[columnIndex - 1]; + // some fields can be null, mainly from those returned by MetaData methods + if(field==null) { + wasNullFlag=true; + return null; + } + switch (field.getSQLType()) { case Types.BIT: diff --git a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java index 7a01a136ecd..33f2d78f1d3 100644 --- a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java +++ b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java @@ -121,11 +121,7 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData { String type_name = getField(column).getTypeName(); - if (type_name.equals("cash")) - return true; - if (type_name.equals("money")) - return true; - return false; + return type_name.equals("cash") || type_name.equals("money"); } /** @@ -214,11 +210,14 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData * * @param column the first column is 1, the second is 2, etc. * @return the column name - * @exception SQLException if a databvase access error occurs + * @exception SQLException if a database access error occurs */ public String getColumnName(int column) throws SQLException { - return getField(column).name; + Field f = getField(column); + if(f!=null) + return f.name; + return "field"+column; } /** @@ -233,13 +232,7 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData */ public String getSchemaName(int column) throws SQLException { - String table_name = getTableName(column); - - // If the table name is invalid, so are we. - if (table_name.equals("")) - return ""; - return ""; // Ok, so I don't know how to - // do this as yet. + return ""; } /** @@ -328,12 +321,7 @@ public class ResultSetMetaData implements java.sql.ResultSetMetaData */ public String getCatalogName(int column) throws SQLException { - String table_name = getTableName(column); - - if (table_name.equals("")) - return ""; - return ""; // As with getSchemaName(), this - // is just the start of it. + return ""; } /** |