Connection.java
上传用户:psq1974
上传日期:2007-01-06
资源大小:1195k
文件大小:33k
- package postgresql;
- import java.io.*;
- import java.lang.*;
- import java.lang.reflect.*;
- import java.net.*;
- import java.util.*;
- import java.sql.*;
- 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.
- *
- * <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.
- *
- * <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
- {
- // This is the network stream associated with this connection
- protected 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;
-
- private String PG_HOST;
- private int PG_PORT;
- private String PG_USER;
- private String PG_PASSWORD;
- private String PG_DATABASE;
- private boolean PG_STATUS;
-
- public boolean CONNECTION_OK = true;
- public boolean CONNECTION_BAD = false;
-
- private boolean autoCommit = true;
- private boolean readOnly = false;
-
- protected 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
- protected static final int PG_PROTOCOL_LATEST_MAJOR = 1;
- protected static final int PG_PROTOCOL_LATEST_MINOR = 0;
- private static final int SM_DATABASE = 64;
- private static final int SM_USER = 32;
- 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.
- protected Hashtable fieldCache = new Hashtable();
-
- /**
- * This is the current date style of the backend
- */
- protected int currentDateStyle;
-
- /**
- * This defines the formats for dates, according to the various date styles.
- *
- * <p>There are two strings for each entry. The first is the string to search
- * for in the datestyle message, and the second the format to use.
- *
- * <p>To add a new date style, work out the format. Then with psql running
- * in the date style you wish to add, type: show datestyle;
- *
- * <p>eg:
- * <br><pre>
- * => show datestyle;
- * NOTICE: Datestyle is SQL with European conventions
- * ^^^^^^^^^^^^^^^^^
- * </pre>The marked part of the string is the first string below. The second
- * is your format. If a style (like ISO) ignores the US/European variants,
- * then you can ignore the "with" part of the string.
- */
- protected static final String dateStyles[] = {
- "Postgres with European", "dd-MM-yyyy",
- "Postgres with US", "MM-dd-yyyy",
- "ISO", "yyyy-MM-dd",
- "SQL with European", "dd/MM/yyyy",
- "SQL with US", "MM/dd/yyyy",
- "German", "dd.MM.yyyy"
- };
-
- // Now handle notices as warnings, so things like "show" now work
- protected 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
- {
- // 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
- if(info.getProperty("user")==null)
- throw new SQLException("The user property is missing. It is mandatory.");
- if(info.getProperty("password")==null)
- throw new SQLException("The password property is missing. It is mandatory.");
-
- this_driver = d;
- this_url = new String(url);
- PG_DATABASE = new String(database);
- PG_PASSWORD = new String(info.getProperty("password"));
- PG_USER = new String(info.getProperty("user"));
- PG_PORT = port;
- PG_HOST = new String(host);
- PG_STATUS = CONNECTION_BAD;
-
- // 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());
- }
-
- // Now we need to construct and send a startup packet
- try
- {
- // 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);
-
- // 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
- do {
- int beresp = pg_stream.ReceiveChar();
- 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':
- // 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];
- rst[0] = (byte)pg_stream.ReceiveChar();
- rst[1] = (byte)pg_stream.ReceiveChar();
- 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 SQLException("Kerberos 4 not supported");
-
- case AUTH_REQ_KRB5:
- DriverManager.println("postgresql: KRB5");
- throw new SQLException("Kerberos 5 not supported");
-
- case AUTH_REQ_PASSWORD:
- DriverManager.println("postgresql: PASSWORD");
- pg_stream.SendInteger(5+PG_PASSWORD.length(),4);
- pg_stream.Send(PG_PASSWORD.getBytes());
- pg_stream.SendInteger(0,1);
- pg_stream.flush();
- break;
-
- case AUTH_REQ_CRYPT:
- DriverManager.println("postgresql: CRYPT");
- String crypted = UnixCrypt.crypt(salt,PG_PASSWORD);
- pg_stream.SendInteger(5+crypted.length(),4);
- pg_stream.Send(crypted.getBytes());
- pg_stream.SendInteger(0,1);
- pg_stream.flush();
- break;
-
- default:
- 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;
-
- default:
- throw new SQLException("error getting authentication request");
- }
- } while(areq != AUTH_REQ_OK);
-
- } catch (IOException e) {
- throw new SQLException("Connection failed: " + e.toString());
- }
-
- // Find out the date style by issuing the SQL: show datestyle
- // This actually issues a warning, and our own warning handling
- // code handles this itself.
- //
- // Also, this query replaced the NULL query issued to test the
- // connection.
- //
- clearWarnings();
- ExecSQL("show datestyle");
-
- // Initialise object handling
- initObjectTypes();
-
- // Mark the connection as ok, and cleanup
- clearWarnings();
- 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);
-
- // 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
- }
- }
-
- /**
- * @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
- {
- 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 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
- */
- 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);
- }
-
- // 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 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]);
- }
- }
- // ***********************************************************************