diff options
Diffstat (limited to 'src/interfaces/jdbc/postgresql/Connection.java')
-rw-r--r-- | src/interfaces/jdbc/postgresql/Connection.java | 1282 |
1 files changed, 482 insertions, 800 deletions
diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index be15b38abe..df354776f7 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -1,41 +1,28 @@ package postgresql; import java.io.*; -import java.lang.*; -import java.lang.reflect.*; import java.net.*; -import java.util.*; import java.sql.*; +import java.util.*; +import postgresql.Field; import postgresql.fastpath.*; import postgresql.largeobject.*; import postgresql.util.*; /** - * A Connection represents a session with a specific database. Within the - * context of a Connection, SQL statements are executed and results are - * returned. + * $Id: Connection.java,v 1.14 1999/01/17 04:51:50 momjian Exp $ * - * <P>A Connection's database is able to provide information describing - * its tables, its supported SQL grammar, its stored procedures, the - * capabilities of this connection, etc. This information is obtained - * with the getMetaData method. + * This abstract class is used by postgresql.Driver to open either the JDBC1 or + * JDBC2 versions of the Connection class. * - * <p><B>Note:</B> By default, the Connection automatically commits changes - * after executing each statement. If auto-commit has been disabled, an - * explicit commit must be done or database changes will not be saved. - * - * @see java.sql.Connection */ -public class Connection implements java.sql.Connection +public abstract class Connection { // This is the network stream associated with this connection - protected PG_Stream pg_stream; + public PG_Stream pg_stream; // 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; + public int maxrows = 0; // maximum no. of rows; 0 = unlimited private String PG_HOST; private int PG_PORT; @@ -47,10 +34,10 @@ public class Connection implements java.sql.Connection public boolean CONNECTION_OK = true; public boolean CONNECTION_BAD = false; - private boolean autoCommit = true; - private boolean readOnly = false; + public boolean autoCommit = true; + public boolean readOnly = false; - protected Driver this_driver; + public Driver this_driver; private String this_url; private String cursor = null; // The positioned update cursor name @@ -78,12 +65,12 @@ public class Connection implements java.sql.Connection // 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(); + public Hashtable fieldCache = new Hashtable(); /** * This is the current date style of the backend */ - protected int currentDateStyle; + public int currentDateStyle; /** * This defines the formats for dates, according to the various date styles. @@ -113,29 +100,29 @@ public class Connection implements java.sql.Connection }; // Now handle notices as warnings, so things like "show" now work - protected SQLWarning firstWarning = null; + public SQLWarning firstWarning = null; - /** - * Connect to a PostgreSQL database back end. - * - * <p><b>Important Notice</b> - * - * <br>Although this will connect to the database, user code should open - * the connection via the DriverManager.getConnection() methods only. - * - * <br>This should only be called from the postgresql.Driver class. - * - * @param host the hostname of the database back end - * @param port the port number of the postmaster process - * @param info a Properties[] thing of the user and password - * @param database the database to connect to - * @param u the URL of the connection - * @param d the Driver instantation of the connection - * @return a valid connection profile - * @exception SQLException if a database access error occurs - */ - public Connection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException - { + /** + * This is called by Class.forName() from within postgresql.Driver + */ + public Connection() + { + } + + /** + * This method actually opens the connection. It is called by Driver. + * + * @param host the hostname of the database back end + * @param port the port number of the postmaster process + * @param info a Properties[] thing of the user and password + * @param database the database to connect to + * @param u the URL of the connection + * @param d the Driver instantation of the connection + * @return a valid connection profile + * @exception SQLException if a database access error occurs + */ + protected void openConnection(String host, int port, Properties info, String database, String url, Driver d) throws SQLException + { // 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 @@ -262,777 +249,472 @@ public class Connection implements java.sql.Connection // Also, this query replaced the NULL query issued to test the // connection. // - clearWarnings(); + firstWarning = null; ExecSQL("show datestyle"); // Initialise object handling initObjectTypes(); // Mark the connection as ok, and cleanup - clearWarnings(); + firstWarning = null; PG_STATUS = CONNECTION_OK; - } - - /** - * SQL statements without parameters are normally executed using - * Statement objects. If the same SQL statement is executed many - * times, it is more efficient to use a PreparedStatement - * - * @return a new Statement object - * @exception SQLException passed through from the constructor - */ - public java.sql.Statement createStatement() throws SQLException - { - return new Statement(this); - } - - /** - * A SQL statement with or without IN parameters can be pre-compiled - * and stored in a PreparedStatement object. This object can then - * be used to efficiently execute this statement multiple times. - * - * <B>Note:</B> This method is optimized for handling parametric - * SQL statements that benefit from precompilation if the drivers - * supports precompilation. PostgreSQL does not support precompilation. - * In this case, the statement is not sent to the database until the - * PreparedStatement is executed. This has no direct effect on users; - * however it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' IN - * parameter placeholders - * @return a new PreparedStatement object containing the pre-compiled - * statement. - * @exception SQLException if a database access error occurs. - */ - public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException - { - return new PreparedStatement(this, sql); - } - - /** - * A SQL stored procedure call statement is handled by creating a - * CallableStatement for it. The CallableStatement provides methods - * for setting up its IN and OUT parameters and methods for executing - * it. - * - * <B>Note:</B> This method is optimised for handling stored procedure - * call statements. Some drivers may send the call statement to the - * database when the prepareCall is done; others may wait until the - * CallableStatement is executed. This has no direct effect on users; - * however, it does affect which method throws certain SQLExceptions - * - * @param sql a SQL statement that may contain one or more '?' parameter - * placeholders. Typically this statement is a JDBC function call - * escape string. - * @return a new CallableStatement object containing the pre-compiled - * SQL statement - * @exception SQLException if a database access error occurs - */ - public java.sql.CallableStatement prepareCall(String sql) throws SQLException - { - throw new SQLException("Callable Statements are not supported at this time"); - // return new CallableStatement(this, sql); - } - - /** - * A driver may convert the JDBC sql grammar into its system's - * native SQL grammar prior to sending it; nativeSQL returns the - * native form of the statement that the driver would have sent. - * - * @param sql a SQL statement that may contain one or more '?' - * parameter placeholders - * @return the native form of this statement - * @exception SQLException if a database access error occurs - */ - public String nativeSQL(String sql) throws SQLException - { - return sql; - } - - /** - * If a connection is in auto-commit mode, than all its SQL - * statements will be executed and committed as individual - * transactions. Otherwise, its SQL statements are grouped - * into transactions that are terminated by either commit() - * or rollback(). By default, new connections are in auto- - * commit mode. The commit occurs when the statement completes - * or the next execute occurs, whichever comes first. In the - * case of statements returning a ResultSet, the statement - * completes when the last row of the ResultSet has been retrieved - * or the ResultSet has been closed. In advanced cases, a single - * statement may return multiple results as well as output parameter - * values. Here the commit occurs when all results and output param - * values have been retrieved. - * - * @param autoCommit - true enables auto-commit; false disables it - * @exception SQLException if a database access error occurs - */ - public void setAutoCommit(boolean autoCommit) throws SQLException - { - if (this.autoCommit == autoCommit) - return; - if (autoCommit) - ExecSQL("end"); - else - ExecSQL("begin"); - this.autoCommit = autoCommit; - } - - /** - * gets the current auto-commit state - * - * @return Current state of the auto-commit mode - * @exception SQLException (why?) - * @see setAutoCommit - */ - public boolean getAutoCommit() throws SQLException - { - return this.autoCommit; - } - - /** - * The method commit() makes all changes made since the previous - * commit/rollback permanent and releases any database locks currently - * held by the Connection. This method should only be used when - * auto-commit has been disabled. (If autoCommit == true, then we - * just return anyhow) - * - * @exception SQLException if a database access error occurs - * @see setAutoCommit - */ - public void commit() throws SQLException - { - if (autoCommit) - return; - ExecSQL("commit"); - autoCommit = true; - ExecSQL("begin"); - autoCommit = false; - } - - /** - * The method rollback() drops all changes made since the previous - * commit/rollback and releases any database locks currently held by - * the Connection. - * - * @exception SQLException if a database access error occurs - * @see commit - */ - public void rollback() throws SQLException - { - if (autoCommit) - return; - ExecSQL("rollback"); - autoCommit = true; - ExecSQL("begin"); - autoCommit = false; - } - - /** - * In some cases, it is desirable to immediately release a Connection's - * database and JDBC resources instead of waiting for them to be - * automatically released (cant think why off the top of my head) - * - * <B>Note:</B> A Connection is automatically closed when it is - * garbage collected. Certain fatal errors also result in a closed - * connection. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException - { - if (pg_stream != null) - { - try - { - pg_stream.close(); - } catch (IOException e) {} - pg_stream = null; - } - } - - /** - * Tests to see if a Connection is closed - * - * @return the status of the connection - * @exception SQLException (why?) - */ - public boolean isClosed() throws SQLException - { - return (pg_stream == null); - } - - /** - * A connection's database is able to provide information describing - * its tables, its supported SQL grammar, its stored procedures, the - * capabilities of this connection, etc. This information is made - * available through a DatabaseMetaData object. - * - * @return a DatabaseMetaData object for this connection - * @exception SQLException if a database access error occurs - */ - public java.sql.DatabaseMetaData getMetaData() throws SQLException - { - if(metadata==null) - metadata = new DatabaseMetaData(this); - return metadata; - } - - /** - * You can put a connection in read-only mode as a hunt to enable - * database optimizations - * - * <B>Note:</B> setReadOnly cannot be called while in the middle - * of a transaction - * - * @param readOnly - true enables read-only mode; false disables it - * @exception SQLException if a database access error occurs - */ - public void setReadOnly (boolean readOnly) throws SQLException - { - this.readOnly = readOnly; - } - - /** - * Tests to see if the connection is in Read Only Mode. Note that - * we cannot really put the database in read only mode, but we pretend - * we can by returning the value of the readOnly flag - * - * @return true if the connection is read only - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException - { - return readOnly; - } - - /** - * A sub-space of this Connection's database may be selected by - * setting a catalog name. If the driver does not support catalogs, - * it will silently ignore this request - * - * @exception SQLException if a database access error occurs - */ - public void setCatalog(String catalog) throws SQLException - { - // No-op - } - - /** - * Return the connections current catalog name, or null if no - * catalog name is set, or we dont support catalogs. - * - * @return the current catalog name or null - * @exception SQLException if a database access error occurs - */ - public String getCatalog() throws SQLException - { - return null; - } - - /** - * You can call this method to try to change the transaction - * isolation level using one of the TRANSACTION_* values. - * - * <B>Note:</B> setTransactionIsolation cannot be called while - * in the middle of a transaction - * - * @param level one of the TRANSACTION_* isolation values with - * the exception of TRANSACTION_NONE; some databases may - * not support other values - * @exception SQLException if a database access error occurs - * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel - */ - public void setTransactionIsolation(int level) throws SQLException - { - throw new SQLException("Transaction Isolation Levels are not implemented"); - } - - /** - * Get this Connection's current transaction isolation mode. - * - * @return the current TRANSACTION_* mode value - * @exception SQLException if a database access error occurs - */ - public int getTransactionIsolation() throws SQLException - { - return java.sql.Connection.TRANSACTION_SERIALIZABLE; - } - - /** - * The first warning reported by calls on this Connection is - * returned. - * - * <B>Note:</B> Sebsequent warnings will be changed to this - * SQLWarning - * - * @return the first SQLWarning or null - * @exception SQLException if a database access error occurs - */ - public SQLWarning getWarnings() throws SQLException - { - return firstWarning; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this connection. - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - firstWarning = null; - } - - // ********************************************************** - // END OF PUBLIC INTERFACE - // ********************************************************** - - /** - * This adds a warning to the warning chain. - * @param msg message to add - */ - 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); + // 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. - // Now check for some specific messages + /** + * This adds a warning to the warning chain. + * @param msg message to add + */ + 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 generated by the SQL "show datestyle" + 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 + } + } - // This is generated by the SQL "show datestyle" - 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 + /** + * @return the date format for the current date style of the backend + */ + public String getDateStyle() + { + return dateStyles[currentDateStyle]; } - } - - /** - * @return the date format for the current date style of the backend - */ - public String getDateStyle() - { - return dateStyles[currentDateStyle]; - } - - /** - * Send a query to the backend. Returns one of the ResultSet - * objects. - * - * <B>Note:</B> there does not seem to be any method currently - * in existance to return the update count. - * - * @param sql the SQL statement to be executed - * @return a ResultSet holding the results - * @exception SQLException if a database error occurs - */ - public ResultSet ExecSQL(String sql) throws SQLException - { - // added Oct 7 1998 to give us thread safety. - synchronized(pg_stream) { - - Field[] fields = null; - Vector tuples = new Vector(); - byte[] buf = new byte[sql.length()]; - int fqp = 0; - boolean hfr = false; - String recv_status = null, msg; - SQLException final_error = null; - if (sql.length() > 8192) - throw new SQLException("SQL Statement too long: " + sql); - try - { - pg_stream.SendChar('Q'); - 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()); - } - - while (!hfr || fqp > 0) - { - Object tup=null; // holds rows as they are recieved - - int c = pg_stream.ReceiveChar(); - - switch (c) - { - case 'A': // Asynchronous Notify - int pid = pg_stream.ReceiveInteger(4); - msg = pg_stream.ReceiveString(8192); - break; - case 'B': // Binary Data Transfer - if (fields == null) - throw new SQLException("Tuple received before MetaData"); - tup = pg_stream.ReceiveTuple(fields.length, true); - // This implements Statement.setMaxRows() - if(maxrows==0 || tuples.size()<maxrows) - tuples.addElement(tup); - break; - case 'C': // Command Status - recv_status = pg_stream.ReceiveString(8192); - if (fields != null) - hfr = true; - else + /** + * Send a query to the backend. Returns one of the ResultSet + * objects. + * + * <B>Note:</B> there does not seem to be any method currently + * in existance to return the update count. + * + * @param sql the SQL statement to be executed + * @return a ResultSet holding the results + * @exception SQLException if a database error occurs + */ + public java.sql.ResultSet ExecSQL(String sql) throws SQLException + { + // added Oct 7 1998 to give us thread safety. + synchronized(pg_stream) { + + Field[] fields = null; + Vector tuples = new Vector(); + byte[] buf = new byte[sql.length()]; + int fqp = 0; + boolean hfr = false; + String recv_status = null, msg; + SQLException final_error = null; + + if (sql.length() > 8192) + throw new SQLException("SQL Statement too long: " + sql); + try { - try - { - 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()); - } - fqp++; + pg_stream.SendChar('Q'); + 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()); } - break; - case 'D': // Text Data Transfer - if (fields == null) - throw new SQLException("Tuple received before MetaData"); - tup = pg_stream.ReceiveTuple(fields.length, false); - // This implements Statement.setMaxRows() - if(maxrows==0 || tuples.size()<maxrows) - tuples.addElement(tup); - break; - case 'E': // Error Message - msg = pg_stream.ReceiveString(4096); - final_error = new SQLException(msg); - hfr = true; - break; - case 'I': // Empty Query - int t = pg_stream.ReceiveChar(); - - if (t != 0) - throw new SQLException("Garbled Data"); - if (fqp > 0) - fqp--; - if (fqp == 0) - hfr = true; - break; - case 'N': // Error Notification - addWarning(pg_stream.ReceiveString(4096)); - break; - case 'P': // Portal Name - String pname = pg_stream.ReceiveString(8192); - break; - case 'T': // MetaData Field Description - if (fields != null) - throw new SQLException("Cannot handle multiple result groups"); - fields = ReceiveFields(); - break; - default: - throw new SQLException("Unknown Response Type: " + (char)c); + + while (!hfr || fqp > 0) + { + Object tup=null; // holds rows as they are recieved + + int c = pg_stream.ReceiveChar(); + + switch (c) + { + case 'A': // Asynchronous Notify + int pid = pg_stream.ReceiveInteger(4); + msg = pg_stream.ReceiveString(8192); + break; + case 'B': // Binary Data Transfer + if (fields == null) + throw new SQLException("Tuple received before MetaData"); + tup = pg_stream.ReceiveTuple(fields.length, true); + // This implements Statement.setMaxRows() + if(maxrows==0 || tuples.size()<maxrows) + tuples.addElement(tup); + break; + case 'C': // Command Status + recv_status = pg_stream.ReceiveString(8192); + if (fields != null) + hfr = true; + else + { + try + { + 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()); + } + fqp++; + } + break; + case 'D': // Text Data Transfer + if (fields == null) + throw new SQLException("Tuple received before MetaData"); + tup = pg_stream.ReceiveTuple(fields.length, false); + // This implements Statement.setMaxRows() + if(maxrows==0 || tuples.size()<maxrows) + tuples.addElement(tup); + break; + case 'E': // Error Message + msg = pg_stream.ReceiveString(4096); + final_error = new SQLException(msg); + hfr = true; + break; + case 'I': // Empty Query + int t = pg_stream.ReceiveChar(); + + if (t != 0) + throw new SQLException("Garbled Data"); + if (fqp > 0) + fqp--; + if (fqp == 0) + hfr = true; + break; + case 'N': // Error Notification + addWarning(pg_stream.ReceiveString(4096)); + break; + case 'P': // Portal Name + String pname = pg_stream.ReceiveString(8192); + break; + case 'T': // MetaData Field Description + if (fields != null) + throw new SQLException("Cannot handle multiple result groups"); + fields = ReceiveFields(); + break; + default: + throw new SQLException("Unknown Response Type: " + (char)c); + } + } + if (final_error != null) + throw final_error; + return getResultSet(this, fields, tuples, recv_status, 1); + //return new ResultSet(this, fields, tuples, recv_status, 1); + } + } + + /** + * Receive the field descriptions from the back end + * + * @return an array of the Field object describing the fields + * @exception SQLException if a database error occurs + */ + private Field[] ReceiveFields() throws SQLException + { + int nf = pg_stream.ReceiveIntegerR(2), i; + Field[] fields = new Field[nf]; + + for (i = 0 ; i < nf ; ++i) + { + 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); + } + 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 + * using a positioned update/delete statement that references the + * cursor name. + * + * We support one cursor per connection. + * + * setCursorName sets the cursor name. + * + * @param cursor the cursor name + * @exception SQLException if a database access error occurs + */ + public void setCursorName(String cursor) throws SQLException + { + this.cursor = cursor; + } + + /** + * getCursorName gets the cursor name. + * + * @return the current cursor name + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return cursor; + } + + /** + * We are required to bring back certain information by + * the DatabaseMetaData class. These functions do that. + * + * Method getURL() brings back the URL (good job we saved it) + * + * @return the url + * @exception SQLException just in case... + */ + public String getURL() throws SQLException + { + return this_url; + } + + /** + * Method getUserName() brings back the User Name (again, we + * saved it) + * + * @return the user name + * @exception SQLException just in case... + */ + public String getUserName() throws SQLException + { + return PG_USER; + } + + /** + * This returns the Fastpath API for the current connection. + * + * <p><b>NOTE:</b> This is not part of JDBC, but allows access to + * functions on the postgresql backend itself. + * + * <p>It is primarily used by the LargeObject API + * + * <p>The best way to use this is as follows: + * + * <p><pre> + * import postgresql.fastpath.*; + * ... + * Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI(); + * </pre> + * + * <p>where myconn is an open Connection to postgresql. + * + * @return Fastpath object allowing access to functions on the postgresql + * backend. + * @exception SQLException by Fastpath when initialising for first time + */ + public Fastpath getFastpathAPI() throws SQLException + { + if(fastpath==null) + 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. + * + * <p><b>NOTE:</b> This is not part of JDBC, but allows access to + * functions on the postgresql backend itself. + * + * <p>The best way to use this is as follows: + * + * <p><pre> + * import postgresql.largeobject.*; + * ... + * LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI(); + * </pre> + * + * <p>where myconn is an open Connection to postgresql. + * + * @return LargeObject object that implements the API + * @exception SQLException by LargeObject when initialising for first time + */ + public LargeObjectManager getLargeObjectAPI() throws SQLException + { + if(largeobject==null) + 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 + * postgresql's more unique data types. + * + * <p>It uses an internal Hashtable to get the handling class. If the + * type is not supported, then an instance of postgresql.util.PGobject + * is returned. + * + * You can use the getValue() or setValue() methods to handle the returned + * object. Custom objects can have their own methods. + * + * In 6.4, this is extended to use the postgresql.util.Serialize class to + * allow the Serialization of Java Objects into the database without using + * Blobs. Refer to that class for details on how this new feature works. + * + * @return PGobject for this type, and set to value + * @exception SQLException if value is not correct for this type + * @see postgresql.util.Serialize + */ + public Object getObject(String type,String value) throws SQLException + { + 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 + if(o == null) { + Serialize ser = new Serialize(this,type); + 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. + // + // This is used to implement the postgresql unique types (like lseg, + // point, etc). + if(o instanceof String) { + // 6.3 style extending PG_Object + PGobject obj = null; + obj = (PGobject)(Class.forName((String)o).newInstance()); + obj.setType(type); + obj.setValue(value); + return (Object)obj; + } else { + // If it's an object, it should be an instance of our Serialize class + // If so, then call it's fetch method. + if(o instanceof Serialize) + 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); } - if (final_error != null) - throw final_error; - return new ResultSet(this, fields, tuples, recv_status, 1); + + // should never be reached + return null; } - } - - /** - * Receive the field descriptions from the back end - * - * @return an array of the Field object describing the fields - * @exception SQLException if a database error occurs - */ - private Field[] ReceiveFields() throws SQLException - { - int nf = pg_stream.ReceiveIntegerR(2), i; - Field[] fields = new Field[nf]; - for (i = 0 ; i < nf ; ++i) - { - 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); - } - 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 - * using a positioned update/delete statement that references the - * cursor name. - * - * We support one cursor per connection. - * - * setCursorName sets the cursor name. - * - * @param cursor the cursor name - * @exception SQLException if a database access error occurs - */ - public void setCursorName(String cursor) throws SQLException - { - this.cursor = cursor; - } - - /** - * getCursorName gets the cursor name. - * - * @return the current cursor name - * @exception SQLException if a database access error occurs - */ - public String getCursorName() throws SQLException - { - return cursor; - } - - /** - * We are required to bring back certain information by - * the DatabaseMetaData class. These functions do that. - * - * Method getURL() brings back the URL (good job we saved it) - * - * @return the url - * @exception SQLException just in case... - */ - public String getURL() throws SQLException - { - return this_url; - } - - /** - * Method getUserName() brings back the User Name (again, we - * saved it) - * - * @return the user name - * @exception SQLException just in case... - */ - public String getUserName() throws SQLException - { - return PG_USER; - } - - /** - * This returns the Fastpath API for the current connection. - * - * <p><b>NOTE:</b> This is not part of JDBC, but allows access to - * functions on the postgresql backend itself. - * - * <p>It is primarily used by the LargeObject API - * - * <p>The best way to use this is as follows: - * - * <p><pre> - * import postgresql.fastpath.*; - * ... - * Fastpath fp = ((postgresql.Connection)myconn).getFastpathAPI(); - * </pre> - * - * <p>where myconn is an open Connection to postgresql. - * - * @return Fastpath object allowing access to functions on the postgresql - * backend. - * @exception SQLException by Fastpath when initialising for first time - */ - public Fastpath getFastpathAPI() throws SQLException - { - if(fastpath==null) - 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. - * - * <p><b>NOTE:</b> This is not part of JDBC, but allows access to - * functions on the postgresql backend itself. - * - * <p>The best way to use this is as follows: - * - * <p><pre> - * import postgresql.largeobject.*; - * ... - * LargeObjectManager lo = ((postgresql.Connection)myconn).getLargeObjectAPI(); - * </pre> - * - * <p>where myconn is an open Connection to postgresql. - * - * @return LargeObject object that implements the API - * @exception SQLException by LargeObject when initialising for first time - */ - public LargeObjectManager getLargeObjectAPI() throws SQLException - { - if(largeobject==null) - 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 - * postgresql's more unique data types. - * - * <p>It uses an internal Hashtable to get the handling class. If the - * type is not supported, then an instance of postgresql.util.PGobject - * is returned. - * - * You can use the getValue() or setValue() methods to handle the returned - * object. Custom objects can have their own methods. - * - * In 6.4, this is extended to use the postgresql.util.Serialize class to - * allow the Serialization of Java Objects into the database without using - * Blobs. Refer to that class for details on how this new feature works. - * - * @return PGobject for this type, and set to value - * @exception SQLException if value is not correct for this type - * @see postgresql.util.Serialize - */ - protected Object getObject(String type,String value) throws SQLException - { - 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 - if(o == null) { - Serialize ser = new Serialize(this,type); - 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. - // - // This is used to implement the postgresql unique types (like lseg, - // point, etc). - if(o instanceof String) { - // 6.3 style extending PG_Object - PGobject obj = null; - obj = (PGobject)(Class.forName((String)o).newInstance()); - obj.setType(type); - obj.setValue(value); - return (Object)obj; - } else { - // If it's an object, it should be an instance of our Serialize class - // If so, then call it's fetch method. - if(o instanceof Serialize) - 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); + /** + * This stores an object into the database. + * @param o Object to store + * @return OID of the new rectord + * @exception SQLException if value is not correct for this type + * @see postgresql.util.Serialize + */ + public int putObject(Object o) throws SQLException + { + 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 + if(x == null) { + Serialize ser = new Serialize(this,type); + 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 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 null; - } - - /** - * This stores an object into the database. - * @param o Object to store - * @return OID of the new rectord - * @exception SQLException if value is not correct for this type - * @see postgresql.util.Serialize - */ - protected int putObject(Object o) throws SQLException - { - 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 - if(x == null) { - Serialize ser = new Serialize(this,type); - 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 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); + /** + * This allows client code to add a handler for one of postgresql's + * more unique data types. + * + * <p><b>NOTE:</b> This is not part of JDBC, but an extension. + * + * <p>The best way to use this is as follows: + * + * <p><pre> + * ... + * ((postgresql.Connection)myconn).addDataType("mytype","my.class.name"); + * ... + * </pre> + * + * <p>where myconn is an open Connection to postgresql. + * + * <p>The handling class must extend postgresql.util.PGobject + * + * @see postgresql.util.PGobject + */ + public void addDataType(String type,String name) + { + objectTypes.put(type,name); } - } - - /** - * This allows client code to add a handler for one of postgresql's - * more unique data types. - * - * <p><b>NOTE:</b> This is not part of JDBC, but an extension. - * - * <p>The best way to use this is as follows: - * - * <p><pre> - * ... - * ((postgresql.Connection)myconn).addDataType("mytype","my.class.name"); - * ... - * </pre> - * - * <p>where myconn is an open Connection to postgresql. - * - * <p>The handling class must extend postgresql.util.PGobject - * - * @see postgresql.util.PGobject - */ - public void addDataType(String type,String name) - { - 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 - // the full class name of the handling class. - // - 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"}, - {"money", "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]); - } + + // 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 + // the full class name of the handling class. + // + 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"}, + {"money", "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(postgresql.Connection conn, Field[] fields, Vector tuples, String status, int updateCount) throws SQLException; } - -// *********************************************************************** - |