diff options
Diffstat (limited to 'src/interfaces/jdbc/org/postgresql/Connection.java')
-rw-r--r-- | src/interfaces/jdbc/org/postgresql/Connection.java | 185 |
1 files changed, 104 insertions, 81 deletions
diff --git a/src/interfaces/jdbc/org/postgresql/Connection.java b/src/interfaces/jdbc/org/postgresql/Connection.java index b828a90b6dc..3ca464d3adb 100644 --- a/src/interfaces/jdbc/org/postgresql/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/Connection.java @@ -10,7 +10,7 @@ import org.postgresql.largeobject.*; import org.postgresql.util.*; /** - * $Id: Connection.java,v 1.11 2000/12/22 03:08:52 momjian Exp $ + * $Id: Connection.java,v 1.12 2001/01/18 14:50:14 peter Exp $ * * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or * JDBC2 versions of the Connection class. @@ -20,10 +20,10 @@ public abstract class Connection { // This is the network stream associated with this connection public PG_Stream pg_stream; - + // This is set by org.postgresql.Statement.setMaxRows() public int maxrows = 0; // maximum no. of rows; 0 = unlimited - + private String PG_HOST; private int PG_PORT; private String PG_USER; @@ -38,17 +38,17 @@ public abstract class Connection * used. */ private String encoding; - + public boolean CONNECTION_OK = true; public boolean CONNECTION_BAD = false; - + public boolean autoCommit = true; public boolean readOnly = false; - + public Driver this_driver; private String this_url; private String cursor = null; // The positioned update cursor name - + // 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 @@ -59,41 +59,41 @@ public abstract class Connection private static final int SM_OPTIONS = 64; private static final int SM_UNUSED = 64; private static final int SM_TTY = 64; - + private static final int AUTH_REQ_OK = 0; private static final int AUTH_REQ_KRB4 = 1; private static final int AUTH_REQ_KRB5 = 2; private static final int AUTH_REQ_PASSWORD = 3; private static final int AUTH_REQ_CRYPT = 4; - + // New for 6.3, salt value for crypt authorisation private String salt; - + // 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. public Hashtable fieldCache = new Hashtable(); - + // 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 receive_sbuf should be used by the different methods - // that call pg_stream.ReceiveString() in this Connection, so - // so we avoid uneccesary new allocations. + // that call pg_stream.ReceiveString() in this Connection, so + // so we avoid uneccesary new allocations. byte receive_sbuf[] = new byte[8192]; - + /** * This is called by Class.forName() from within org.postgresql.Driver */ public Connection() { } - + /** * This method actually opens the connection. It is called by Driver. * @@ -115,7 +115,7 @@ public abstract class Connection throw new PSQLException("postgresql.con.user"); if(info.getProperty("password")==null) throw new PSQLException("postgresql.con.pass"); - + this_driver = d; this_url = url; PG_DATABASE = database; @@ -137,7 +137,7 @@ public abstract class Connection } catch (IOException e) { throw new PSQLException ("postgresql.con.failed",e); } - + // Now we need to construct and send a startup packet try { @@ -146,13 +146,13 @@ public abstract class Connection 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); - + // 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 int areq = -1; // must have a value here @@ -169,11 +169,11 @@ public abstract class Connection // throw new SQLException(pg_stream.ReceiveString (receive_sbuf, 4096, getEncoding())); - + case 'R': // Get the type of request areq = pg_stream.ReceiveIntegerR(4); - + // Get the password salt if there is one if(areq == AUTH_REQ_CRYPT) { byte[] rst = new byte[2]; @@ -182,21 +182,21 @@ public abstract class Connection salt = new String(rst,0,2); DriverManager.println("Salt="+salt); } - + // now send the auth packet switch(areq) { case AUTH_REQ_OK: break; - + case AUTH_REQ_KRB4: DriverManager.println("postgresql: KRB4"); throw new PSQLException("postgresql.con.kerb4"); - + case AUTH_REQ_KRB5: DriverManager.println("postgresql: KRB5"); throw new PSQLException("postgresql.con.kerb5"); - + case AUTH_REQ_PASSWORD: DriverManager.println("postgresql: PASSWORD"); pg_stream.SendInteger(5+PG_PASSWORD.length(),4); @@ -204,7 +204,7 @@ public abstract class Connection pg_stream.SendInteger(0,1); pg_stream.flush(); break; - + case AUTH_REQ_CRYPT: DriverManager.println("postgresql: CRYPT"); String crypted = UnixCrypt.crypt(salt,PG_PASSWORD); @@ -213,21 +213,21 @@ public abstract class Connection pg_stream.SendInteger(0,1); pg_stream.flush(); break; - + default: throw new PSQLException("postgresql.con.auth",new Integer(areq)); } break; - + default: throw new PSQLException("postgresql.con.authfail"); } } while(areq != AUTH_REQ_OK); - + } catch (IOException e) { 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(); @@ -266,7 +266,7 @@ public abstract class Connection // We also ask the DB for certain properties (i.e. DatabaseEncoding at this time) // firstWarning = null; - + java.sql.ResultSet initrset = ExecSQL("set datestyle to 'ISO'; select getdatabaseencoding()"); String dbEncoding = null; @@ -341,19 +341,19 @@ public abstract class Connection encoding = null; } } - + // Initialise object handling initObjectTypes(); - + // Mark the connection as ok, and cleanup firstWarning = null; PG_STATUS = CONNECTION_OK; } - + // These methods used to be in the main Connection implementation. As they // are common to all implementations (JDBC1 or 2), they are placed here. // This should make it easy to maintain the two specifications. - + /** * This adds a warning to the warning chain. * @param msg message to add @@ -361,15 +361,15 @@ public abstract class Connection public void addWarning(String msg) { DriverManager.println(msg); - + // Add the warning to the chain if(firstWarning!=null) firstWarning.setNextWarning(new SQLWarning(msg)); else firstWarning = new SQLWarning(msg); - + // Now check for some specific messages - + // This is obsolete in 6.5, but I've left it in here so if we need to use this // technique again, we'll know where to place it. // @@ -377,13 +377,13 @@ public abstract class Connection //if(msg.startsWith("NOTICE:") && msg.indexOf("DateStyle")>0) { //// 13 is the length off "DateStyle is " //msg = msg.substring(msg.indexOf("DateStyle is ")+13); - // + // //for(int i=0;i<dateStyles.length;i+=2) //if(msg.startsWith(dateStyles[i])) //currentDateStyle=i+1; // this is the index of the format //} } - + /** * Send a query to the backend. Returns one of the ResultSet * objects. @@ -404,8 +404,10 @@ public abstract class Connection // This will let the driver reuse byte arrays that has already // been allocated instead of allocating new ones in order // to gain performance improvements. - pg_stream.deallocate(); - + // PM 17/01/01: Commented out due to race bug. See comments in + // PG_Stream + //pg_stream.deallocate(); + Field[] fields = null; Vector tuples = new Vector(); byte[] buf = null; @@ -415,7 +417,7 @@ public abstract class Connection int update_count = 1; int insert_oid = 0; SQLException final_error = null; - + // Commented out as the backend can now handle queries // larger than 8K. Peter June 6 2000 //if (sql.length() > 8192) @@ -441,13 +443,13 @@ public abstract class Connection } catch (IOException e) { throw new PSQLException("postgresql.con.ioerror",e); } - + while (!hfr || fqp > 0) { Object tup=null; // holds rows as they are recieved - + int c = pg_stream.ReceiveChar(); - + switch (c) { case 'A': // Asynchronous Notify @@ -464,7 +466,7 @@ public abstract class Connection break; case 'C': // Command Status recv_status = pg_stream.ReceiveString(receive_sbuf,8192,getEncoding()); - + // Now handle the update count correctly. if(recv_status.startsWith("INSERT") || recv_status.startsWith("UPDATE") || recv_status.startsWith("DELETE")) { try { @@ -511,7 +513,7 @@ public abstract class Connection break; case 'I': // Empty Query int t = pg_stream.ReceiveChar(); - + if (t != 0) throw new PSQLException("postgresql.con.garbled"); if (fqp > 0) @@ -538,7 +540,7 @@ public abstract class Connection } if (final_error != null) throw final_error; - + return getResultSet(this, fields, tuples, recv_status, update_count, insert_oid); } } @@ -553,7 +555,7 @@ public abstract class Connection { int nf = pg_stream.ReceiveIntegerR(2), i; Field[] fields = new Field[nf]; - + for (i = 0 ; i < nf ; ++i) { String typname = pg_stream.ReceiveString(receive_sbuf,8192,getEncoding()); @@ -564,7 +566,7 @@ public abstract class Connection } return fields; } - + /** * In SQL, a result table can be retrieved through a cursor that * is named. The current row of a result can be updated or deleted @@ -582,7 +584,7 @@ public abstract class Connection { this.cursor = cursor; } - + /** * getCursorName gets the cursor name. * @@ -593,7 +595,7 @@ public abstract class Connection { return cursor; } - + /** * We are required to bring back certain information by * the DatabaseMetaData class. These functions do that. @@ -607,7 +609,7 @@ public abstract class Connection { return this_url; } - + /** * Method getUserName() brings back the User Name (again, we * saved it) @@ -622,13 +624,13 @@ public abstract class Connection /** * Get the character encoding to use for this connection. - * @return the encoding to use, or <b>null</b> for the + * @return the encoding to use, or <b>null</b> for the * default encoding. */ public String getEncoding() throws SQLException { return encoding; } - + /** * This returns the Fastpath API for the current connection. * @@ -657,10 +659,10 @@ public abstract class Connection fastpath = new Fastpath(this,pg_stream); return fastpath; } - + // This holds a reference to the Fastpath API if already open private Fastpath fastpath = null; - + /** * This returns the LargeObject API for the current connection. * @@ -686,10 +688,10 @@ public abstract class Connection largeobject = new LargeObjectManager(this); return largeobject; } - + // This holds a reference to the LargeObject API if already open private LargeObjectManager largeobject = null; - + /** * This method is used internally to return an object based around * org.postgresql's more unique data types. @@ -713,7 +715,7 @@ public abstract class Connection { try { Object o = objectTypes.get(type); - + // If o is null, then the type is unknown, so check to see if type // is an actual table name. If it does, see if a Class is known that // can handle it @@ -722,7 +724,7 @@ public abstract class Connection objectTypes.put(type,ser); return ser.fetch(Integer.parseInt(value)); } - + // If o is not null, and it is a String, then its a class name that // extends PGobject. // @@ -748,11 +750,11 @@ public abstract class Connection } catch(Exception ex) { throw new PSQLException("postgresql.con.creobj",type,ex); } - + // should never be reached return null; } - + /** * This stores an object into the database. * @param o Object to store @@ -765,7 +767,7 @@ public abstract class Connection try { String type = o.getClass().getName(); Object x = objectTypes.get(type); - + // If x is null, then the type is unknown, so check to see if type // is an actual table name. If it does, see if a Class is known that // can handle it @@ -774,15 +776,15 @@ public abstract class Connection objectTypes.put(type,ser); return ser.store(o); } - + // If it's an object, it should be an instance of our Serialize class // 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 PSQLException("postgresql.con.strobj"); - + } catch(SQLException sx) { // rethrow the exception. Done because we capture any others next sx.fillInStackTrace(); @@ -791,7 +793,7 @@ public abstract class Connection throw new PSQLException("postgresql.con.strobjex",ex); } } - + /** * This allows client code to add a handler for one of org.postgresql's * more unique data types. @@ -816,10 +818,10 @@ public abstract class Connection { objectTypes.put(type,name); } - + // This holds the available types private Hashtable objectTypes = new Hashtable(); - + // This array contains the types that are supported as standard. // // The first entry is the types name on the database, the second @@ -835,25 +837,25 @@ public abstract class Connection {"polygon", "org.postgresql.geometric.PGpolygon"}, {"money", "org.postgresql.util.PGmoney"} }; - + // This initialises the objectTypes hashtable private void initObjectTypes() { for(int i=0;i<defaultObjectTypes.length;i++) objectTypes.put(defaultObjectTypes[i][0],defaultObjectTypes[i][1]); } - + // These are required by other common classes public abstract java.sql.Statement createStatement() throws SQLException; - + /** * This returns a resultset. It must be overridden, so that the correct * version (from jdbc1 or jdbc2) are returned. */ protected abstract java.sql.ResultSet getResultSet(org.postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount,int insertOID) throws SQLException; - + public abstract void close() throws SQLException; - + /** * Overides finalize(). If called, it closes the connection. * @@ -866,12 +868,33 @@ public abstract class Connection { close(); } - + /** * This is an attempt to implement SQL Escape clauses */ public String EscapeSQL(String sql) { - return sql; + //if (DEBUG) { System.out.println ("parseSQLEscapes called"); } + + // If we find a "{d", assume we have a date escape. + // + // Since the date escape syntax is very close to the + // native Postgres date format, we just remove the escape + // delimiters. + // + // This implementation could use some optimization, but it has + // worked in practice for two years of solid use. + int index = sql.indexOf("{d"); + while (index != -1) { + //System.out.println ("escape found at index: " + index); + StringBuffer buf = new StringBuffer(sql); + buf.setCharAt(index, ' '); + buf.setCharAt(index + 1, ' '); + buf.setCharAt(sql.indexOf('}', index), ' '); + sql = new String(buf); + index = sql.indexOf("{d"); + } + //System.out.println ("modified SQL: " + sql); + return sql; } - + } |