From 6a061da272f04e1463864065f87f1f3fd61d6162 Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Sun, 31 Aug 1997 08:15:13 +0000 Subject: [PATCH] Update patch from Peter --- .../jdbc/postgresql/CallableStatement.java | 243 +- .../jdbc/postgresql/Connection.java | 1609 ++++---- .../jdbc/postgresql/DatabaseMetaData.java | 3569 ++++++++++------- src/interfaces/jdbc/postgresql/Driver.java | 500 +-- src/interfaces/jdbc/postgresql/Field.java | 160 +- src/interfaces/jdbc/postgresql/PG_Object.java | 142 +- src/interfaces/jdbc/postgresql/ResultSet.java | 1580 ++++---- .../jdbc/postgresql/ResultSetMetaData.java | 821 ++-- 8 files changed, 4656 insertions(+), 3968 deletions(-) diff --git a/src/interfaces/jdbc/postgresql/CallableStatement.java b/src/interfaces/jdbc/postgresql/CallableStatement.java index ff7ec7c26f..ede69bbb12 100644 --- a/src/interfaces/jdbc/postgresql/CallableStatement.java +++ b/src/interfaces/jdbc/postgresql/CallableStatement.java @@ -1,126 +1,133 @@ package postgresql; -import java.math.*; import java.sql.*; +import java.math.*; /** - * @version 1.0 15-APR-1997 - * @author Adrian Hall - * - * CallableStatement is used to execute SQL stored procedures. - * - * JDBC provides a stored procedure SQL escape that allows stored procedures - * to be called in a standard way for all RDBMS's. This escape syntax has - * one form that includes a result parameter and one that does not. If used, - * the result parameter must be generated as an OUT parameter. The other - * parameters may be used for input, output or both. Parameters are refered - * to sequentially, by number. The first parameter is 1. - * - *
- *	{?= call [,, ...]}
- *	{call [,, ...]}
- * 
- * - * IN parameters are set using the set methods inherited from - * PreparedStatement. The type of all OUT parameters must be registered - * prior to executing the stored procedure; their values are retrieved - * after execution via the get methods provided here. - * - * A CallableStatement may return a ResultSet or multiple ResultSets. Multiple - * ResultSets are handled using operations inherited from Statement. - * - * For maximum portability, a call's ResultSets and update counts should be - * processed prior to getting the values of output parameters. - * - * @see java.sql.Connection#prepareCall - * @see java.sql.ResultSet - * @see java.sql.CallableStatement + * JDBC Interface to Postgres95 functions */ -public class CallableStatement implements java.sql.CallableStatement + +// Copy methods from the Result set object here. + +public class CallableStatement extends PreparedStatement implements java.sql.CallableStatement { - public void registerOutParameter (int paramterIndex, int sqlType) throws SQLException - { - // XXX-Not Implemented - } - - public void registerOutParameter (int parameterIndex, int sqlType, int scale) throws SQLException - { - // XXX-Not Implemented - } - - public boolean wasNull () throws SQLException - { - // XXX-Not Implemented - } - - public String getString (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public boolean getBoolean (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public byte getByte (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public short getShort (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public int getInt (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public long getLong (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public float getFloat (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public double getDouble (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public BigDecimal getBigDecimal (int parameterIndex, int scale) throws SQLException - { - // XXX-Not Implemented - } - - public byte[] getBytes (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public Date getDate (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public Time getTime (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public Timestamp getTimestamp (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - - public Object getObject (int parameterIndex) throws SQLException - { - // XXX-Not Implemented - } - + CallableStatement(Connection c,String q) throws SQLException + { + super(c,q); + } + + // Before executing a stored procedure call you must explicitly + // call registerOutParameter to register the java.sql.Type of each + // out parameter. + public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { + } + + // You must also specify the scale for numeric/decimal types: + public void registerOutParameter(int parameterIndex, int sqlType, + int scale) throws SQLException + { + } + + public boolean isNull(int parameterIndex) throws SQLException { + return true; + } + + // New API (JPM) + public boolean wasNull() throws SQLException { + // check to see if the last access threw an exception + return false; // fake it for now + } + + // Methods for retrieving OUT parameters from this statement. + public String getChar(int parameterIndex) throws SQLException { + return null; + } + + // New API (JPM) + public String getString(int parameterIndex) throws SQLException { + return null; + } + //public String getVarChar(int parameterIndex) throws SQLException { + // return null; + //} + + public String getLongVarChar(int parameterIndex) throws SQLException { + return null; + } + + // New API (JPM) (getBit) + public boolean getBoolean(int parameterIndex) throws SQLException { + return false; + } + + // New API (JPM) (getTinyInt) + public byte getByte(int parameterIndex) throws SQLException { + return 0; + } + + // New API (JPM) (getSmallInt) + public short getShort(int parameterIndex) throws SQLException { + return 0; + } + + // New API (JPM) (getInteger) + public int getInt(int parameterIndex) throws SQLException { + return 0; + } + + // New API (JPM) (getBigInt) + public long getLong(int parameterIndex) throws SQLException { + return 0; + } + + public float getFloat(int parameterIndex) throws SQLException { + return (float) 0.0; + } + + public double getDouble(int parameterIndex) throws SQLException { + return 0.0; + } + + public BigDecimal getBigDecimal(int parameterIndex, int scale) + throws SQLException { + return null; + } + + // New API (JPM) (getBinary) + public byte[] getBytes(int parameterIndex) throws SQLException { + return null; + } + + // New API (JPM) (getLongVarBinary) + public byte[] getBinaryStream(int parameterIndex) throws SQLException { + return null; + } + + public java.sql.Date getDate(int parameterIndex) throws SQLException { + return null; + } + public java.sql.Time getTime(int parameterIndex) throws SQLException { + return null; + } + public java.sql.Timestamp getTimestamp(int parameterIndex) + throws SQLException { + return null; + } + + //---------------------------------------------------------------------- + // Advanced features: + + // You can obtain a ParameterMetaData object to get information + // about the parameters to this CallableStatement. + public DatabaseMetaData getMetaData() { + return null; + } + + // getObject returns a Java object for the parameter. + // See the JDBC spec's "Dynamic Programming" chapter for details. + public Object getObject(int parameterIndex) + throws SQLException { + return null; + } } + diff --git a/src/interfaces/jdbc/postgresql/Connection.java b/src/interfaces/jdbc/postgresql/Connection.java index aa354b61fe..a208970ae2 100644 --- a/src/interfaces/jdbc/postgresql/Connection.java +++ b/src/interfaces/jdbc/postgresql/Connection.java @@ -28,570 +28,569 @@ import postgresql.*; */ public class Connection implements java.sql.Connection { - private PG_Stream pg_stream; - - 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 int STARTUP_CODE = 7; - - private boolean autoCommit = true; - private boolean readOnly = false; - - private Driver this_driver; - private String this_url; - private String cursor = null; // The positioned update cursor name - - /** - * Connect to a PostgreSQL database back end. - * - * @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 + private PG_Stream pg_stream; + + 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 int STARTUP_CODE = 7; + + 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 + + /** + * Connect to a PostgreSQL database back end. + * + * @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 + { + int len = 288; // Length of a startup packet + + 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; + + try + { + pg_stream = new PG_Stream(host, port); + } catch (IOException e) { + throw new SQLException ("Connection failed: " + e.toString()); + } + + // Now we need to construct and send a startup packet + try { - int len = 288; // Length of a startup packet - - 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; - - try + 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); + } catch (IOException e) { + throw new SQLException("Connection failed: " + e.toString()); + } + ExecSQL(" "); // Test connection + 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. + * + * Note: 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. + * + * Note: 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) + * + * Note: 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 + { + return new DatabaseMetaData(this); + } + + /** + * You can put a connection in read-only mode as a hunt to enable + * database optimizations + * + * Note: 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. + * + * Note: 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. + * + * Note: 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 null; // We handle warnings as errors + } + + /** + * 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 + { + // Not handles since we handle wanrings as errors + } + + // ********************************************************** + // END OF PUBLIC INTERFACE + // ********************************************************** + + /** + * Send a query to the backend. Returns one of the ResultSet + * objects. + * + * Note: 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 synchronized ResultSet ExecSQL(String sql) throws SQLException + { + 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); + } catch (IOException e) { + throw new SQLException("I/O Error: " + e.toString()); + } + + while (!hfr || fqp > 0) + { + 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"); + tuples.addElement(pg_stream.ReceiveTuple(fields.length, true)); + break; + case 'C': // Command Status + recv_status = pg_stream.ReceiveString(8192); + if (fields != null) + hfr = true; + else { - pg_stream = new PG_Stream(host, port); - } catch (IOException e) { - throw new SQLException ("Connection failed: " + e.toString()); + try + { + pg_stream.SendChar('Q'); + pg_stream.SendChar(' '); + pg_stream.SendChar(0); + } catch (IOException e) { + throw new SQLException("I/O Error: " + e.toString()); + } + fqp++; } - - // Now we need to construct and send a startup packet - try - { - 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); - } catch (IOException e) { - throw new SQLException("Connection failed: " + e.toString()); - } - ExecSQL(" "); // Test connection - 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. - * - * Note: 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. - * - * Note: 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) - * - * Note: 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 - { -// return new DatabaseMetaData(this); - throw new SQLException("DatabaseMetaData not supported"); - } - - /** - * You can put a connection in read-only mode as a hunt to enable - * database optimizations - * - * Note: 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. - * - * Note: 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. - * - * Note: 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 null; // We handle warnings as errors - } - - /** - * 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 - { - // Not handles since we handle wanrings as errors - } - - // ********************************************************** - // END OF PUBLIC INTERFACE - // ********************************************************** - - /** - * Send a query to the backend. Returns one of the ResultSet - * objects. - * - * Note: 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 synchronized ResultSet ExecSQL(String sql) throws SQLException - { - 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); - } catch (IOException e) { - throw new SQLException("I/O Error: " + e.toString()); - } - - while (!hfr || fqp > 0) - { - 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"); - tuples.addElement(pg_stream.ReceiveTuple(fields.length, true)); - 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); - } 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"); - tuples.addElement(pg_stream.ReceiveTuple(fields.length, false)); - 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 - msg = pg_stream.ReceiveString(4096); - PrintStream log = DriverManager.getLogStream(); - log.println(msg); - 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.ReceiveInteger(2), i; - Field[] fields = new Field[nf]; - - for (i = 0 ; i < nf ; ++i) - { - String typname = pg_stream.ReceiveString(8192); - int typid = pg_stream.ReceiveInteger(4); - int typlen = pg_stream.ReceiveInteger(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; + break; + case 'D': // Text Data Transfer + if (fields == null) + throw new SQLException("Tuple received before MetaData"); + tuples.addElement(pg_stream.ReceiveTuple(fields.length, false)); + 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 + msg = pg_stream.ReceiveString(4096); + PrintStream log = DriverManager.getLogStream(); + log.println(msg); + 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.ReceiveInteger(2), i; + Field[] fields = new Field[nf]; + + for (i = 0 ; i < nf ; ++i) + { + String typname = pg_stream.ReceiveString(8192); + int typid = pg_stream.ReceiveInteger(4); + int typlen = pg_stream.ReceiveInteger(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; + } } // *********************************************************************** @@ -599,249 +598,249 @@ public class Connection implements java.sql.Connection // This class handles all the Streamed I/O for a postgresql connection class PG_Stream { - private Socket connection; - private InputStream pg_input; - private OutputStream pg_output; - - /** - * Constructor: Connect to the PostgreSQL back end and return - * a stream connection. - * - * @param host the hostname to connect to - * @param port the port number that the postmaster is sitting on - * @exception IOException if an IOException occurs below it. - */ - public PG_Stream(String host, int port) throws IOException - { - connection = new Socket(host, port); - pg_input = connection.getInputStream(); - pg_output = connection.getOutputStream(); - } - - /** - * Sends a single character to the back end - * - * @param val the character to be sent - * @exception IOException if an I/O error occurs - */ - public void SendChar(int val) throws IOException - { - pg_output.write(val); - } - - /** - * Sends an integer to the back end - * - * @param val the integer to be sent - * @param siz the length of the integer in bytes (size of structure) - * @exception IOException if an I/O error occurs - */ - public void SendInteger(int val, int siz) throws IOException - { - byte[] buf = new byte[siz]; - - while (siz-- > 0) - { - buf[siz] = (byte)(val & 0xff); - val >>= 8; - } - Send(buf); - } - - /** - * Send an array of bytes to the backend - * - * @param buf The array of bytes to be sent - * @exception IOException if an I/O error occurs - */ - public void Send(byte buf[]) throws IOException - { - pg_output.write(buf); - } - - /** - * Send an exact array of bytes to the backend - if the length - * has not been reached, send nulls until it has. - * - * @param buf the array of bytes to be sent - * @param siz the number of bytes to be sent - * @exception IOException if an I/O error occurs - */ - public void Send(byte buf[], int siz) throws IOException - { - int i; - - pg_output.write(buf, 0, (buf.length < siz ? buf.length : siz)); - if (buf.length < siz) - { - for (i = buf.length ; i < siz ; ++i) - { - pg_output.write(0); - } - } - } - - /** - * Receives a single character from the backend - * - * @return the character received - * @exception SQLException if an I/O Error returns - */ - public int ReceiveChar() throws SQLException - { - int c = 0; - - try - { - c = pg_input.read(); - if (c < 0) throw new IOException("EOF"); - } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); - } - return c; - } - - /** - * Receives an integer from the backend - * - * @param siz length of the integer in bytes - * @return the integer received from the backend - * @exception SQLException if an I/O error occurs - */ - public int ReceiveInteger(int siz) throws SQLException - { - int n = 0; - - try - { - for (int i = 0 ; i < siz ; i++) - { - int b = pg_input.read(); - - if (b < 0) - throw new IOException("EOF"); - n = n | (b >> (8 * i)) ; - } - } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); - } - return n; - } - - /** - * Receives a null-terminated string from the backend. Maximum of - * maxsiz bytes - if we don't see a null, then we assume something - * has gone wrong. - * - * @param maxsiz maximum length of string - * @return string from back end - * @exception SQLException if an I/O error occurs - */ - public String ReceiveString(int maxsiz) throws SQLException - { - byte[] rst = new byte[maxsiz]; - int s = 0; - - try - { - while (s < maxsiz) - { - int c = pg_input.read(); - if (c < 0) - throw new IOException("EOF"); - else if (c == 0) - break; - else - rst[s++] = (byte)c; - } - if (s >= maxsiz) - throw new IOException("Too Much Data"); - } catch (IOException e) { - throw new SQLException("Error reading from backend: " + e.toString()); - } - String v = new String(rst, 0, s); - return v; - } - - /** - * Read a tuple from the back end. A tuple is a two dimensional - * array of bytes - * - * @param nf the number of fields expected - * @param bin true if the tuple is a binary tuple - * @return null if the current response has no more tuples, otherwise - * an array of strings - * @exception SQLException if a data I/O error occurs - */ - public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException - { - int i, bim = (nf + 7)/8; - byte[] bitmask = Receive(bim); - byte[][] answer = new byte[nf][0]; - - int whichbit = 0x80; - int whichbyte = 0; - - for (i = 0 ; i < nf ; ++i) - { - boolean isNull = ((bitmask[whichbyte] & whichbit) == 0); - whichbit >>= 1; - if (whichbit == 0) - { - ++whichbyte; - whichbit = 0x80; - } - if (isNull) - answer[i] = null; - else - { - int len = ReceiveInteger(4); - if (!bin) - len -= 4; - if (len < 0) - len = 0; - answer[i] = Receive(len); - } - } - return answer; - } - - /** - * Reads in a given number of bytes from the backend - * - * @param siz number of bytes to read - * @return array of bytes received - * @exception SQLException if a data I/O error occurs - */ - 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; - } - - /** - * Closes the connection - * - * @exception IOException if a IO Error occurs - */ - public void close() throws IOException - { - pg_output.close(); - pg_input.close(); - connection.close(); - } + private Socket connection; + private InputStream pg_input; + private OutputStream pg_output; + + /** + * Constructor: Connect to the PostgreSQL back end and return + * a stream connection. + * + * @param host the hostname to connect to + * @param port the port number that the postmaster is sitting on + * @exception IOException if an IOException occurs below it. + */ + public PG_Stream(String host, int port) throws IOException + { + connection = new Socket(host, port); + pg_input = connection.getInputStream(); + pg_output = connection.getOutputStream(); + } + + /** + * Sends a single character to the back end + * + * @param val the character to be sent + * @exception IOException if an I/O error occurs + */ + public void SendChar(int val) throws IOException + { + pg_output.write(val); + } + + /** + * Sends an integer to the back end + * + * @param val the integer to be sent + * @param siz the length of the integer in bytes (size of structure) + * @exception IOException if an I/O error occurs + */ + public void SendInteger(int val, int siz) throws IOException + { + byte[] buf = new byte[siz]; + + while (siz-- > 0) + { + buf[siz] = (byte)(val & 0xff); + val >>= 8; + } + Send(buf); + } + + /** + * Send an array of bytes to the backend + * + * @param buf The array of bytes to be sent + * @exception IOException if an I/O error occurs + */ + public void Send(byte buf[]) throws IOException + { + pg_output.write(buf); + } + + /** + * Send an exact array of bytes to the backend - if the length + * has not been reached, send nulls until it has. + * + * @param buf the array of bytes to be sent + * @param siz the number of bytes to be sent + * @exception IOException if an I/O error occurs + */ + public void Send(byte buf[], int siz) throws IOException + { + int i; + + pg_output.write(buf, 0, (buf.length < siz ? buf.length : siz)); + if (buf.length < siz) + { + for (i = buf.length ; i < siz ; ++i) + { + pg_output.write(0); + } + } + } + + /** + * Receives a single character from the backend + * + * @return the character received + * @exception SQLException if an I/O Error returns + */ + public int ReceiveChar() throws SQLException + { + int c = 0; + + try + { + c = pg_input.read(); + if (c < 0) throw new IOException("EOF"); + } catch (IOException e) { + throw new SQLException("Error reading from backend: " + e.toString()); + } + return c; + } + + /** + * Receives an integer from the backend + * + * @param siz length of the integer in bytes + * @return the integer received from the backend + * @exception SQLException if an I/O error occurs + */ + public int ReceiveInteger(int siz) throws SQLException + { + int n = 0; + + try + { + for (int i = 0 ; i < siz ; i++) + { + int b = pg_input.read(); + + if (b < 0) + throw new IOException("EOF"); + n = n | (b >> (8 * i)) ; + } + } catch (IOException e) { + throw new SQLException("Error reading from backend: " + e.toString()); + } + return n; + } + + /** + * Receives a null-terminated string from the backend. Maximum of + * maxsiz bytes - if we don't see a null, then we assume something + * has gone wrong. + * + * @param maxsiz maximum length of string + * @return string from back end + * @exception SQLException if an I/O error occurs + */ + public String ReceiveString(int maxsiz) throws SQLException + { + byte[] rst = new byte[maxsiz]; + int s = 0; + + try + { + while (s < maxsiz) + { + int c = pg_input.read(); + if (c < 0) + throw new IOException("EOF"); + else if (c == 0) + break; + else + rst[s++] = (byte)c; + } + if (s >= maxsiz) + throw new IOException("Too Much Data"); + } catch (IOException e) { + throw new SQLException("Error reading from backend: " + e.toString()); + } + String v = new String(rst, 0, s); + return v; + } + + /** + * Read a tuple from the back end. A tuple is a two dimensional + * array of bytes + * + * @param nf the number of fields expected + * @param bin true if the tuple is a binary tuple + * @return null if the current response has no more tuples, otherwise + * an array of strings + * @exception SQLException if a data I/O error occurs + */ + public byte[][] ReceiveTuple(int nf, boolean bin) throws SQLException + { + int i, bim = (nf + 7)/8; + byte[] bitmask = Receive(bim); + byte[][] answer = new byte[nf][0]; + + int whichbit = 0x80; + int whichbyte = 0; + + for (i = 0 ; i < nf ; ++i) + { + boolean isNull = ((bitmask[whichbyte] & whichbit) == 0); + whichbit >>= 1; + if (whichbit == 0) + { + ++whichbyte; + whichbit = 0x80; + } + if (isNull) + answer[i] = null; + else + { + int len = ReceiveInteger(4); + if (!bin) + len -= 4; + if (len < 0) + len = 0; + answer[i] = Receive(len); + } + } + return answer; + } + + /** + * Reads in a given number of bytes from the backend + * + * @param siz number of bytes to read + * @return array of bytes received + * @exception SQLException if a data I/O error occurs + */ + 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; + } + + /** + * Closes the connection + * + * @exception IOException if a IO Error occurs + */ + public void close() throws IOException + { + pg_output.close(); + pg_input.close(); + connection.close(); + } } diff --git a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java index 259829c3fb..00485b2585 100644 --- a/src/interfaces/jdbc/postgresql/DatabaseMetaData.java +++ b/src/interfaces/jdbc/postgresql/DatabaseMetaData.java @@ -1,6 +1,7 @@ package postgresql; import java.sql.*; +import java.util.*; /** * @version 1.0 15-APR-1997 @@ -30,1527 +31,2053 @@ import java.sql.*; */ public class DatabaseMetaData implements java.sql.DatabaseMetaData { - Connection connection; // The connection association - - public DatabaseMetaData(Connection conn) - { - this.connection = conn; - } - - /** - * Can all the procedures returned by getProcedures be called - * by the current user? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean allProceduresAreCallable() throws SQLException - { - return true; // For now... - } - - /** - * Can all the tables returned by getTable be SELECTed by - * the current user? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean allTablesAreSelectable() throws SQLException - { - return true; // For now... - } - - /** - * What is the URL for this database? - * - * @return the url or null if it cannott be generated - * @exception SQLException if a database access error occurs - */ - public String getURL() throws SQLException - { - return connection.getURL(); - } - - /** - * What is our user name as known to the database? - * - * @return our database user name - * @exception SQLException if a database access error occurs - */ - public String getUserName() throws SQLException - { - return connection.getUserName(); - } - - /** - * Is the database in read-only mode? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly() throws SQLException - { - return connection.isReadOnly(); - } - - /** - * Are NULL values sorted high? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedHigh() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted low? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedLow() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted at the start regardless of sort order? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedAtStart() throws SQLException - { - return false; - } - - /** - * Are NULL values sorted at the end regardless of sort order? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullsAreSortedAtEnd() throws SQLException - { - return true; - } - - /** - * What is the name of this database product - we hope that it is - * PostgreSQL, so we return that explicitly. - * - * @return the database product name - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductName() throws SQLException - { - return new String("PostgreSQL"); - } - - /** - * What is the version of this database product. Note that - * PostgreSQL 6.1 has a system catalog called pg_version - - * however, select * from pg_version on any database retrieves - * no rows. For now, we will return the version 6.1 (in the - * hopes that we change this driver as often as we change the - * database) - * - * @return the database version - * @exception SQLException if a database access error occurs - */ - public String getDatabaseProductVersion() throws SQLException - { - return ("6.1"); - } - - /** - * What is the name of this JDBC driver? If we don't know this - * we are doing something wrong! - * - * @return the JDBC driver name - * @exception SQLException why? - */ - public String getDriverName() throws SQLException - { - return new String("PostgreSQL Native Driver"); - } - - /** - * What is the version string of this JDBC driver? Again, this is - * static. - * - * @return the JDBC driver name. - * @exception SQLException why? - */ - public String getDriverVersion() throws SQLException - { - return new String("1.0"); - } - - /** - * What is this JDBC driver's major version number? - * - * @return the JDBC driver major version - */ - public int getDriverMajorVersion() - { - return 1; - } - - /** - * What is this JDBC driver's minor version number? - * - * @return the JDBC driver minor version - */ - public int getDriverMinorVersion() - { - return 0; - } - - /** - * Does the database store tables in a local file? No - it - * stores them in a file on the server. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean usesLocalFiles() throws SQLException - { - return false; - } - - /** - * Does the database use a file for each table? Well, not really, - * since it doesnt use local files. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean usesLocalFilePerTable() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers - * as case sensitive and as a result store them in mixed case? - * A JDBC-Compliant driver will always return false. - * - * Predicament - what do they mean by "SQL identifiers" - if it - * means the names of the tables and columns, then the answers - * given below are correct - otherwise I don't know. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseIdentifiers() throws SQLException - { - return true; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in upper case? - * - * @return true if so - */ - public boolean storesUpperCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in lower case? - * - * @return true if so - */ - public boolean storesLowerCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case unquoted SQL identifiers as - * case insensitive and store them in mixed case? - * - * @return true if so - */ - public boolean storesMixedCaseIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as - * case sensitive and as a result store them in mixed case? A - * JDBC compliant driver will always return true. - * - * Predicament - what do they mean by "SQL identifiers" - if it - * means the names of the tables and columns, then the answers - * given below are correct - otherwise I don't know. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException - { - return true; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as - * case insensitive and store them in upper case? - * - * @return true if so - */ - public boolean storesUpperCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as case - * insensitive and store them in lower case? - * - * @return true if so - */ - public boolean storesLowerCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * Does the database treat mixed case quoted SQL identifiers as case - * insensitive and store them in mixed case? - * - * @return true if so - */ - public boolean storesMixedCaseQuotedIdentifiers() throws SQLException - { - return false; - } - - /** - * What is the string used to quote SQL identifiers? This returns - * a space if identifier quoting isn't supported. A JDBC Compliant - * driver will always use a double quote character. - * - * If an SQL identifier is a table name, column name, etc. then - * we do not support it. - * - * @return the quoting string - * @exception SQLException if a database access error occurs - */ - public String getIdentifierQuoteString() throws SQLException - { - return new String(" "); - } - - /** - * Get a comma separated list of all a database's SQL keywords that - * are NOT also SQL92 keywords. - * - * Within PostgreSQL, the keywords are found in - * src/backend/parser/keywords.c - * For SQL Keywords, I took the list provided at - * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt - * which is for SQL3, not SQL-92, but it is close enough for - * this purpose. - * - * @return a comma separated list of keywords we use - * @exception SQLException if a database access error occurs - */ - public String getSQLKeywords() throws SQLException - { - return new String("abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"); - } - - public String getNumericFunctions() throws SQLException - { - // XXX-Not Implemented - } - - public String getStringFunctions() throws SQLException - { - // XXX-Not Implemented - } - - public String getSystemFunctions() throws SQLException - { - // XXX-Not Implemented - } - - public String getTimeDateFunctions() throws SQLException - { - // XXX-Not Implemented - } - - /** - * This is the string that can be used to escape '_' and '%' in - * a search string pattern style catalog search parameters - * - * @return the string used to escape wildcard characters - * @exception SQLException if a database access error occurs - */ - public String getSearchStringEscape() throws SQLException - { - return new String("\\"); - } - - /** - * Get all the "extra" characters that can bew used in unquoted - * identifier names (those beyond a-zA-Z0-9 and _) - * - * From the file src/backend/parser/scan.l, an identifier is - * {letter}{letter_or_digit} which makes it just those listed - * above. - * - * @return a string containing the extra characters - * @exception SQLException if a database access error occurs - */ - public String getExtraNameCharacters() throws SQLException - { - return new String(""); - } - - /** - * Is "ALTER TABLE" with an add column supported? - * Yes for PostgreSQL 6.1 - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithAddColumn() throws SQLException - { - return true; - } - - /** - * Is "ALTER TABLE" with a drop column supported? - * Yes for PostgreSQL 6.1 - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsAlterTableWithDropColumn() throws SQLException - { - return true; - } - - /** - * Is column aliasing supported? - * - * If so, the SQL AS clause can be used to provide names for - * computed columns or to provide alias names for columns as - * required. A JDBC Compliant driver always returns true. - * - * e.g. - * - * select count(C) as C_COUNT from T group by C; - * - * should return a column named as C_COUNT instead of count(C) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsColumnAliasing() throws SQLException - { - return true; - } - - /** - * Are concatenations between NULL and non-NULL values NULL? A - * JDBC Compliant driver always returns true - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean nullPlusNonNullIsNull() throws SQLException - { - return true; - } - - public boolean supportsConvert() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsConvert(int fromType, int toType) throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsTableCorrelationNames() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsDifferentTableCorrelationNames() throws SQLException - { - // XXX-Not Implemented - } - - /** - * Are expressions in "ORCER BY" lists supported? - * - * e.g. select * from t order by a + b; - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsExpressionsInOrderBy() throws SQLException - { - return false; - } - - /** - * Can an "ORDER BY" clause use columns not in the SELECT? - * I checked it, and you can't. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOrderByUnrelated() throws SQLException - { - return false; - } - - /** - * Is some form of "GROUP BY" clause supported? - * I checked it, and yes it is. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupBy() throws SQLException - { - return true; - } - - /** - * Can a "GROUP BY" clause use columns not in the SELECT? - * I checked it - it seems to allow it - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByUnrelated() throws SQLException - { - return true; - } - - /** - * Can a "GROUP BY" clause add columns not in the SELECT provided - * it specifies all the columns in the SELECT? Does anyone actually - * understand what they mean here? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsGroupByBeyondSelect() throws SQLException - { - return true; // For now... - } - - /** - * Is the escape character in "LIKE" clauses supported? A - * JDBC compliant driver always returns true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLikeEscapeClause() throws SQLException - { - return true; - } - - /** - * Are multiple ResultSets from a single execute supported? - * Well, I implemented it, but I dont think this is possible from - * the back ends point of view. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMultipleResultSets() throws SQLException - { - return false; - } - - /** - * Can we have multiple transactions open at once (on different - * connections?) - * I guess we can have, since Im relying on it. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMultipleTransactions() throws SQLException - { - return true; - } - - /** - * Can columns be defined as non-nullable. A JDBC Compliant driver - * always returns true. We dont support NOT NULL, so we are not - * JDBC compliant. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsNonNullableColumns() throws SQLException - { - return false; - } - - /** - * Does this driver support the minimum ODBC SQL grammar. This - * grammar is defined at: - * - * http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm - * - * In Appendix C. From this description, we seem to support the - * ODBC minimal (Level 0) grammar. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsMinimumSQLGrammar() throws SQLException - { - return true; - } - - /** - * Does this driver support the Core ODBC SQL grammar. We need - * SQL-92 conformance for this. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCoreSQLGrammar() throws SQLException - { - return false; - } + Connection connection; // The connection association + + public DatabaseMetaData(Connection conn) + { + this.connection = conn; + } + + /** + * Can all the procedures returned by getProcedures be called + * by the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allProceduresAreCallable() throws SQLException + { + return true; // For now... + } + + /** + * Can all the tables returned by getTable be SELECTed by + * the current user? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean allTablesAreSelectable() throws SQLException + { + return true; // For now... + } + + /** + * What is the URL for this database? + * + * @return the url or null if it cannott be generated + * @exception SQLException if a database access error occurs + */ + public String getURL() throws SQLException + { + return connection.getURL(); + } + + /** + * What is our user name as known to the database? + * + * @return our database user name + * @exception SQLException if a database access error occurs + */ + public String getUserName() throws SQLException + { + return connection.getUserName(); + } + + /** + * Is the database in read-only mode? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly() throws SQLException + { + return connection.isReadOnly(); + } + + /** + * Are NULL values sorted high? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedHigh() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted low? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedLow() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the start regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtStart() throws SQLException + { + return false; + } + + /** + * Are NULL values sorted at the end regardless of sort order? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullsAreSortedAtEnd() throws SQLException + { + return true; + } + + /** + * What is the name of this database product - we hope that it is + * PostgreSQL, so we return that explicitly. + * + * @return the database product name + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductName() throws SQLException + { + return new String("PostgreSQL"); + } + + /** + * What is the version of this database product. Note that + * PostgreSQL 6.1 has a system catalog called pg_version - + * however, select * from pg_version on any database retrieves + * no rows. For now, we will return the version 6.1 (in the + * hopes that we change this driver as often as we change the + * database) + * + * @return the database version + * @exception SQLException if a database access error occurs + */ + public String getDatabaseProductVersion() throws SQLException + { + return ("6.2"); + } + + /** + * What is the name of this JDBC driver? If we don't know this + * we are doing something wrong! + * + * @return the JDBC driver name + * @exception SQLException why? + */ + public String getDriverName() throws SQLException + { + return new String("PostgreSQL Native Driver"); + } + + /** + * What is the version string of this JDBC driver? Again, this is + * static. + * + * @return the JDBC driver name. + * @exception SQLException why? + */ + public String getDriverVersion() throws SQLException + { + return new String(Integer.toString(connection.this_driver.getMajorVersion())+"."+Integer.toString(connection.this_driver.getMinorVersion())); + } + + /** + * What is this JDBC driver's major version number? + * + * @return the JDBC driver major version + */ + public int getDriverMajorVersion() + { + return connection.this_driver.getMajorVersion(); + } + + /** + * What is this JDBC driver's minor version number? + * + * @return the JDBC driver minor version + */ + public int getDriverMinorVersion() + { + return connection.this_driver.getMinorVersion(); + } + + /** + * Does the database store tables in a local file? No - it + * stores them in a file on the server. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFiles() throws SQLException + { + return false; + } + + /** + * Does the database use a file for each table? Well, not really, + * since it doesnt use local files. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean usesLocalFilePerTable() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers + * as case sensitive and as a result store them in mixed case? + * A JDBC-Compliant driver will always return false. + * + * Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case unquoted SQL identifiers as + * case insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case sensitive and as a result store them in mixed case? A + * JDBC compliant driver will always return true. + * + * Predicament - what do they mean by "SQL identifiers" - if it + * means the names of the tables and columns, then the answers + * given below are correct - otherwise I don't know. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException + { + return true; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as + * case insensitive and store them in upper case? + * + * @return true if so + */ + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in lower case? + * + * @return true if so + */ + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * Does the database treat mixed case quoted SQL identifiers as case + * insensitive and store them in mixed case? + * + * @return true if so + */ + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException + { + return false; + } + + /** + * What is the string used to quote SQL identifiers? This returns + * a space if identifier quoting isn't supported. A JDBC Compliant + * driver will always use a double quote character. + * + * If an SQL identifier is a table name, column name, etc. then + * we do not support it. + * + * @return the quoting string + * @exception SQLException if a database access error occurs + */ + public String getIdentifierQuoteString() throws SQLException + { + return new String(" "); + } + + /** + * Get a comma separated list of all a database's SQL keywords that + * are NOT also SQL92 keywords. + * + * Within PostgreSQL, the keywords are found in + * src/backend/parser/keywords.c + * For SQL Keywords, I took the list provided at + * http://web.dementia.org/~shadow/sql/sql3bnf.sep93.txt + * which is for SQL3, not SQL-92, but it is close enough for + * this purpose. + * + * @return a comma separated list of keywords we use + * @exception SQLException if a database access error occurs + */ + public String getSQLKeywords() throws SQLException + { + return new String("abort,acl,add,aggregate,append,archive,arch_store,backward,binary,change,cluster,copy,database,delimiters,do,extend,explain,forward,heavy,index,inherits,isnull,light,listen,load,merge,nothing,notify,notnull,oids,purge,rename,replace,retrieve,returns,rule,recipe,setof,stdin,stdout,store,vacuum,verbose,version"); + } + + public String getNumericFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getStringFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getSystemFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + public String getTimeDateFunctions() throws SQLException + { + // XXX-Not Implemented + return ""; + } + + /** + * This is the string that can be used to escape '_' and '%' in + * a search string pattern style catalog search parameters + * + * @return the string used to escape wildcard characters + * @exception SQLException if a database access error occurs + */ + public String getSearchStringEscape() throws SQLException + { + return new String("\\"); + } + + /** + * Get all the "extra" characters that can bew used in unquoted + * identifier names (those beyond a-zA-Z0-9 and _) + * + * From the file src/backend/parser/scan.l, an identifier is + * {letter}{letter_or_digit} which makes it just those listed + * above. + * + * @return a string containing the extra characters + * @exception SQLException if a database access error occurs + */ + public String getExtraNameCharacters() throws SQLException + { + return new String(""); + } + + /** + * Is "ALTER TABLE" with an add column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithAddColumn() throws SQLException + { + return true; + } + + /** + * Is "ALTER TABLE" with a drop column supported? + * Yes for PostgreSQL 6.1 + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsAlterTableWithDropColumn() throws SQLException + { + return true; + } + + /** + * Is column aliasing supported? + * + * If so, the SQL AS clause can be used to provide names for + * computed columns or to provide alias names for columns as + * required. A JDBC Compliant driver always returns true. + * + * e.g. + * + * select count(C) as C_COUNT from T group by C; + * + * should return a column named as C_COUNT instead of count(C) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsColumnAliasing() throws SQLException + { + return true; + } + + /** + * Are concatenations between NULL and non-NULL values NULL? A + * JDBC Compliant driver always returns true + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean nullPlusNonNullIsNull() throws SQLException + { + return true; + } + + public boolean supportsConvert() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsConvert(int fromType, int toType) throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsTableCorrelationNames() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsDifferentTableCorrelationNames() throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Are expressions in "ORCER BY" lists supported? + * + * e.g. select * from t order by a + b; + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExpressionsInOrderBy() throws SQLException + { + return true; + } + + /** + * Can an "ORDER BY" clause use columns not in the SELECT? + * I checked it, and you can't. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOrderByUnrelated() throws SQLException + { + return false; + } + + /** + * Is some form of "GROUP BY" clause supported? + * I checked it, and yes it is. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupBy() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause use columns not in the SELECT? + * I checked it - it seems to allow it + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByUnrelated() throws SQLException + { + return true; + } + + /** + * Can a "GROUP BY" clause add columns not in the SELECT provided + * it specifies all the columns in the SELECT? Does anyone actually + * understand what they mean here? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsGroupByBeyondSelect() throws SQLException + { + return true; // For now... + } + + /** + * Is the escape character in "LIKE" clauses supported? A + * JDBC compliant driver always returns true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLikeEscapeClause() throws SQLException + { + return true; + } + + /** + * Are multiple ResultSets from a single execute supported? + * Well, I implemented it, but I dont think this is possible from + * the back ends point of view. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleResultSets() throws SQLException + { + return false; + } + + /** + * Can we have multiple transactions open at once (on different + * connections?) + * I guess we can have, since Im relying on it. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMultipleTransactions() throws SQLException + { + return true; + } + + /** + * Can columns be defined as non-nullable. A JDBC Compliant driver + * always returns true. We dont support NOT NULL, so we are not + * JDBC compliant. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsNonNullableColumns() throws SQLException + { + return false; + } + + /** + * Does this driver support the minimum ODBC SQL grammar. This + * grammar is defined at: + * + * http://www.microsoft.com/msdn/sdk/platforms/doc/odbc/src/intropr.htm + * + * In Appendix C. From this description, we seem to support the + * ODBC minimal (Level 0) grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsMinimumSQLGrammar() throws SQLException + { + return true; + } + + /** + * Does this driver support the Core ODBC SQL grammar. We need + * SQL-92 conformance for this. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCoreSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the Extended (Level 2) ODBC SQL + * grammar. We don't conform to the Core (Level 1), so we can't + * conform to the Extended SQL Grammar. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsExtendedSQLGrammar() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 entry level SQL grammar? + * All JDBC Compliant drivers must return true. I think we have + * to support outer joins for this to be true. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92EntryLevelSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 intermediate level SQL + * grammar? Anyone who does not support Entry level cannot support + * Intermediate level. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92IntermediateSQL() throws SQLException + { + return false; + } + + /** + * Does this driver support the ANSI-92 full SQL grammar? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsANSI92FullSQL() throws SQLException + { + return false; + } + + /** + * Is the SQL Integrity Enhancement Facility supported? + * I haven't seen this mentioned anywhere, so I guess not + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsIntegrityEnhancementFacility() throws SQLException + { + return false; + } + + /** + * Is some form of outer join supported? From my knowledge, nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOuterJoins() throws SQLException + { + return false; + } + + /** + * Are full nexted outer joins supported? Well, we dont support any + * form of outer join, so this is no as well + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsFullOuterJoins() throws SQLException + { + return false; + } + + /** + * Is there limited support for outer joins? (This will be true if + * supportFullOuterJoins is true) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsLimitedOuterJoins() throws SQLException + { + return false; + } + + /** + * What is the database vendor's preferred term for "schema" - well, + * we do not provide support for schemas, so lets just use that + * term. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getSchemaTerm() throws SQLException + { + return new String("Schema"); + } + + /** + * What is the database vendor's preferred term for "procedure" - + * I kind of like "Procedure" myself. + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getProcedureTerm() throws SQLException + { + return new String("Procedure"); + } + + /** + * What is the database vendor's preferred term for "catalog"? - + * we dont have a preferred term, so just use Catalog + * + * @return the vendor term + * @exception SQLException if a database access error occurs + */ + public String getCatalogTerm() throws SQLException + { + return new String("Catalog"); + } + + /** + * Does a catalog appear at the start of a qualified table name? + * (Otherwise it appears at the end). + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCatalogAtStart() throws SQLException + { + return false; + } + + /** + * What is the Catalog separator. Hmmm....well, I kind of like + * a period (so we get catalog.table definitions). - I don't think + * PostgreSQL supports catalogs anyhow, so it makes no difference. + * + * @return the catalog separator string + * @exception SQLException if a database access error occurs + */ + public String getCatalogSeparator() throws SQLException + { + return new String("."); + } + + /** + * Can a schema name be used in a data manipulation statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a procedure call statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a schema be used in a table definition statement? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in an index definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a schema name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a data manipulation statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInDataManipulation() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a procedure call statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInProcedureCalls() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a table definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInTableDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in an index definition? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInIndexDefinitions() throws SQLException + { + return false; + } + + /** + * Can a catalog name be used in a privilege definition statement? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException + { + return false; + } + + /** + * We support cursors for gets only it seems. I dont see a method + * to get a positioned delete. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedDelete() throws SQLException + { + return false; // For now... + } + + /** + * Is positioned UPDATE supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsPositionedUpdate() throws SQLException + { + return false; // For now... + } + + public boolean supportsSelectForUpdate() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsStoredProcedures() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInComparisons() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInExists() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInIns() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsSubqueriesInQuantifieds() throws SQLException + { + // XXX-Not Implemented + return false; + } + + public boolean supportsCorrelatedSubqueries() throws SQLException + { + // XXX-Not Implemented + return false; + } + + /** + * Is SQL UNION supported? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnion() throws SQLException + { + return false; + } + + /** + * Is SQL UNION ALL supported? Nope. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsUnionAll() throws SQLException + { + return false; + } + + /** + * In PostgreSQL, Cursors are only open within transactions. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossCommit() throws SQLException + { + return false; + } + + /** + * Do we support open cursors across multiple transactions? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenCursorsAcrossRollback() throws SQLException + { + return false; + } + + /** + * Can statements remain open across commits? They may, but + * this driver cannot guarentee that. In further reflection. + * we are talking a Statement object jere, so the answer is + * yes, since the Statement is only a vehicle to ExecSQL() + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossCommit() throws SQLException + { + return true; + } + + /** + * Can statements remain open across rollbacks? They may, but + * this driver cannot guarentee that. In further contemplation, + * we are talking a Statement object here, so the answer is yes, + * since the Statement is only a vehicle to ExecSQL() in Connection + * + * @return true if they always remain open; false otherwise + * @exception SQLException if a database access error occurs + */ + public boolean supportsOpenStatementsAcrossRollback() throws SQLException + { + return true; + } + + /** + * How many hex characters can you have in an inline binary literal + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxBinaryLiteralLength() throws SQLException + { + return 0; // For now... + } + + /** + * What is the maximum length for a character literal + * I suppose it is 8190 (8192 - 2 for the quotes) + * + * @return the max literal length + * @exception SQLException if a database access error occurs + */ + public int getMaxCharLiteralLength() throws SQLException + { + return 8190; + } + + /** + * Whats the limit on column name length. The description of + * pg_class would say '32' (length of pg_class.relname) - we + * should probably do a query for this....but.... + * + * @return the maximum column name length + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of columns in a "GROUP BY" clause? + * + * @return the max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInGroupBy() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns allowed in an index? + * 6.0 only allowed one column, but 6.1 introduced multi-column + * indices, so, theoretically, its all of them. + * + * @return max number of columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInIndex() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What's the maximum number of columns in an "ORDER BY clause? + * Theoretically, all of them! + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInOrderBy() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What is the maximum number of columns in a "SELECT" list? + * Theoretically, all of them! + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInSelect() throws SQLException + { + return getMaxColumnsInTable(); + } + + /** + * What is the maximum number of columns in a table? From the + * create_table(l) manual page... + * + * "The new class is created as a heap with no initial data. A + * class can have no more than 1600 attributes (realistically, + * this is limited by the fact that tuple sizes must be less than + * 8192 bytes)..." + * + * @return the max columns + * @exception SQLException if a database access error occurs + */ + public int getMaxColumnsInTable() throws SQLException + { + return 1600; + } + + /** + * How many active connection can we have at a time to this + * database? Well, since it depends on postmaster, which just + * does a listen() followed by an accept() and fork(), its + * basically very high. Unless the system runs out of processes, + * it can be 65535 (the number of aux. ports on a TCP/IP system). + * I will return 8192 since that is what even the largest system + * can realistically handle, + * + * @return the maximum number of connections + * @exception SQLException if a database access error occurs + */ + public int getMaxConnections() throws SQLException + { + return 8192; + } + + /** + * What is the maximum cursor name length (the same as all + * the other F***** identifiers!) + * + * @return max cursor name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxCursorNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum length of an index (in bytes)? Now, does + * the spec. mean name of an index (in which case its 32, the + * same as a table) or does it mean length of an index element + * (in which case its 8192, the size of a row) or does it mean + * the number of rows it can access (in which case it 2^32 - + * a 4 byte OID number)? I think its the length of an index + * element, personally, so Im setting it to 8192. + * + * @return max index length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxIndexLength() throws SQLException + { + return 8192; + } + + public int getMaxSchemaNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a procedure name? + * (length of pg_proc.proname used) - again, I really + * should do a query here to get it. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxProcedureNameLength() throws SQLException + { + return 32; + } + + public int getMaxCatalogNameLength() throws SQLException + { + // XXX-Not Implemented + return 0; + } + + /** + * What is the maximum length of a single row? (not including + * blobs). 8192 is defined in PostgreSQL. + * + * @return max row size in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxRowSize() throws SQLException + { + return 8192; + } + + /** + * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY + * blobs? We don't handle blobs yet + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException + { + return false; + } + + /** + * What is the maximum length of a SQL statement? + * + * @return max length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxStatementLength() throws SQLException + { + return 8192; + } + + /** + * How many active statements can we have open at one time to + * this database? Basically, since each Statement downloads + * the results as the query is executed, we can have many. However, + * we can only really have one statement per connection going + * at once (since they are executed serially) - so we return + * one. + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxStatements() throws SQLException + { + return 1; + } + + /** + * What is the maximum length of a table name? This was found + * from pg_class.relname length + * + * @return max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxTableNameLength() throws SQLException + { + return 32; + } + + /** + * What is the maximum number of tables that can be specified + * in a SELECT? Theoretically, this is the same number as the + * number of tables allowable. In practice tho, it is much smaller + * since the number of tables is limited by the statement, we + * return 1024 here - this is just a number I came up with (being + * the number of tables roughly of three characters each that you + * can fit inside a 8192 character buffer with comma separators). + * + * @return the maximum + * @exception SQLException if a database access error occurs + */ + public int getMaxTablesInSelect() throws SQLException + { + return 1024; + } + + /** + * What is the maximum length of a user name? Well, we generally + * use UNIX like user names in PostgreSQL, so I think this would + * be 8. However, showing the schema for pg_user shows a length + * for username of 32. + * + * @return the max name length in bytes + * @exception SQLException if a database access error occurs + */ + public int getMaxUserNameLength() throws SQLException + { + return 32; + } + + + /** + * What is the database's default transaction isolation level? We + * do not support this, so all transactions are SERIALIZABLE. + * + * @return the default isolation level + * @exception SQLException if a database access error occurs + * @see Connection + */ + public int getDefaultTransactionIsolation() throws SQLException + { + return Connection.TRANSACTION_SERIALIZABLE; + } + + /** + * Are transactions supported? If not, commit and rollback are noops + * and the isolation level is TRANSACTION_NONE. We do support + * transactions. + * + * @return true if transactions are supported + * @exception SQLException if a database access error occurs + */ + public boolean supportsTransactions() throws SQLException + { + return true; + } + + /** + * Does the database support the given transaction isolation level? + * We only support TRANSACTION_SERIALIZABLE + * + * @param level the values are defined in java.sql.Connection + * @return true if so + * @exception SQLException if a database access error occurs + * @see Connection + */ + public boolean supportsTransactionIsolationLevel(int level) throws SQLException + { + if (level == Connection.TRANSACTION_SERIALIZABLE) + return true; + else + return false; + } + + /** + * Are both data definition and data manipulation transactions + * supported? I checked it, and could not do a CREATE TABLE + * within a transaction, so I am assuming that we don't + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException + { + return false; + } + + /** + * Are only data manipulation statements withing a transaction + * supported? + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean supportsDataManipulationTransactionsOnly() throws SQLException + { + return true; + } + + /** + * Does a data definition statement within a transaction force + * the transaction to commit? I think this means something like: + * + * CREATE TABLE T (A INT); + * INSERT INTO T (A) VALUES (2); + * BEGIN; + * UPDATE T SET A = A + 1; + * CREATE TABLE X (A INT); + * SELECT A FROM T INTO X; + * COMMIT; + * + * does the CREATE TABLE call cause a commit? The answer is no. + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionCausesTransactionCommit() throws SQLException + { + return false; + } + + /** + * Is a data definition statement within a transaction ignored? + * It seems to be (from experiment in previous method) + * + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean dataDefinitionIgnoredInTransactions() throws SQLException + { + return false; + } + + /** + * Get a description of stored procedures available in a catalog + * + * Only procedure descriptions matching the schema and procedure + * name criteria are returned. They are ordered by PROCEDURE_SCHEM + * and PROCEDURE_NAME + * + * Each procedure description has the following columns: + * PROCEDURE_CAT String => procedure catalog (may be null) + * PROCEDURE_SCHEM String => procedure schema (may be null) + * PROCEDURE_NAME String => procedure name + * Field 4 reserved (make it null) + * Field 5 reserved (make it null) + * Field 6 reserved (make it null) + * REMARKS String => explanatory comment on the procedure + * PROCEDURE_TYPE short => kind of procedure + * * procedureResultUnknown - May return a result + * * procedureNoResult - Does not return a result + * * procedureReturnsResult - Returns a result + * + * @param catalog - a catalog name; "" retrieves those without a + * catalog; null means drop catalog name from criteria + * @param schemaParrern - a schema name pattern; "" retrieves those + * without a schema - we ignore this parameter + * @param procedureNamePattern - a procedure name pattern + * @return ResultSet - each row is a procedure description + * @exception SQLException if a database access error occurs + */ + static final int iVarcharOid = 1043; // This is the OID for a varchar() + static final int iInt2Oid = 21; // This is the OID for an int2 + public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException + { + // the field descriptors for the new ResultSet + 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"); + while (r.next()) + { + byte[][] tuple = new byte[8][0]; - /** - * Does this driver support the Extended (Level 2) ODBC SQL - * grammar. We don't conform to the Core (Level 1), so we can't - * conform to the Extended SQL Grammar. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsExtendedSQLGrammar() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 entry level SQL grammar? - * All JDBC Compliant drivers must return true. I think we have - * to support outer joins for this to be true. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92EntryLevelSQL() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 intermediate level SQL - * grammar? Anyone who does not support Entry level cannot support - * Intermediate level. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92IntermediateSQL() throws SQLException - { - return false; - } - - /** - * Does this driver support the ANSI-92 full SQL grammar? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsANSI92FullSQL() throws SQLException - { - return false; - } - - /** - * Is the SQL Integrity Enhancement Facility supported? - * I haven't seen this mentioned anywhere, so I guess not - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsIntegrityEnhancementFacility() throws SQLException - { - return false; - } - - /** - * Is some form of outer join supported? From my knowledge, nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOuterJoins() throws SQLException - { - return false; - } - - /** - * Are full nexted outer joins supported? Well, we dont support any - * form of outer join, so this is no as well - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsFullOuterJoins() throws SQLException - { - return false; - } - - /** - * Is there limited support for outer joins? (This will be true if - * supportFullOuterJoins is true) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsLimitedOuterJoins() throws SQLException - { - return false; - } - - /** - * What is the database vendor's preferred term for "schema" - well, - * we do not provide support for schemas, so lets just use that - * term. - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getSchemaTerm() throws SQLException - { - return new String("Schema"); - } - - /** - * What is the database vendor's preferred term for "procedure" - - * I kind of like "Procedure" myself. - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getProcedureTerm() throws SQLException - { - return new String("Procedure"); - } - - /** - * What is the database vendor's preferred term for "catalog"? - - * we dont have a preferred term, so just use Catalog - * - * @return the vendor term - * @exception SQLException if a database access error occurs - */ - public String getCatalogTerm() throws SQLException - { - return new String("Catalog"); - } - - /** - * Does a catalog appear at the start of a qualified table name? - * (Otherwise it appears at the end). - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isCatalogAtStart() throws SQLException - { - return false; - } - - /** - * What is the Catalog separator. Hmmm....well, I kind of like - * a period (so we get catalog.table definitions). - I don't think - * PostgreSQL supports catalogs anyhow, so it makes no difference. - * - * @return the catalog separator string - * @exception SQLException if a database access error occurs - */ - public String getCatalogSeparator() throws SQLException - { - return new String("."); - } - - /** - * Can a schema name be used in a data manipulation statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInDataManipulation() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in a procedure call statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInProcedureCalls() throws SQLException - { - return false; - } - - /** - * Can a schema be used in a table definition statement? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInTableDefinitions() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in an index definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInIndexDefinitions() throws SQLException - { - return false; - } - - /** - * Can a schema name be used in a privilege definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a data manipulation statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInDataManipulation() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a procedure call statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInProcedureCalls() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a table definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInTableDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in an index definition? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInIndexDefinitions() throws SQLException - { - return false; - } - - /** - * Can a catalog name be used in a privilege definition statement? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException - { - return false; - } - - /** - * We support cursors for gets only it seems. I dont see a method - * to get a positioned delete. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsPositionedDelete() throws SQLException - { - return false; // For now... - } - - /** - * Is positioned UPDATE supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsPositionedUpdate() throws SQLException - { - return false; // For now... - } - - public boolean supportsSelectForUpdate() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsStoredProcedures() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsSubqueriesInComparisons() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsSubqueriesInExists() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsSubqueriesInIns() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsSubqueriesInQuantifieds() throws SQLException - { - // XXX-Not Implemented - } - - public boolean supportsCorrelatedSubqueries() throws SQLException - { - // XXX-Not Implemented - } - - /** - * Is SQL UNION supported? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnion() throws SQLException - { - return false; - } - - /** - * Is SQL UNION ALL supported? Nope. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsUnionAll() throws SQLException - { - return false; - } - - /** - * In PostgreSQL, Cursors are only open within transactions. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenCursorsAcrossCommit() throws SQLException - { - return false; - } - - /** - * Do we support open cursors across multiple transactions? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenCursorsAcrossRollback() throws SQLException - { - return false; - } - - /** - * Can statements remain open across commits? They may, but - * this driver cannot guarentee that. In further reflection. - * we are talking a Statement object jere, so the answer is - * yes, since the Statement is only a vehicle to ExecSQL() - * - * @return true if they always remain open; false otherwise - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenStatementsAcrossCommit() throws SQLException - { - return true; - } - - /** - * Can statements remain open across rollbacks? They may, but - * this driver cannot guarentee that. In further contemplation, - * we are talking a Statement object here, so the answer is yes, - * since the Statement is only a vehicle to ExecSQL() in Connection - * - * @return true if they always remain open; false otherwise - * @exception SQLException if a database access error occurs - */ - public boolean supportsOpenStatementsAcrossRollback() throws SQLException - { - return true; - } - - /** - * How many hex characters can you have in an inline binary literal - * - * @return the max literal length - * @exception SQLException if a database access error occurs - */ - public int getMaxBinaryLiteralLength() throws SQLException - { - return 0; // For now... - } - - /** - * What is the maximum length for a character literal - * I suppose it is 8190 (8192 - 2 for the quotes) - * - * @return the max literal length - * @exception SQLException if a database access error occurs - */ - public int getMaxCharLiteralLength() throws SQLException - { - return 8190; - } - - /** - * Whats the limit on column name length. The description of - * pg_class would say '32' (length of pg_class.relname) - we - * should probably do a query for this....but.... - * - * @return the maximum column name length - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum number of columns in a "GROUP BY" clause? - * - * @return the max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInGroupBy() throws SQLException - { - return getMaxColumnsInTable(); - } - - /** - * What's the maximum number of columns allowed in an index? - * 6.0 only allowed one column, but 6.1 introduced multi-column - * indices, so, theoretically, its all of them. - * - * @return max number of columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInIndex() throws SQLException - { - return getMaxColumnsInTable(); - } - - /** - * What's the maximum number of columns in an "ORDER BY clause? - * Theoretically, all of them! - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInOrderBy() throws SQLException - { - return getMaxColumnsInTable(); - } - - /** - * What is the maximum number of columns in a "SELECT" list? - * Theoretically, all of them! - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInSelect() throws SQLException - { - return getMaxColumnsInTable(); - } - - /** - * What is the maximum number of columns in a table? From the - * create_table(l) manual page... - * - * "The new class is created as a heap with no initial data. A - * class can have no more than 1600 attributes (realistically, - * this is limited by the fact that tuple sizes must be less than - * 8192 bytes)..." - * - * @return the max columns - * @exception SQLException if a database access error occurs - */ - public int getMaxColumnsInTable() throws SQLException - { - return 1600; - } - - /** - * How many active connection can we have at a time to this - * database? Well, since it depends on postmaster, which just - * does a listen() followed by an accept() and fork(), its - * basically very high. Unless the system runs out of processes, - * it can be 65535 (the number of aux. ports on a TCP/IP system). - * I will return 8192 since that is what even the largest system - * can realistically handle, - * - * @return the maximum number of connections - * @exception SQLException if a database access error occurs - */ - public int getMaxConnections() throws SQLException - { - return 8192; - } - - /** - * What is the maximum cursor name length (the same as all - * the other F***** identifiers!) - * - * @return max cursor name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxCursorNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum length of an index (in bytes)? Now, does - * the spec. mean name of an index (in which case its 32, the - * same as a table) or does it mean length of an index element - * (in which case its 8192, the size of a row) or does it mean - * the number of rows it can access (in which case it 2^32 - - * a 4 byte OID number)? I think its the length of an index - * element, personally, so Im setting it to 8192. - * - * @return max index length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxIndexLength() throws SQLException - { - return 8192; - } - - public int getMaxSchemaNameLength() throws SQLException - { - // XXX-Not Implemented - } - - /** - * What is the maximum length of a procedure name? - * (length of pg_proc.proname used) - again, I really - * should do a query here to get it. - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxProcedureNameLength() throws SQLException - { - return 32; - } - - public int getMaxCatalogNameLength() throws SQLException - { - // XXX-Not Implemented - } - - /** - * What is the maximum length of a single row? (not including - * blobs). 8192 is defined in PostgreSQL. - * - * @return max row size in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxRowSize() throws SQLException - { - return 8192; - } - - /** - * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY - * blobs? We don't handle blobs yet - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean doesMaxRowSizeIncludeBlobs() throws SQLException - { - return false; - } - - /** - * What is the maximum length of a SQL statement? - * - * @return max length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxStatementLength() throws SQLException - { - return 8192; - } - - /** - * How many active statements can we have open at one time to - * this database? Basically, since each Statement downloads - * the results as the query is executed, we can have many. However, - * we can only really have one statement per connection going - * at once (since they are executed serially) - so we return - * one. - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxStatements() throws SQLException - { - return 1; - } - - /** - * What is the maximum length of a table name? This was found - * from pg_class.relname length - * - * @return max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxTableNameLength() throws SQLException - { - return 32; - } - - /** - * What is the maximum number of tables that can be specified - * in a SELECT? Theoretically, this is the same number as the - * number of tables allowable. In practice tho, it is much smaller - * since the number of tables is limited by the statement, we - * return 1024 here - this is just a number I came up with (being - * the number of tables roughly of three characters each that you - * can fit inside a 8192 character buffer with comma separators). - * - * @return the maximum - * @exception SQLException if a database access error occurs - */ - public int getMaxTablesInSelect() throws SQLException - { - return 1024; - } - - /** - * What is the maximum length of a user name? Well, we generally - * use UNIX like user names in PostgreSQL, so I think this would - * be 8. However, showing the schema for pg_user shows a length - * for username of 32. - * - * @return the max name length in bytes - * @exception SQLException if a database access error occurs - */ - public int getMaxUserNameLength() throws SQLException - { - return 32; - } - + String name = r.getString(1); + remarks = new String("no remarks"); + boolean retset = r.getBoolean(2); - /** - * What is the database's default transaction isolation level? We - * do not support this, so all transactions are SERIALIZABLE. - * - * @return the default isolation level - * @exception SQLException if a database access error occurs - * @see Connection - */ - public int getDefaultTransactionIsolation() throws SQLException - { - return Connection.TRANSACTION_SERIALIZABLE; - } - - /** - * Are transactions supported? If not, commit and rollback are noops - * and the isolation level is TRANSACTION_NONE. We do support - * transactions. - * - * @return true if transactions are supported - * @exception SQLException if a database access error occurs - */ - public boolean supportsTransactions() throws SQLException - { - return true; - } - - /** - * Does the database support the given transaction isolation level? - * We only support TRANSACTION_SERIALIZABLE - * - * @param level the values are defined in java.sql.Connection - * @return true if so - * @exception SQLException if a database access error occurs - * @see Connection - */ - public boolean supportsTransactionIsolationLevel(int level) throws SQLException - { - if (level == Connection.TRANSACTION_SERIALIZABLE) - return true; - else - return false; - } - - /** - * Are both data definition and data manipulation transactions - * supported? I checked it, and could not do a CREATE TABLE - * within a transaction, so I am assuming that we don't - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException - { - return false; - } - - /** - * Are only data manipulation statements withing a transaction - * supported? - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean supportsDataManipulationTransactionsOnly() throws SQLException - { - return true; - } - - /** - * Does a data definition statement within a transaction force - * the transaction to commit? I think this means something like: - * - * CREATE TABLE T (A INT); - * INSERT INTO T (A) VALUES (2); - * BEGIN; - * UPDATE T SET A = A + 1; - * CREATE TABLE X (A INT); - * SELECT A FROM T INTO X; - * COMMIT; - * - * does the CREATE TABLE call cause a commit? The answer is no. - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionCausesTransactionCommit() throws SQLException - { - return false; - } - - /** - * Is a data definition statement within a transaction ignored? - * It seems to be (from experiment in previous method) - * - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean dataDefinitionIgnoredInTransactions() throws SQLException - { - return false; - } - - /** - * Get a description of stored procedures available in a catalog - * - * Only procedure descriptions matching the schema and procedure - * name criteria are returned. They are ordered by PROCEDURE_SCHEM - * and PROCEDURE_NAME - * - * Each procedure description has the following columns: - * PROCEDURE_CAT String => procedure catalog (may be null) - * PROCEDURE_SCHEM String => procedure schema (may be null) - * PROCEDURE_NAME String => procedure name - * Field 4 reserved (make it null) - * Field 5 reserved (make it null) - * Field 6 reserved (make it null) - * REMARKS String => explanatory comment on the procedure - * PROCEDURE_TYPE short => kind of procedure - * * procedureResultUnknown - May return a result - * * procedureNoResult - Does not return a result - * * procedureReturnsResult - Returns a result - * - * @param catalog - a catalog name; "" retrieves those without a - * catalog; null means drop catalog name from criteria - * @param schemaParrern - a schema name pattern; "" retrieves those - * without a schema - we ignore this parameter - * @param procedureNamePattern - a procedure name pattern - * @return ResultSet - each row is a procedure description - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException - { - Field[] f = new Field[8]; // the field descriptors for the new ResultSet - static final int iVarcharOid = 1043; // This is the OID for a varchar() - static final int iInt2Oid = 21; // This is the OID for an int2 - ResultSet r; // ResultSet for the SQL query that we need to do - Vector v; // The new ResultSet tuple stuff - String remarks = new String("no remarks"); - - Field[0] = new Field(conn, new String("PROCEDURE_CAT"), iVarcharOid, 32); - Field[1] = new Field(conn, new String("PROCEDURE_SCHEM"), iVarcharOid, 32); - Field[2] = new Field(conn, new String("PROCEDURE_NAME"), iVarcharOid, 32); - Field[3] = null; - Field[4] = null; - Field[5] = null; - Field[6] = new Field(conn, new String("REMARKS"), iVarcharOid, 8192); - Field[7] = new Field(conn, new String("PROCEDURE_TYPE"), iInt2Oid, 2); - r = conn.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"); - while (r.next()) - { - byte[][] tuple = new byte[8][0]; - - String name = r.getString(1); - String remarks = new String("no remarks"); - boolean retset = r.getBoolean(2); - - byte[0] = null; // Catalog name - byte[1] = null; // Schema name - byte[2] = name.getBytes(); // Procedure name - byte[3] = null; // Reserved - byte[4] = null; // Reserved - byte[5] = null; // Reserved - byte[6] = remarks.getBytes(); // Remarks - if (retset) - byte[7] = procedureReturnsResult; - else - byte[7] = procedureNoResult; - v.addElement(byte); - } - return new ResultSet(conn, f, v, "OK", 1); - } - - public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getSchemas() throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getCatalogs() throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getTableTypes() throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getTypeInfo() throws SQLException - { - // XXX-Not Implemented - } - - public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException - { - // XXX-Not Implemented - } + 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; + else + tuple[7][0] = (byte)java.sql.DatabaseMetaData.procedureNoResult; + v.addElement(tuple); + } + return new ResultSet(connection, f, v, "OK", 1); + } + + public java.sql.ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException + { + // XXX-Not Implemented + return null; + } + + public java.sql.ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]) throws SQLException + { + return connection.createStatement().executeQuery("SELECT '' as TABLE_CAT,'' AS TABLE_SCHEM,relname AS TABLE_NAME,'TABLE' AS TABLE_TYPE,'' AS REMARKS FROM pg_class WHERE relkind = 'r' and relname !~ '^pg_' and relname !~ '^Inv' and relname ~ '"+tableNamePattern+"' ORDER BY TABLE_NAME"); + } + + /** + * Get the schema names available in this database. The results + * are ordered by schema name. + * + *

The schema column is: + *

    + *
  1. TABLE_SCHEM String => schema name + *
+ * + * @return ResultSet each row has a single String column that is a + * schema name + */ + public java.sql.ResultSet getSchemas() throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get the catalog names available in this database. The results + * are ordered by catalog name. + * + *

The catalog column is: + *

    + *
  1. TABLE_CAT String => catalog name + *
+ * + * @return ResultSet each row has a single String column that is a + * catalog name + */ + // We don't use catalog names, so this returns a single catalog + public java.sql.ResultSet getCatalogs() throws SQLException + { + return connection.createStatement().executeQuery("SELECT '' as TABLE_CAT"); + } + + /** + * Get the table types available in this database. The results + * are ordered by table type. + * + *

The table type is: + *

    + *
  1. TABLE_TYPE String => table type. Typical types are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", + * "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + *
+ * + * @return ResultSet each row has a single String column that is a + * table type + */ + public java.sql.ResultSet getTableTypes() throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of table columns available in a catalog. + * + *

Only column descriptions matching the catalog, schema, table + * and column name criteria are returned. They are ordered by + * TABLE_SCHEM, TABLE_NAME and ORDINAL_POSITION. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. DATA_TYPE short => SQL type from java.sql.Types + *
  6. TYPE_NAME String => Data source dependent type name + *
  7. COLUMN_SIZE int => column size. For char or date + * types this is the maximum number of characters, for numeric or + * decimal types this is precision. + *
  8. BUFFER_LENGTH is not used. + *
  9. DECIMAL_DIGITS int => the number of fractional digits + *
  10. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + *
  11. NULLABLE int => is NULL allowed? + *
      + *
    • columnNoNulls - might not allow NULL values + *
    • columnNullable - definitely allows NULL values + *
    • columnNullableUnknown - nullability unknown + *
    + *
  12. REMARKS String => comment describing column (may be null) + *
  13. COLUMN_DEF String => default value (may be null) + *
  14. SQL_DATA_TYPE int => unused + *
  15. SQL_DATETIME_SUB int => unused + *
  16. CHAR_OCTET_LENGTH int => for char types the + * maximum number of bytes in the column + *
  17. ORDINAL_POSITION int => index of column in table + * (starting at 1) + *
  18. IS_NULLABLE String => "NO" means column definitely + * does not allow NULL values; "YES" means the column might + * allow NULL values. An empty string means nobody knows. + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException + { + // XXX-Not Implemented + // PM: this will be implemented, as soon as I sort out how to convert the + // code from the other driver (private note: look at getProcedures() ) + return null; + } + + /** + * Get a description of the access rights for a table's columns. + * + *

Only privileges matching the column name criteria are + * returned. They are ordered by COLUMN_NAME and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param columnNamePattern a column name pattern + * @return ResultSet each row is a column privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of the access rights for each table available + * in a catalog. + * + *

Only privileges matching the schema and table name + * criteria are returned. They are ordered by TABLE_SCHEM, + * TABLE_NAME, and PRIVILEGE. + * + *

Each privilige description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. GRANTOR => grantor of access (may be null) + *
  6. GRANTEE String => grantee of access + *
  7. PRIVILEGE String => name of access (SELECT, + * INSERT, UPDATE, REFRENCES, ...) + *
  8. IS_GRANTABLE String => "YES" if grantee is permitted + * to grant to others; "NO" if not; null if unknown + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schemaPattern a schema name pattern; "" retrieves those + * without a schema + * @param tableNamePattern a table name pattern + * @return ResultSet each row is a table privilege description + * @see #getSearchStringEscape + */ + public java.sql.ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's optimal set of columns that + * uniquely identifies a row. They are ordered by SCOPE. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => actual scope of result + *
      + *
    • bestRowTemporary - very temporary, while using row + *
    • bestRowTransaction - valid for remainder of current transaction + *
    • bestRowSession - valid for remainder of current session + *
    + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => not used + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • bestRowUnknown - may or may not be pseudo column + *
    • bestRowNotPseudo - is NOT a pseudo column + *
    • bestRowPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @param scope the scope of interest; use same values as SCOPE + * @param nullable include columns that are nullable? + * @return ResultSet each row is a column description + */ + public java.sql.ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's columns that are automatically + * updated when any value in a row is updated. They are + * unordered. + * + *

Each column description has the following columns: + *

    + *
  1. SCOPE short => is not used + *
  2. COLUMN_NAME String => column name + *
  3. DATA_TYPE short => SQL data type from java.sql.Types + *
  4. TYPE_NAME String => Data source dependent type name + *
  5. COLUMN_SIZE int => precision + *
  6. BUFFER_LENGTH int => length of column value in bytes + *
  7. DECIMAL_DIGITS short => scale + *
  8. PSEUDO_COLUMN short => is this a pseudo column + * like an Oracle ROWID + *
      + *
    • versionColumnUnknown - may or may not be pseudo column + *
    • versionColumnNotPseudo - is NOT a pseudo column + *
    • versionColumnPseudo - is a pseudo column + *
    + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name; "" retrieves those without a schema + * @param table a table name + * @return ResultSet each row is a column description + */ + public java.sql.ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's primary key columns. They + * are ordered by COLUMN_NAME. + * + *

Each column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. COLUMN_NAME String => column name + *
  5. KEY_SEQ short => sequence number within primary key + *
  6. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + */ + public java.sql.ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException + { + return connection.createStatement().executeQuery("SELECT " + + "'' as TABLE_CAT," + + "'' AS TABLE_SCHEM," + + "bc.relname AS TABLE_NAME," + + "ic.relname AS COLUMN_NAME," + + "'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 " + + " WHERE relkind = 'r' " + // -- not indices + " and bc.relname ~ '"+table+"'" + + " and i.indrelid = bc.oid" + + " and i.indexrelid = ic.oid" + + " and i.indkey[0] = a.attnum" + + " and i.indproc = '0'::oid" + + " and a.attrelid = bc.oid" + + " ORDER BY TABLE_NAME, COLUMN_NAME;" + ); + } + + /** + * Get a description of the primary key columns that are + * referenced by a table's foreign key columns (the primary keys + * imported by a table). They are ordered by PKTABLE_CAT, + * PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ. + * + *

Each primary key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog + * being imported (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema + * being imported (may be null) + *
  3. PKTABLE_NAME String => primary key table name + * being imported + *
  4. PKCOLUMN_NAME String => primary key column name + * being imported + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + *
  8. FKCOLUMN_NAME String => foreign key column name + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key name (may be null) + *
  13. PK_NAME String => primary key name (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a primary key column description + * @see #getExportedKeys + */ + public java.sql.ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a foreign key columns that reference a + * table's primary key columns (the foreign keys exported by a + * table). They are ordered by FKTABLE_CAT, FKTABLE_SCHEM, + * FKTABLE_NAME, and KEY_SEQ. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of the foreign key columns in the foreign key + * table that reference the primary key columns of the primary key + * table (describe how one table imports another's key.) This + * should normally return a single foreign key/primary key pair + * (most tables only import a foreign key from a table once.) They + * are ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and + * KEY_SEQ. + * + *

Each foreign key column description has the following columns: + *

    + *
  1. PKTABLE_CAT String => primary key table catalog (may be null) + *
  2. PKTABLE_SCHEM String => primary key table schema (may be null) + *
  3. PKTABLE_NAME String => primary key table name + *
  4. PKCOLUMN_NAME String => primary key column name + *
  5. FKTABLE_CAT String => foreign key table catalog (may be null) + * being exported (may be null) + *
  6. FKTABLE_SCHEM String => foreign key table schema (may be null) + * being exported (may be null) + *
  7. FKTABLE_NAME String => foreign key table name + * being exported + *
  8. FKCOLUMN_NAME String => foreign key column name + * being exported + *
  9. KEY_SEQ short => sequence number within foreign key + *
  10. UPDATE_RULE short => What happens to + * foreign key when primary is updated: + *
      + *
    • importedKeyCascade - change imported key to agree + * with primary key update + *
    • importedKeyRestrict - do not allow update of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been updated + *
    + *
  11. DELETE_RULE short => What happens to + * the foreign key when primary is deleted. + *
      + *
    • importedKeyCascade - delete rows that import a deleted key + *
    • importedKeyRestrict - do not allow delete of primary + * key if it has been imported + *
    • importedKeySetNull - change imported key to NULL if + * its primary key has been deleted + *
    + *
  12. FK_NAME String => foreign key identifier (may be null) + *
  13. PK_NAME String => primary key identifier (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those + * without a schema + * @param table a table name + * @return ResultSet each row is a foreign key column description + * @see #getImportedKeys + */ + public java.sql.ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of all the standard SQL types supported by + * this database. They are ordered by DATA_TYPE and then by how + * closely the data type maps to the corresponding JDBC SQL type. + * + *

Each type description has the following columns: + *

    + *
  1. TYPE_NAME String => Type name + *
  2. DATA_TYPE short => SQL data type from java.sql.Types + *
  3. PRECISION int => maximum precision + *
  4. LITERAL_PREFIX String => prefix used to quote a literal + * (may be null) + *
  5. LITERAL_SUFFIX String => suffix used to quote a literal + (may be null) + *
  6. CREATE_PARAMS String => parameters used in creating + * the type (may be null) + *
  7. NULLABLE short => can you use NULL for this type? + *
      + *
    • typeNoNulls - does not allow NULL values + *
    • typeNullable - allows NULL values + *
    • typeNullableUnknown - nullability unknown + *
    + *
  8. CASE_SENSITIVE boolean=> is it case sensitive? + *
  9. SEARCHABLE short => can you use "WHERE" based on this type: + *
      + *
    • typePredNone - No support + *
    • typePredChar - Only supported with WHERE .. LIKE + *
    • typePredBasic - Supported except for WHERE .. LIKE + *
    • typeSearchable - Supported for all WHERE .. + *
    + *
  10. UNSIGNED_ATTRIBUTE boolean => is it unsigned? + *
  11. FIXED_PREC_SCALE boolean => can it be a money value? + *
  12. AUTO_INCREMENT boolean => can it be used for an + * auto-increment value? + *
  13. LOCAL_TYPE_NAME String => localized version of type name + * (may be null) + *
  14. MINIMUM_SCALE short => minimum scale supported + *
  15. MAXIMUM_SCALE short => maximum scale supported + *
  16. SQL_DATA_TYPE int => unused + *
  17. SQL_DATETIME_SUB int => unused + *
  18. NUM_PREC_RADIX int => usually 2 or 10 + *
+ * + * @return ResultSet each row is a SQL type description + */ + public java.sql.ResultSet getTypeInfo() throws SQLException + { + // XXX-Not Implemented + return null; + } + + /** + * Get a description of a table's indices and statistics. They are + * ordered by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION. + * + *

Each index column description has the following columns: + *

    + *
  1. TABLE_CAT String => table catalog (may be null) + *
  2. TABLE_SCHEM String => table schema (may be null) + *
  3. TABLE_NAME String => table name + *
  4. NON_UNIQUE boolean => Can index values be non-unique? + * false when TYPE is tableIndexStatistic + *
  5. INDEX_QUALIFIER String => index catalog (may be null); + * null when TYPE is tableIndexStatistic + *
  6. INDEX_NAME String => index name; null when TYPE is + * tableIndexStatistic + *
  7. TYPE short => index type: + *
      + *
    • tableIndexStatistic - this identifies table statistics that are + * returned in conjuction with a table's index descriptions + *
    • tableIndexClustered - this is a clustered index + *
    • tableIndexHashed - this is a hashed index + *
    • tableIndexOther - this is some other style of index + *
    + *
  8. ORDINAL_POSITION short => column sequence number + * within index; zero when TYPE is tableIndexStatistic + *
  9. COLUMN_NAME String => column name; null when TYPE is + * tableIndexStatistic + *
  10. ASC_OR_DESC String => column sort sequence, "A" => ascending, + * "D" => descending, may be null if sort sequence is not supported; + * null when TYPE is tableIndexStatistic + *
  11. CARDINALITY int => When TYPE is tableIndexStatisic then + * this is the number of rows in the table; otherwise it is the + * number of unique values in the index. + *
  12. PAGES int => When TYPE is tableIndexStatisic then + * this is the number of pages used for the table, otherwise it + * is the number of pages used for the current index. + *
  13. FILTER_CONDITION String => Filter condition, if any. + * (may be null) + *
+ * + * @param catalog a catalog name; "" retrieves those without a catalog + * @param schema a schema name pattern; "" retrieves those without a schema + * @param table a table name + * @param unique when true, return only indices for unique values; + * when false, return indices regardless of whether unique or not + * @param approximate when true, result is allowed to reflect approximate + * or out of data values; when false, results are requested to be + * accurate + * @return ResultSet each row is an index column description + */ + public java.sql.ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException + { + // XXX-Not Implemented + return null; + } } diff --git a/src/interfaces/jdbc/postgresql/Driver.java b/src/interfaces/jdbc/postgresql/Driver.java index 055a65601a..674c96fc66 100644 --- a/src/interfaces/jdbc/postgresql/Driver.java +++ b/src/interfaces/jdbc/postgresql/Driver.java @@ -2,12 +2,8 @@ package postgresql; import java.sql.*; import java.util.*; -import postgresql.*; /** - * @version 1.0 15-APR-1997 - * @author Adrian Hall - * * The Java SQL framework allows for multiple database drivers. Each * driver should supply a class that implements the Driver interface * @@ -28,242 +24,274 @@ import postgresql.*; */ public class Driver implements java.sql.Driver { - - static - { - try - { - new Driver(); - } catch (SQLException e) { - e.printStackTrace(); - } + // These should be in sync with the backend that the driver was + // distributed with + static final int MAJORVERSION = 6; + static final int MINORVERSION = 2; + + static + { + try { + // moved the registerDriver from the constructor to here + // because some clients call the driver themselves (I know, as + // my early jdbc work did - and that was based on other examples). + // Placing it here, means that the driver is registered once only. + java.sql.DriverManager.registerDriver(new Driver()); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * Construct a new driver and register it with DriverManager + * + * @exception SQLException for who knows what! + */ + public Driver() throws SQLException + { + } + + /** + * Try to make a database connection to the given URL. The driver + * should return "null" if it realizes it is the wrong kind of + * driver to connect to the given URL. This will be common, as + * when the JDBC driverManager is asked to connect to a given URL, + * it passes the URL to each loaded driver in turn. + * + * The driver should raise an SQLException if it is the right driver + * to connect to the given URL, but has trouble connecting to the + * database. + * + * The java.util.Properties argument can be used to pass arbitrary + * string tag/value pairs as connection arguments. Normally, at least + * "user" and "password" properties should be included in the + * properties. + * + * Our protocol takes the form: + *
+   *	jdbc:postgresql://host:port/database
+   * 
+ * + * @param url the URL of the database to connect to + * @param info a list of arbitrary tag/value pairs as connection + * arguments + * @return a connection to the URL or null if it isnt us + * @exception SQLException if a database access error occurs + * @see java.sql.Driver#connect + */ + public java.sql.Connection connect(String url, Properties info) throws SQLException + { + if((props = parseURL(url,info))==null) + return null; + + return new Connection (host(), port(), props, database(), url, this); + } + + /** + * Returns true if the driver thinks it can open a connection to the + * given URL. Typically, drivers will return true if they understand + * the subprotocol specified in the URL and false if they don't. Our + * protocols start with jdbc:postgresql: + * + * @see java.sql.Driver#acceptsURL + * @param url the URL of the driver + * @return true if this driver accepts the given URL + * @exception SQLException if a database-access error occurs + * (Dont know why it would *shrug*) + */ + public boolean acceptsURL(String url) throws SQLException + { + if(parseURL(url,null)==null) + return false; + return true; + } + + /** + * The getPropertyInfo method is intended to allow a generic GUI + * tool to discover what properties it should prompt a human for + * in order to get enough information to connect to a database. + * Note that depending on the values the human has supplied so + * far, additional values may become necessary, so it may be necessary + * to iterate through several calls to getPropertyInfo + * + * @param url the Url of the database to connect to + * @param info a proposed list of tag/value pairs that will be sent on + * connect open. + * @return An array of DriverPropertyInfo objects describing + * possible properties. This array may be an empty array if + * no properties are required + * @exception SQLException if a database-access error occurs + * @see java.sql.Driver#getPropertyInfo + */ + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException + { + return null; // We don't need anything except + // the username, which is a default + } + + /** + * Gets the drivers major version number + * + * @return the drivers major version number + */ + public int getMajorVersion() + { + return MAJORVERSION; + } + + /** + * Get the drivers minor version number + * + * @return the drivers minor version number + */ + public int getMinorVersion() + { + return MINORVERSION; + } + + /** + * Report whether the driver is a genuine JDBC compliant driver. A + * driver may only report "true" here if it passes the JDBC compliance + * tests, otherwise it is required to return false. JDBC compliance + * requires full support for the JDBC API and full support for SQL 92 + * Entry Level. + */ + public boolean jdbcCompliant() + { + return false; + } + + private Properties props; + + static private String[] protocols = { "jdbc","postgresql" }; + + /** + * Constructs a new DriverURL, splitting the specified URL into its + * component parts + */ + Properties parseURL(String url,Properties defaults) throws SQLException + { + int state = -1; + Properties urlProps = new Properties(defaults); + String key = new String(); + String value = new String(); + + StringTokenizer st = new StringTokenizer(url, ":/;=&?", true); + for (int count = 0; (st.hasMoreTokens()); count++) { + String token = st.nextToken(); + + // PM June 29 1997 + // Added this, to help me understand how this works. + // Unless you want each token to be processed, leave this commented out + // but don't delete it. + //DriverManager.println("wellFormedURL: state="+state+" count="+count+" token='"+token+"'"); + + // PM Aug 2 1997 - Modified to allow multiple backends + if (count <= 3) { + if ((count % 2) == 1 && token.equals(":")) + ; + else if((count % 2) == 0) { + boolean found=(count==0)?true:false; + for(int tmp=0;tmp 0) { + urlProps.put("Protocol",token); + found=true; + } + } + } + + if(found == false) + return null; + } else return null; + } + else if (count > 3) { + if (count == 4 && token.equals("/")) state = 0; + else if (count == 4) { + urlProps.put("PGDBNAME", token); + state = -2; } - - /** - * Construct a new driver and register it with DriverManager - * - * @exception SQLException for who knows what! - */ - public Driver() throws SQLException - { - java.sql.DriverManager.registerDriver(this); + else if (count == 5 && state == 0 && token.equals("/")) + state = 1; + else if (count == 5 && state == 0) + return null; + else if (count == 6 && state == 1) + urlProps.put("PGHOST", token); + else if (count == 7 && token.equals(":")) state = 2; + else if (count == 8 && state == 2) { + try { + Integer portNumber = Integer.decode(token); + urlProps.put("PGPORT", portNumber.toString()); + } catch (Exception e) { + return null; + } } - - /** - * Try to make a database connection to the given URL. The driver - * should return "null" if it realizes it is the wrong kind of - * driver to connect to the given URL. This will be common, as - * when the JDBC driverManager is asked to connect to a given URL, - * it passes the URL to each loaded driver in turn. - * - * The driver should raise an SQLException if it is the right driver - * to connect to the given URL, but has trouble connecting to the - * database. - * - * The java.util.Properties argument can be used to pass arbitrary - * string tag/value pairs as connection arguments. Normally, at least - * "user" and "password" properties should be included in the - * properties. - * - * Our protocol takes the form: - *
-	 *	jdbc:postgresql://host:port/database
-	 * 
- * - * @param url the URL of the database to connect to - * @param info a list of arbitrary tag/value pairs as connection - * arguments - * @return a connection to the URL or null if it isnt us - * @exception SQLException if a database access error occurs - * @see java.sql.Driver#connect - */ - public java.sql.Connection connect(String url, Properties info) throws SQLException - { - DriverURL dr = new DriverURL(url); - int port; - - if (!(dr.protocol().equals("jdbc"))) - return null; - if (!(dr.subprotocol().equals("postgresql"))) - return null; - if (dr.host().equals("unknown")) - return null; - port = dr.port(); - if (port == -1) - port = 5432; // Default PostgreSQL port - return new Connection (dr.host(), port, info, dr.database(), url, this); + else if ((count == 7 || count == 9) && + (state == 1 || state == 2) && token.equals("/")) + state = -1; + else if (state == -1) { + urlProps.put("PGDBNAME", token); + state = -2; } - - /** - * Returns true if the driver thinks it can open a connection to the - * given URL. Typically, drivers will return true if they understand - * the subprotocol specified in the URL and false if they don't. Our - * protocols start with jdbc:postgresql: - * - * @see java.sql.Driver#acceptsURL - * @param url the URL of the driver - * @return true if this driver accepts the given URL - * @exception SQLException if a database-access error occurs - * (Dont know why it would *shrug*) - */ - public boolean acceptsURL(String url) throws SQLException - { - DriverURL dr = new DriverURL(url); - - if (dr.protocol().equals("jdbc")) - if (dr.subprotocol().equals("postgresql")) - return true; - return false; + else if (state <= -2 && (count % 2) == 1) { + // PM Aug 2 1997 - added tests for ? and & + if (token.equals(";") || token.equals("?") || token.equals("&") ) state = -3; + else if (token.equals("=")) state = -5; } - - /** - * The getPropertyInfo method is intended to allow a generic GUI - * tool to discover what properties it should prompt a human for - * in order to get enough information to connect to a database. - * Note that depending on the values the human has supplied so - * far, additional values may become necessary, so it may be necessary - * to iterate through several calls to getPropertyInfo - * - * @param url the Url of the database to connect to - * @param info a proposed list of tag/value pairs that will be sent on - * connect open. - * @return An array of DriverPropertyInfo objects describing - * possible properties. This array may be an empty array if - * no properties are required - * @exception SQLException if a database-access error occurs - * @see java.sql.Driver#getPropertyInfo - */ - public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException - { - return null; // We don't need anything except - // the username, which is a default - } - - /** - * Gets the drivers major version number - * - * @return the drivers major version number - */ - public int getMajorVersion() - { - return 1; - } - - /** - * Get the drivers minor version number - * - * @return the drivers minor version number - */ - public int getMinorVersion() - { - return 0; - } - - /** - * Report whether the driver is a genuine JDBC compliant driver. A - * driver may only report "true" here if it passes the JDBC compliance - * tests, otherwise it is required to return false. JDBC compliance - * requires full support for the JDBC API and full support for SQL 92 - * Entry Level. - */ - public boolean jdbcCompliant() - { - return false; + else if (state <= -2 && (count % 2) == 0) { + if (state == -3) key = token; + else if (state == -5) { + value = token; + //DriverManager.println("put("+key+","+value+")"); + urlProps.put(key, value); + state = -2; + } } + } + } + + // PM June 29 1997 + // This now outputs the properties only if we are logging + if(DriverManager.getLogStream() != null) + urlProps.list(DriverManager.getLogStream()); + + return urlProps; + + } + + /** + * Returns the hostname portion of the URL + */ + public String host() + { + return props.getProperty("PGHOST","localhost"); + } + + /** + * Returns the port number portion of the URL + * or -1 if no port was specified + */ + public int port() + { + return Integer.parseInt(props.getProperty("PGPORT","5432")); + } + + /** + * Returns the database name of the URL + */ + public String database() + { + return props.getProperty("PGDBNAME"); + } + + /** + * Returns any property + */ + public String property(String name) + { + return props.getProperty(name); + } } -/** - * The DriverURL class splits a JDBC URL into its subcomponents - * - * protocol:subprotocol:/[/host[:port]/][database] - */ -class DriverURL -{ - private String protocol, subprotocol, host, database; - private int port = -1; - - /** - * Constructs a new DriverURL, splitting the specified URL into its - * component parts - */ - public DriverURL(String url) throws SQLException - { - int a, b, c; - String tmp, hostport, dbportion; - - a = url.indexOf(':'); - if (a == -1) - throw new SQLException("Bad URL Protocol specifier"); - b = url.indexOf(':', a+1); - if (b == -1) - throw new SQLException("Bad URL Subprotocol specifier"); - protocol = new String(url.substring(0, a)); - subprotocol = new String(url.substring(a+1, b)); - tmp = new String(url.substring(b+1, url.length())); - if (tmp.length() < 2) - throw new SQLException("Bad URL Database specifier"); - if (!tmp.substring(0, 2).equals("//")) - { - host = new String("unknown"); - port = -1; - database = new String(tmp.substring(1, tmp.length())); - return; - } - dbportion = new String(tmp.substring(2, tmp.length())); - c = dbportion.indexOf('/'); - if (c == -1) - throw new SQLException("Bad URL Database specifier"); - a = dbportion.indexOf(':'); - if (a == -1) - { - host = new String(dbportion.substring(0, c)); - port = -1; - database = new String(dbportion.substring(c+1, dbportion.length())); - } else { - host = new String(dbportion.substring(0, a)); - port = Integer.valueOf(dbportion.substring(a+1, c)).intValue(); - database = new String(dbportion.substring(c+1, dbportion.length())); - } - } - - /** - * Returns the protocol name of the DriverURL - */ - public String protocol() - { - return protocol; - } - - /** - * Returns the subprotocol name of the DriverURL - */ - public String subprotocol() - { - return subprotocol; - } - - /** - * Returns the hostname portion of the URL - */ - public String host() - { - return host; - } - - /** - * Returns the port number portion of the URL - * or -1 if no port was specified - */ - public int port() - { - return port; - } - - /** - * Returns the database name of the URL - */ - public String database() - { - return database; - } -} diff --git a/src/interfaces/jdbc/postgresql/Field.java b/src/interfaces/jdbc/postgresql/Field.java index 3d27dc6c78..2beb1d2941 100644 --- a/src/interfaces/jdbc/postgresql/Field.java +++ b/src/interfaces/jdbc/postgresql/Field.java @@ -13,77 +13,91 @@ import postgresql.*; */ public class Field { - int length; // Internal Length of this field - int oid; // OID of the type - Connection conn; // Connection Instantation - String name; // Name of this field - - int sql_type = -1; // The entry in java.sql.Types for this field - String type_name = null;// The sql type name - - /** - * Construct a field based on the information fed to it. - * - * @param conn the connection this field came from - * @param name the name of the field - * @param oid the OID of the field - * @param len the length of the field - */ - public Field(Connection conn, String name, int oid, int length) - { - this.conn = conn; - this.name = name; - this.oid = oid; - this.length = length; - } - - /** - * the ResultSet and ResultMetaData both need to handle the SQL - * type, which is gained from another query. Note that we cannot - * use getObject() in this, since getObject uses getSQLType(). - * - * @return the entry in Types that refers to this field - * @exception SQLException if a database access error occurs - */ - public int getSQLType() throws SQLException - { - if (sql_type == -1) - { - ResultSet result = (postgresql.ResultSet)conn.ExecSQL("select typname from pg_type where oid = " + oid); - if (result.getColumnCount() != 1 || result.getTupleCount() != 1) - throw new SQLException("Unexpected return from query for type"); - result.next(); - type_name = result.getString(1); - if (type_name.equals("int2")) sql_type = Types.SMALLINT; - else if (type_name.equals("int4")) sql_type = Types.INTEGER; - else if (type_name.equals("int8")) sql_type = Types.BIGINT; - else if (type_name.equals("cash")) sql_type = Types.DECIMAL; - else if (type_name.equals("money")) sql_type = Types.DECIMAL; - else if (type_name.equals("float4")) sql_type = Types.REAL; - else if (type_name.equals("float8")) sql_type = Types.DOUBLE; - else if (type_name.equals("bpchar")) sql_type = Types.CHAR; - else if (type_name.equals("varchar")) sql_type = Types.VARCHAR; - else if (type_name.equals("bool")) sql_type = Types.BIT; - else if (type_name.equals("date")) sql_type = Types.DATE; - else if (type_name.equals("time")) sql_type = Types.TIME; - else if (type_name.equals("abstime")) sql_type = Types.TIMESTAMP; - else sql_type = Types.OTHER; - } - return sql_type; - } - - /** - * We also need to get the type name as returned by the back end. - * This is held in type_name AFTER a call to getSQLType. Since - * we get this information within getSQLType (if it isn't already - * done), we can just call getSQLType and throw away the result. - * - * @return the String representation of the type of this field - * @exception SQLException if a database access error occurs - */ - public String getTypeName() throws SQLException - { - int sql = getSQLType(); - return type_name; - } + int length; // Internal Length of this field + int oid; // OID of the type + Connection conn; // Connection Instantation + String name; // Name of this field + + int sql_type = -1; // The entry in java.sql.Types for this field + String type_name = null;// The sql type name + + /** + * Construct a field based on the information fed to it. + * + * @param conn the connection this field came from + * @param name the name of the field + * @param oid the OID of the field + * @param len the length of the field + */ + public Field(Connection conn, String name, int oid, int length) + { + this.conn = conn; + this.name = name; + this.oid = oid; + this.length = length; + } + + /** + * the ResultSet and ResultMetaData both need to handle the SQL + * type, which is gained from another query. Note that we cannot + * use getObject() in this, since getObject uses getSQLType(). + * + * @return the entry in Types that refers to this field + * @exception SQLException if a database access error occurs + */ + public int getSQLType() throws SQLException + { + if (sql_type == -1) + { + ResultSet result = (postgresql.ResultSet)conn.ExecSQL("select typname from pg_type where oid = " + oid); + if (result.getColumnCount() != 1 || result.getTupleCount() != 1) + throw new SQLException("Unexpected return from query for type"); + result.next(); + type_name = result.getString(1); + if (type_name.equals("int2")) + sql_type = Types.SMALLINT; + else if (type_name.equals("int4")) + sql_type = Types.INTEGER; + else if (type_name.equals("int8")) + sql_type = Types.BIGINT; + else if (type_name.equals("cash")) + sql_type = Types.DECIMAL; + else if (type_name.equals("money")) + sql_type = Types.DECIMAL; + else if (type_name.equals("float4")) + sql_type = Types.REAL; + else if (type_name.equals("float8")) + sql_type = Types.DOUBLE; + else if (type_name.equals("bpchar")) + sql_type = Types.CHAR; + else if (type_name.equals("varchar")) + sql_type = Types.VARCHAR; + else if (type_name.equals("bool")) + sql_type = Types.BIT; + else if (type_name.equals("date")) + sql_type = Types.DATE; + else if (type_name.equals("time")) + sql_type = Types.TIME; + else if (type_name.equals("abstime")) + sql_type = Types.TIMESTAMP; + else + sql_type = Types.OTHER; + } + return sql_type; + } + + /** + * We also need to get the type name as returned by the back end. + * This is held in type_name AFTER a call to getSQLType. Since + * we get this information within getSQLType (if it isn't already + * done), we can just call getSQLType and throw away the result. + * + * @return the String representation of the type of this field + * @exception SQLException if a database access error occurs + */ + public String getTypeName() throws SQLException + { + int sql = getSQLType(); + return type_name; + } } diff --git a/src/interfaces/jdbc/postgresql/PG_Object.java b/src/interfaces/jdbc/postgresql/PG_Object.java index 89518dc0d8..22c0c039c6 100644 --- a/src/interfaces/jdbc/postgresql/PG_Object.java +++ b/src/interfaces/jdbc/postgresql/PG_Object.java @@ -14,18 +14,132 @@ import postgresql.*; */ public class PG_Object { - public String type; - public String value; - - /** - * Constructor for the PostgreSQL generic object - * - * @param type a string describing the type of the object - * @param value a string representation of the value of the object - */ - public PG_Object(String type, String value) - { - this.type = type; - this.value = value; - } + public String type; + public String value; + + /** + * Constructor for the PostgreSQL generic object + * + * @param type a string describing the type of the object + * @param value a string representation of the value of the object + */ + public PG_Object(String type, String value) throws SQLException + { + this.type = type; + this.value = value; + } + + /** + * This returns true if the object is a 'box' + */ + public boolean isBox() + { + return type.equals("box"); + } + + /** + * This returns a PGbox object, or null if it's not + * @return PGbox + */ + public PGbox getBox() throws SQLException + { + if(isBox()) + return new PGbox(value); + return null; + } + + /** + * This returns true if the object is a 'point' + */ + public boolean isCircle() + { + return type.equals("circle"); + } + + /** + * This returns a PGcircle object, or null if it's not + * @return PGcircle + */ + public PGcircle getCircle() throws SQLException + { + if(isCircle()) + return new PGcircle(value); + return null; + } + + /** + * This returns true if the object is a 'lseg' (line segment) + */ + public boolean isLseg() + { + return type.equals("lseg"); + } + + /** + * This returns a PGlsegobject, or null if it's not + * @return PGlseg + */ + public PGlseg getLseg() throws SQLException + { + if(isLseg()) + return new PGlseg(value); + return null; + } + + /** + * This returns true if the object is a 'path' + */ + public boolean isPath() + { + return type.equals("path"); + } + + /** + * This returns a PGpath object, or null if it's not + * @return PGpath + */ + public PGpath getPath() throws SQLException + { + if(isPath()) + return new PGpath(value); + return null; + } + + /** + * This returns true if the object is a 'point' + */ + public boolean isPoint() + { + return type.equals("point"); + } + + /** + * This returns a PGpoint object, or null if it's not + * @return PGpoint object + */ + public PGpoint getPoint() throws SQLException + { + if(isPoint()) + return new PGpoint(value); + return null; + } + + /** + * This returns true if the object is a 'polygon' + */ + public boolean isPolygon() + { + return type.equals("polygon"); + } + + /** + * This returns a PGpolygon object, or null if it's not + * @return PGpolygon + */ + public PGpolygon getPolygon() throws SQLException + { + if(isPolygon()) + return new PGpolygon(value); + return null; + } } diff --git a/src/interfaces/jdbc/postgresql/ResultSet.java b/src/interfaces/jdbc/postgresql/ResultSet.java index c5894e7bff..d9222ee8ea 100644 --- a/src/interfaces/jdbc/postgresql/ResultSet.java +++ b/src/interfaces/jdbc/postgresql/ResultSet.java @@ -9,9 +9,6 @@ import java.sql.*; import postgresql.*; /** - * @version 1.0 15-APR-1997 - * @author Adrian Hall - * * A ResultSet provides access to a table of data generated by executing a * Statement. The table rows are retrieved in sequence. Within a row its * column values can be accessed in any order. @@ -54,792 +51,793 @@ import postgresql.*; */ public class ResultSet implements java.sql.ResultSet { - Vector rows; // The results - Field fields[]; // The field descriptions - String status; // Status of the result - int updateCount; // How many rows did we get back? - int current_row; // Our pointer to where we are at - byte[][] this_row; // the current row result - Connection connection; // the connection which we returned from - SQLWarning warnings = null; // The warning chain - boolean wasNullFlag = false; // the flag for wasNull() - - // We can chain multiple resultSets together - this points to - // next resultSet in the chain. - private ResultSet next = null; - - /** - * Create a new ResultSet - Note that we create ResultSets to - * represent the results of everything. - * - * @param fields an array of Field objects (basically, the - * ResultSet MetaData) - * @param tuples Vector of the actual data - * @param status the status string returned from the back end - * @param updateCount the number of rows affected by the operation - * @param cursor the positioned update/delete cursor name - */ - public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) - { - this.connection = conn; - this.fields = fields; - this.rows = tuples; - this.status = status; - this.updateCount = updateCount; - this.this_row = null; - this.current_row = -1; - } - - /** - * A ResultSet is initially positioned before its first row, - * the first call to next makes the first row the current row; - * the second call makes the second row the current row, etc. - * - * If an input stream from the previous row is open, it is - * implicitly closed. The ResultSet's warning chain is cleared - * when a new row is read - * - * @return true if the new current is valid; false if there are no - * more rows - * @exception SQLException if a database access error occurs - */ - public boolean next() throws SQLException - { - if (++current_row >= rows.size()) - return false; - this_row = (byte [][])rows.elementAt(current_row); - return true; - } - - /** - * In some cases, it is desirable to immediately release a ResultSet - * database and JDBC resources instead of waiting for this to happen - * when it is automatically closed. The close method provides this - * immediate release. - * - * Note: A ResultSet is automatically closed by the Statement - * the Statement that generated it when that Statement is closed, - * re-executed, or is used to retrieve the next result from a sequence - * of multiple results. A ResultSet is also automatically closed - * when it is garbage collected. - * - * @exception SQLException if a database access error occurs - */ - public void close() throws SQLException - { - // No-op - } - - /** - * A column may have the value of SQL NULL; wasNull() reports whether - * the last column read had this special value. Note that you must - * first call getXXX on a column to try to read its value and then - * call wasNull() to find if the value was SQL NULL - * - * @return true if the last column read was SQL NULL - * @exception SQLException if a database access error occurred - */ - public boolean wasNull() throws SQLException - { - return wasNullFlag; - } - - /** - * Get the value of a column in the current row as a Java String - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, null for SQL NULL - * @exception SQLException if a database access error occurs - */ - public String getString(int columnIndex) throws SQLException - { - byte[] bytes = getBytes(columnIndex); - - if (bytes == null) - return null; - return new String(bytes); - } - - /** - * Get the value of a column in the current row as a Java boolean - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value, false for SQL NULL - * @exception SQLException if a database access error occurs - */ - public boolean getBoolean(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - int c = s.charAt(0); - return ((c == 't') || (c == 'T')); - } - return false; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java byte. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public byte getByte(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Byte.parseByte(s); - } catch (NumberFormatException e) { - throw new SQLException("Bad Byte Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java short. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public short getShort(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Short.parseShort(s); - } catch (NumberFormatException e) { - throw new SQLException("Bad Short Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java int. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public int getInt(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Integer Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java long. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public long getLong(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Long.parseLong(s); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Long Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java float. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public float getFloat(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Float.valueOf(s).floatValue(); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Float Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java double. - * - * @param columnIndex the first column is 1, the second is 2,... - * @return the column value; 0 if SQL NULL - * @exception SQLException if a database access error occurs - */ - public double getDouble(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - return Double.valueOf(s).doubleValue(); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Double Form: " + s); - } - } - return 0; // SQL NULL - } - - /** - * Get the value of a column in the current row as a - * java.lang.BigDecimal object - * - * @param columnIndex the first column is 1, the second is 2... - * @param scale the number of digits to the right of the decimal - * @return the column value; if the value is SQL NULL, null - * @exception SQLException if a database access error occurs - */ - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException - { - String s = getString(columnIndex); - BigDecimal val; - - if (s != null) - { - try - { - val = new BigDecimal(s); - } catch (NumberFormatException e) { - throw new SQLException ("Bad BigDecimal Form: " + s); - } - try - { - return val.setScale(scale); - } catch (ArithmeticException e) { - throw new SQLException ("Bad BigDecimal Form: " + s); - } - } - return null; // SQL NULL - } - - /** - * Get the value of a column in the current row as a Java byte array - * The bytes represent the raw values returned by the driver. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return the column value; if the value is SQL NULL, the result - * is null - * @exception SQLException if a database access error occurs - */ - public byte[] getBytes(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new SQLException("Column Index out of range"); - wasNullFlag = (this_row[columnIndex - 1] == null); - return this_row[columnIndex - 1]; - } - - /** - * Get the value of a column in the current row as a java.sql.Date - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public java.sql.Date getDate(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - if (s.length() != 10) - throw new NumberFormatException("Wrong Length!"); - int mon = Integer.parseInt(s.substring(0,2)); - int day = Integer.parseInt(s.substring(3,5)); - int yr = Integer.parseInt(s.substring(6)); - return new java.sql.Date(yr - 1900, mon -1, day); - } catch (NumberFormatException e) { - throw new SQLException("Bad Date Form: " + s); - } - } - return null; // SQL NULL - } - - /** - * Get the value of a column in the current row as a java.sql.Time - * object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Time getTime(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - - if (s != null) - { - try - { - if (s.length() != 5 && s.length() != 8) - throw new NumberFormatException("Wrong Length!"); - int hr = Integer.parseInt(s.substring(0,2)); - int min = Integer.parseInt(s.substring(3,5)); - int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); - return new Time(hr, min, sec); - } catch (NumberFormatException e) { - throw new SQLException ("Bad Time Form: " + s); - } - } - return null; // SQL NULL - } - - /** - * Get the value of a column in the current row as a - * java.sql.Timestamp object - * - * @param columnIndex the first column is 1, the second is 2... - * @return the column value; null if SQL NULL - * @exception SQLException if a database access error occurs - */ - public Timestamp getTimestamp(int columnIndex) throws SQLException - { - String s = getString(columnIndex); - DateFormat df = DateFormat.getDateInstance(); - - if (s != null) - { - try - { - java.sql.Date d = (java.sql.Date)df.parse(s); - return new Timestamp(d.getTime()); - } catch (ParseException e) { - throw new SQLException("Bad Timestamp Format: " + s); - } - } - return null; // SQL NULL - } - - /** - * A column value can be retrieved as a stream of ASCII characters - * and then read in chunks from the stream. This method is - * particular suitable for retrieving large LONGVARCHAR values. - * The JDBC driver will do any necessary conversion from the - * database format into ASCII. - * - * Note: All the data in the returned stream must be read - * prior to getting the value of any other column. The next call - * to a get method implicitly closes the stream. Also, a stream - * may return 0 for available() whether there is data available - * or not. - * - * We implement an ASCII stream as a Binary stream - we should really - * do the data conversion, but I cannot be bothered to implement this - * right now. - * - * @param columnIndex the first column is 1, the second is 2, ... - * @return a Java InputStream that delivers the database column - * value as a stream of one byte ASCII characters. If the - * value is SQL NULL then the result is null - * @exception SQLException if a database access error occurs - * @see getBinaryStream - */ - public InputStream getAsciiStream(int columnIndex) throws SQLException - { - return getBinaryStream(columnIndex); - } - - /** - * A column value can also be retrieved as a stream of Unicode - * characters. We implement this as a binary stream. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of two byte Unicode characters. If the value is - * SQL NULL, then the result is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getBinaryStream - */ - public InputStream getUnicodeStream(int columnIndex) throws SQLException - { - return getBinaryStream(columnIndex); - } - - /** - * A column value can also be retrieved as a binary strea. This - * method is suitable for retrieving LONGVARBINARY values. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Java InputStream that delivers the database column value - * as a stream of two byte Unicode characters. If the value is - * SQL NULL, then the result is null - * @exception SQLException if a database access error occurs - * @see getAsciiStream - * @see getUnicodeStream - */ - public InputStream getBinaryStream(int columnIndex) throws SQLException - { - byte b[] = getBytes(columnIndex); - - if (b != null) - return new ByteArrayInputStream(b); - return null; // SQL NULL - } - - /** - * The following routines simply convert the columnName into - * a columnIndex and then call the appropriate routine above. - * - * @param columnName is the SQL name of the column - * @return the column value - * @exception SQLException if a database access error occurs - */ - public String getString(String columnName) throws SQLException - { - return getString(findColumn(columnName)); - } - - public boolean getBoolean(String columnName) throws SQLException - { - return getBoolean(findColumn(columnName)); - } - - public byte getByte(String columnName) throws SQLException - { - - return getByte(findColumn(columnName)); - } - - public short getShort(String columnName) throws SQLException - { - return getShort(findColumn(columnName)); - } - - public int getInt(String columnName) throws SQLException - { - return getInt(findColumn(columnName)); - } - - public long getLong(String columnName) throws SQLException - { - return getLong(findColumn(columnName)); - } - - public float getFloat(String columnName) throws SQLException - { - return getFloat(findColumn(columnName)); - } - - public double getDouble(String columnName) throws SQLException - { - return getDouble(findColumn(columnName)); - } - - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException - { - return getBigDecimal(findColumn(columnName), scale); - } - - public byte[] getBytes(String columnName) throws SQLException - { - return getBytes(findColumn(columnName)); - } - - public java.sql.Date getDate(String columnName) throws SQLException - { - return getDate(findColumn(columnName)); - } - - public Time getTime(String columnName) throws SQLException - { - return getTime(findColumn(columnName)); - } - - public Timestamp getTimestamp(String columnName) throws SQLException - { - return getTimestamp(findColumn(columnName)); - } - - public InputStream getAsciiStream(String columnName) throws SQLException - { - return getAsciiStream(findColumn(columnName)); - } - - public InputStream getUnicodeStream(String columnName) throws SQLException - { - return getUnicodeStream(findColumn(columnName)); - } - - public InputStream getBinaryStream(String columnName) throws SQLException - { - return getBinaryStream(findColumn(columnName)); - } - - /** - * The first warning reported by calls on this ResultSet is - * returned. Subsequent ResultSet warnings will be chained - * to this SQLWarning. - * - * The warning chain is automatically cleared each time a new - * row is read. - * - * Note: This warning chain only covers warnings caused by - * ResultSet methods. Any warnings caused by statement methods - * (such as reading OUT parameters) will be chained on the - * Statement object. - * - * @return the first SQLWarning or null; - * @exception SQLException if a database access error occurs. - */ - public SQLWarning getWarnings() throws SQLException - { - return warnings; - } - - /** - * After this call, getWarnings returns null until a new warning - * is reported for this ResultSet - * - * @exception SQLException if a database access error occurs - */ - public void clearWarnings() throws SQLException - { - warnings = null; - } - - /** - * Get the name of the SQL cursor used by this ResultSet - * - * In SQL, a result table is retrieved though 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. - * - * JDBC supports this SQL feature by providing the name of the - * SQL cursor used by a ResultSet. The current row of a ResulSet - * is also the current row of this SQL cursor. - * - * Note: If positioned update is not supported, a SQLException - * is thrown. - * - * @return the ResultSet's SQL cursor name. - * @exception SQLException if a database access error occurs - */ - public String getCursorName() throws SQLException - { - return connection.getCursorName(); - } - - /** - * The numbers, types and properties of a ResultSet's columns are - * provided by the getMetaData method - * - * @return a description of the ResultSet's columns - * @exception SQLException if a database access error occurs - */ - public java.sql.ResultSetMetaData getMetaData() throws SQLException - { - return new ResultSetMetaData(rows, fields); - } - - /** - * Get the value of a column in the current row as a Java object - * - * This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - * This method may also be used to read database specific abstract - * data types. - * - * @param columnIndex the first column is 1, the second is 2... - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(int columnIndex) throws SQLException - { - Field field; - - if (columnIndex < 1 || columnIndex > fields.length) - throw new SQLException("Column index out of range"); - field = fields[columnIndex - 1]; - - switch (field.getSQLType()) - { - case Types.BIT: - return new Boolean(getBoolean(columnIndex)); - case Types.SMALLINT: - return new Integer(getInt(columnIndex)); - case Types.INTEGER: - return new Integer(getInt(columnIndex)); - case Types.BIGINT: - return new Long(getLong(columnIndex)); - case Types.NUMERIC: - return getBigDecimal(columnIndex, 0); - case Types.REAL: - return new Float(getFloat(columnIndex)); - case Types.DOUBLE: - return new Double(getDouble(columnIndex)); - case Types.CHAR: - case Types.VARCHAR: - return getString(columnIndex); - case Types.DATE: - return getDate(columnIndex); - case Types.TIME: - return getTime(columnIndex); - case Types.TIMESTAMP: - return getTimestamp(columnIndex); - default: - return new PG_Object(field.getTypeName(), getString(columnIndex)); - } - } - - /** - * Get the value of a column in the current row as a Java object - * - * This method will return the value of the given column as a - * Java object. The type of the Java object will be the default - * Java Object type corresponding to the column's SQL type, following - * the mapping specified in the JDBC specification. - * - * This method may also be used to read database specific abstract - * data types. - * - * @param columnName is the SQL name of the column - * @return a Object holding the column value - * @exception SQLException if a database access error occurs - */ - public Object getObject(String columnName) throws SQLException - { - return getObject(findColumn(columnName)); - } - - /** - * Map a ResultSet column name to a ResultSet column index - * - * @param columnName the name of the column - * @return the column index - * @exception SQLException if a database access error occurs - */ - public int findColumn(String columnName) throws SQLException - { - int i; - - for (i = 0 ; i < fields.length; ++i) - if (fields[i].name.equalsIgnoreCase(columnName)) - return (i+1); - throw new SQLException ("Column name not found"); - } - - // ************************************************************ - // END OF PUBLIC INTERFACE - // ************************************************************ - - /** - * We at times need to know if the resultSet we are working - * with is the result of an UPDATE, DELETE or INSERT (in which - * case, we only have a row count), or of a SELECT operation - * (in which case, we have multiple fields) - this routine - * tells us. - * - * @return true if we have tuples available - */ - public boolean reallyResultSet() - { - return (fields != null); - } - - /** - * Since ResultSets can be chained, we need some method of - * finding the next one in the chain. The method getNext() - * returns the next one in the chain. - * - * @return the next ResultSet, or null if there are none - */ - public ResultSet getNext() - { - return next; - } - - /** - * This following method allows us to add a ResultSet object - * to the end of the current chain. - * - * @param r the resultset to add to the end of the chain. - */ - public void append(ResultSet r) - { - if (next == null) - next = r; - else - next.append(r); - } - - /** - * If we are just a place holder for results, we still need - * to get an updateCount. This method returns it. - * - * @return the updateCount - */ - public int getResultCount() - { - return updateCount; - } - - /** - * We also need to provide a couple of auxiliary functions for - * the implementation of the ResultMetaData functions. In - * particular, we need to know the number of rows and the - * number of columns. Rows are also known as Tuples - * - * getTupleCount returns the number of rows - * - * @return the number of rows - */ - public int getTupleCount() - { - return rows.size(); - } - - /** - * getColumnCount returns the number of columns - * - * @return the number of columns - */ - public int getColumnCount() - { - return fields.length; - } + Vector rows; // The results + Field fields[]; // The field descriptions + String status; // Status of the result + int updateCount; // How many rows did we get back? + int current_row; // Our pointer to where we are at + byte[][] this_row; // the current row result + Connection connection; // the connection which we returned from + SQLWarning warnings = null; // The warning chain + boolean wasNullFlag = false; // the flag for wasNull() + + // We can chain multiple resultSets together - this points to + // next resultSet in the chain. + private ResultSet next = null; + + /** + * Create a new ResultSet - Note that we create ResultSets to + * represent the results of everything. + * + * @param fields an array of Field objects (basically, the + * ResultSet MetaData) + * @param tuples Vector of the actual data + * @param status the status string returned from the back end + * @param updateCount the number of rows affected by the operation + * @param cursor the positioned update/delete cursor name + */ + public ResultSet(Connection conn, Field[] fields, Vector tuples, String status, int updateCount) + { + this.connection = conn; + this.fields = fields; + this.rows = tuples; + this.status = status; + this.updateCount = updateCount; + this.this_row = null; + this.current_row = -1; + } + + /** + * A ResultSet is initially positioned before its first row, + * the first call to next makes the first row the current row; + * the second call makes the second row the current row, etc. + * + * If an input stream from the previous row is open, it is + * implicitly closed. The ResultSet's warning chain is cleared + * when a new row is read + * + * @return true if the new current is valid; false if there are no + * more rows + * @exception SQLException if a database access error occurs + */ + public boolean next() throws SQLException + { + if (++current_row >= rows.size()) + return false; + this_row = (byte [][])rows.elementAt(current_row); + return true; + } + + /** + * In some cases, it is desirable to immediately release a ResultSet + * database and JDBC resources instead of waiting for this to happen + * when it is automatically closed. The close method provides this + * immediate release. + * + * Note: A ResultSet is automatically closed by the Statement + * the Statement that generated it when that Statement is closed, + * re-executed, or is used to retrieve the next result from a sequence + * of multiple results. A ResultSet is also automatically closed + * when it is garbage collected. + * + * @exception SQLException if a database access error occurs + */ + public void close() throws SQLException + { + // No-op + } + + /** + * A column may have the value of SQL NULL; wasNull() reports whether + * the last column read had this special value. Note that you must + * first call getXXX on a column to try to read its value and then + * call wasNull() to find if the value was SQL NULL + * + * @return true if the last column read was SQL NULL + * @exception SQLException if a database access error occurred + */ + public boolean wasNull() throws SQLException + { + return wasNullFlag; + } + + /** + * Get the value of a column in the current row as a Java String + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, null for SQL NULL + * @exception SQLException if a database access error occurs + */ + public String getString(int columnIndex) throws SQLException + { + byte[] bytes = getBytes(columnIndex); + + if (bytes == null) + return null; + return new String(bytes); + } + + /** + * Get the value of a column in the current row as a Java boolean + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value, false for SQL NULL + * @exception SQLException if a database access error occurs + */ + public boolean getBoolean(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + int c = s.charAt(0); + return ((c == 't') || (c == 'T')); + } + return false; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public byte getByte(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Byte.parseByte(s); + } catch (NumberFormatException e) { + throw new SQLException("Bad Byte Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java short. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public short getShort(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Short.parseShort(s); + } catch (NumberFormatException e) { + throw new SQLException("Bad Short Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java int. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public int getInt(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Integer Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java long. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public long getLong(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Long.parseLong(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Long Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java float. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public float getFloat(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Float.valueOf(s).floatValue(); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Float Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex the first column is 1, the second is 2,... + * @return the column value; 0 if SQL NULL + * @exception SQLException if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + return Double.valueOf(s).doubleValue(); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Double Form: " + s); + } + } + return 0; // SQL NULL + } + + /** + * Get the value of a column in the current row as a + * java.lang.BigDecimal object + * + * @param columnIndex the first column is 1, the second is 2... + * @param scale the number of digits to the right of the decimal + * @return the column value; if the value is SQL NULL, null + * @exception SQLException if a database access error occurs + */ + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException + { + String s = getString(columnIndex); + BigDecimal val; + + if (s != null) + { + try + { + val = new BigDecimal(s); + } catch (NumberFormatException e) { + throw new SQLException ("Bad BigDecimal Form: " + s); + } + try + { + return val.setScale(scale); + } catch (ArithmeticException e) { + throw new SQLException ("Bad BigDecimal Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a Java byte array + * The bytes represent the raw values returned by the driver. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return the column value; if the value is SQL NULL, the result + * is null + * @exception SQLException if a database access error occurs + */ + public byte[] getBytes(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column Index out of range"); + wasNullFlag = (this_row[columnIndex - 1] == null); + return this_row[columnIndex - 1]; + } + + /** + * Get the value of a column in the current row as a java.sql.Date + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + if (s.length() != 10) + throw new NumberFormatException("Wrong Length!"); + int mon = Integer.parseInt(s.substring(0,2)); + int day = Integer.parseInt(s.substring(3,5)); + int yr = Integer.parseInt(s.substring(6)); + return new java.sql.Date(yr - 1900, mon -1, day); + } catch (NumberFormatException e) { + throw new SQLException("Bad Date Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a java.sql.Time + * object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Time getTime(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + + if (s != null) + { + try + { + if (s.length() != 5 && s.length() != 8) + throw new NumberFormatException("Wrong Length!"); + int hr = Integer.parseInt(s.substring(0,2)); + int min = Integer.parseInt(s.substring(3,5)); + int sec = (s.length() == 5) ? 0 : Integer.parseInt(s.substring(6)); + return new Time(hr, min, sec); + } catch (NumberFormatException e) { + throw new SQLException ("Bad Time Form: " + s); + } + } + return null; // SQL NULL + } + + /** + * Get the value of a column in the current row as a + * java.sql.Timestamp object + * + * @param columnIndex the first column is 1, the second is 2... + * @return the column value; null if SQL NULL + * @exception SQLException if a database access error occurs + */ + public Timestamp getTimestamp(int columnIndex) throws SQLException + { + String s = getString(columnIndex); + DateFormat df = DateFormat.getDateInstance(); + + if (s != null) + { + try + { + java.sql.Date d = (java.sql.Date)df.parse(s); + return new Timestamp(d.getTime()); + } catch (ParseException e) { + throw new SQLException("Bad Timestamp Format: " + s); + } + } + return null; // SQL NULL + } + + /** + * A column value can be retrieved as a stream of ASCII characters + * and then read in chunks from the stream. This method is + * particular suitable for retrieving large LONGVARCHAR values. + * The JDBC driver will do any necessary conversion from the + * database format into ASCII. + * + * Note: All the data in the returned stream must be read + * prior to getting the value of any other column. The next call + * to a get method implicitly closes the stream. Also, a stream + * may return 0 for available() whether there is data available + * or not. + * + * We implement an ASCII stream as a Binary stream - we should really + * do the data conversion, but I cannot be bothered to implement this + * right now. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @return a Java InputStream that delivers the database column + * value as a stream of one byte ASCII characters. If the + * value is SQL NULL then the result is null + * @exception SQLException if a database access error occurs + * @see getBinaryStream + */ + public InputStream getAsciiStream(int columnIndex) throws SQLException + { + return getBinaryStream(columnIndex); + } + + /** + * A column value can also be retrieved as a stream of Unicode + * characters. We implement this as a binary stream. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of two byte Unicode characters. If the value is + * SQL NULL, then the result is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getBinaryStream + */ + public InputStream getUnicodeStream(int columnIndex) throws SQLException + { + return getBinaryStream(columnIndex); + } + + /** + * A column value can also be retrieved as a binary strea. This + * method is suitable for retrieving LONGVARBINARY values. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Java InputStream that delivers the database column value + * as a stream of two byte Unicode characters. If the value is + * SQL NULL, then the result is null + * @exception SQLException if a database access error occurs + * @see getAsciiStream + * @see getUnicodeStream + */ + public InputStream getBinaryStream(int columnIndex) throws SQLException + { + byte b[] = getBytes(columnIndex); + + if (b != null) + return new ByteArrayInputStream(b); + return null; // SQL NULL + } + + /** + * The following routines simply convert the columnName into + * a columnIndex and then call the appropriate routine above. + * + * @param columnName is the SQL name of the column + * @return the column value + * @exception SQLException if a database access error occurs + */ + public String getString(String columnName) throws SQLException + { + return getString(findColumn(columnName)); + } + + public boolean getBoolean(String columnName) throws SQLException + { + return getBoolean(findColumn(columnName)); + } + + public byte getByte(String columnName) throws SQLException + { + + return getByte(findColumn(columnName)); + } + + public short getShort(String columnName) throws SQLException + { + return getShort(findColumn(columnName)); + } + + public int getInt(String columnName) throws SQLException + { + return getInt(findColumn(columnName)); + } + + public long getLong(String columnName) throws SQLException + { + return getLong(findColumn(columnName)); + } + + public float getFloat(String columnName) throws SQLException + { + return getFloat(findColumn(columnName)); + } + + public double getDouble(String columnName) throws SQLException + { + return getDouble(findColumn(columnName)); + } + + public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException + { + return getBigDecimal(findColumn(columnName), scale); + } + + public byte[] getBytes(String columnName) throws SQLException + { + return getBytes(findColumn(columnName)); + } + + public java.sql.Date getDate(String columnName) throws SQLException + { + return getDate(findColumn(columnName)); + } + + public Time getTime(String columnName) throws SQLException + { + return getTime(findColumn(columnName)); + } + + public Timestamp getTimestamp(String columnName) throws SQLException + { + return getTimestamp(findColumn(columnName)); + } + + public InputStream getAsciiStream(String columnName) throws SQLException + { + return getAsciiStream(findColumn(columnName)); + } + + public InputStream getUnicodeStream(String columnName) throws SQLException + { + return getUnicodeStream(findColumn(columnName)); + } + + public InputStream getBinaryStream(String columnName) throws SQLException + { + return getBinaryStream(findColumn(columnName)); + } + + /** + * The first warning reported by calls on this ResultSet is + * returned. Subsequent ResultSet warnings will be chained + * to this SQLWarning. + * + * The warning chain is automatically cleared each time a new + * row is read. + * + * Note: This warning chain only covers warnings caused by + * ResultSet methods. Any warnings caused by statement methods + * (such as reading OUT parameters) will be chained on the + * Statement object. + * + * @return the first SQLWarning or null; + * @exception SQLException if a database access error occurs. + */ + public SQLWarning getWarnings() throws SQLException + { + return warnings; + } + + /** + * After this call, getWarnings returns null until a new warning + * is reported for this ResultSet + * + * @exception SQLException if a database access error occurs + */ + public void clearWarnings() throws SQLException + { + warnings = null; + } + + /** + * Get the name of the SQL cursor used by this ResultSet + * + * In SQL, a result table is retrieved though 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. + * + * JDBC supports this SQL feature by providing the name of the + * SQL cursor used by a ResultSet. The current row of a ResulSet + * is also the current row of this SQL cursor. + * + * Note: If positioned update is not supported, a SQLException + * is thrown. + * + * @return the ResultSet's SQL cursor name. + * @exception SQLException if a database access error occurs + */ + public String getCursorName() throws SQLException + { + return connection.getCursorName(); + } + + /** + * The numbers, types and properties of a ResultSet's columns are + * provided by the getMetaData method + * + * @return a description of the ResultSet's columns + * @exception SQLException if a database access error occurs + */ + public java.sql.ResultSetMetaData getMetaData() throws SQLException + { + return new ResultSetMetaData(rows, fields); + } + + /** + * Get the value of a column in the current row as a Java object + * + * This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + * This method may also be used to read database specific abstract + * data types. + * + * @param columnIndex the first column is 1, the second is 2... + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException + { + Field field; + + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column index out of range"); + field = fields[columnIndex - 1]; + + switch (field.getSQLType()) + { + case Types.BIT: + return new Boolean(getBoolean(columnIndex)); + case Types.SMALLINT: + return new Integer(getInt(columnIndex)); + case Types.INTEGER: + return new Integer(getInt(columnIndex)); + case Types.BIGINT: + return new Long(getLong(columnIndex)); + case Types.NUMERIC: + return getBigDecimal(columnIndex, 0); + case Types.REAL: + return new Float(getFloat(columnIndex)); + case Types.DOUBLE: + return new Double(getDouble(columnIndex)); + case Types.CHAR: + case Types.VARCHAR: + return getString(columnIndex); + case Types.DATE: + return getDate(columnIndex); + case Types.TIME: + return getTime(columnIndex); + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + default: + return new PG_Object(field.getTypeName(), getString(columnIndex)); + } + } + + /** + * Get the value of a column in the current row as a Java object + * + * This method will return the value of the given column as a + * Java object. The type of the Java object will be the default + * Java Object type corresponding to the column's SQL type, following + * the mapping specified in the JDBC specification. + * + * This method may also be used to read database specific abstract + * data types. + * + * @param columnName is the SQL name of the column + * @return a Object holding the column value + * @exception SQLException if a database access error occurs + */ + public Object getObject(String columnName) throws SQLException + { + return getObject(findColumn(columnName)); + } + + /** + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName the name of the column + * @return the column index + * @exception SQLException if a database access error occurs + */ + public int findColumn(String columnName) throws SQLException + { + int i; + + for (i = 0 ; i < fields.length; ++i) + if (fields[i].name.equalsIgnoreCase(columnName)) + return (i+1); + throw new SQLException ("Column name not found"); + } + + // ************************************************************ + // END OF PUBLIC INTERFACE + // ************************************************************ + + /** + * We at times need to know if the resultSet we are working + * with is the result of an UPDATE, DELETE or INSERT (in which + * case, we only have a row count), or of a SELECT operation + * (in which case, we have multiple fields) - this routine + * tells us. + * + * @return true if we have tuples available + */ + public boolean reallyResultSet() + { + return (fields != null); + } + + /** + * Since ResultSets can be chained, we need some method of + * finding the next one in the chain. The method getNext() + * returns the next one in the chain. + * + * @return the next ResultSet, or null if there are none + */ + public ResultSet getNext() + { + return next; + } + + /** + * This following method allows us to add a ResultSet object + * to the end of the current chain. + * + * @param r the resultset to add to the end of the chain. + */ + public void append(ResultSet r) + { + if (next == null) + next = r; + else + next.append(r); + } + + /** + * If we are just a place holder for results, we still need + * to get an updateCount. This method returns it. + * + * @return the updateCount + */ + public int getResultCount() + { + return updateCount; + } + + /** + * We also need to provide a couple of auxiliary functions for + * the implementation of the ResultMetaData functions. In + * particular, we need to know the number of rows and the + * number of columns. Rows are also known as Tuples + * + * getTupleCount returns the number of rows + * + * @return the number of rows + */ + public int getTupleCount() + { + return rows.size(); + } + + /** + * getColumnCount returns the number of columns + * + * @return the number of columns + */ + public int getColumnCount() + { + return fields.length; + } } + diff --git a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java index a6974b3076..f058e2e0ba 100644 --- a/src/interfaces/jdbc/postgresql/ResultSetMetaData.java +++ b/src/interfaces/jdbc/postgresql/ResultSetMetaData.java @@ -16,414 +16,415 @@ import postgresql.*; */ public class ResultSetMetaData implements java.sql.ResultSetMetaData { - Vector rows; - Field[] fields; - - /** - * Initialise for a result with a tuple set and - * a field descriptor set - * - * @param rows the Vector of rows returned by the ResultSet - * @param fields the array of field descriptors - */ - public ResultSetMetaData(Vector rows, Field[] fields) - { - this.rows = rows; - this.fields = fields; - } - - /** - * Whats the number of columns in the ResultSet? - * - * @return the number - * @exception SQLException if a database access error occurs - */ - public int getColumnCount() throws SQLException - { - return fields.length; - } - - /** - * Is the column automatically numbered (and thus read-only) - * I believe that PostgreSQL does not support this feature. - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isAutoIncrement(int column) throws SQLException - { - return false; - } - - /** - * Does a column's case matter? ASSUMPTION: Any field that is - * not obviously case insensitive is assumed to be case sensitive - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isCaseSensitive(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - case Types.INTEGER: - case Types.FLOAT: - case Types.REAL: - case Types.DOUBLE: - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - return false; - default: - return true; - } - } - - /** - * Can the column be used in a WHERE clause? Basically for - * this, I split the functions into two types: recognised - * types (which are always useable), and OTHER types (which - * may or may not be useable). The OTHER types, for now, I - * will assume they are useable. We should really query the - * catalog to see if they are useable. - * - * @param column the first column is 1, the second is 2... - * @return true if they can be used in a WHERE clause - * @exception SQLException if a database access error occurs - */ - public boolean isSearchable(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - // This switch is pointless, I know - but it is a set-up - // for further expansion. - switch (sql_type) - { - case Types.OTHER: - return true; - default: - return true; - } - } - - /** - * Is the column a cash value? 6.1 introduced the cash/money - * type, which haven't been incorporated as of 970414, so I - * just check the type name for both 'cash' and 'money' - * - * @param column the first column is 1, the second is 2... - * @return true if its a cash column - * @exception SQLException if a database access error occurs - */ - public boolean isCurrency(int column) throws SQLException - { - String type_name = getField(column).getTypeName(); - - if (type_name.equals("cash")) - return true; - if (type_name.equals("money")) - return true; - return false; - } - - /** - * Can you put a NULL in this column? I think this is always - * true in 6.1's case. It would only be false if the field had - * been defined NOT NULL (system catalogs could be queried?) - * - * @param column the first column is 1, the second is 2... - * @return one of the columnNullable values - * @exception SQLException if a database access error occurs - */ - public int isNullable(int column) throws SQLException - { - return columnNullable; // We can always put NULL in - } - - /** - * Is the column a signed number? In PostgreSQL, all numbers - * are signed, so this is trivial. However, strings are not - * signed (duh!) - * - * @param column the first column is 1, the second is 2... - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isSigned(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - case Types.INTEGER: - case Types.FLOAT: - case Types.REAL: - case Types.DOUBLE: - return true; - case Types.DATE: - case Types.TIME: - case Types.TIMESTAMP: - return false; // I don't know about these? - default: - return false; - } - } - - /** - * What is the column's normal maximum width in characters? - * - * @param column the first column is 1, the second is 2, etc. - * @return the maximum width - * @exception SQLException if a database access error occurs - */ - public int getColumnDisplaySize(int column) throws SQLException - { - int max = getColumnLabel(column).length(); - int i; - - for (i = 0 ; i < rows.size(); ++i) - { - byte[][] x = (byte[][])(rows.elementAt(i)); - int xl = x[column - 1].length; - if (xl > max) - max = xl; - } - return max; - } - - /** - * What is the suggested column title for use in printouts and - * displays? We suggest the ColumnName! - * - * @param column the first column is 1, the second is 2, etc. - * @return the column label - * @exception SQLException if a database access error occurs - */ - public String getColumnLabel(int column) throws SQLException - { - return getColumnName(column); - } - - /** - * What's a column's name? - * - * @param column the first column is 1, the second is 2, etc. - * @return the column name - * @exception SQLException if a databvase access error occurs - */ - public String getColumnName(int column) throws SQLException - { - return getField(column).name; - } - - /** - * What is a column's table's schema? This relies on us knowing - * the table name....which I don't know how to do as yet. The - * JDBC specification allows us to return "" if this is not - * applicable. - * - * @param column the first column is 1, the second is 2... - * @return the Schema - * @exception SQLException if a database access error occurs - */ - 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. - } - - /** - * What is a column's number of decimal digits. - * - * @param column the first column is 1, the second is 2... - * @return the precision - * @exception SQLException if a database access error occurs - */ - public int getPrecision(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - return 5; - case Types.INTEGER: - return 10; - case Types.REAL: - return 8; - case Types.FLOAT: - return 16; - case Types.DOUBLE: - return 16; - default: - throw new SQLException("no precision for non-numeric data types."); - } - } - - /** - * What is a column's number of digits to the right of the - * decimal point? - * - * @param column the first column is 1, the second is 2... - * @return the scale - * @exception SQLException if a database access error occurs - */ - public int getScale(int column) throws SQLException - { - int sql_type = getField(column).getSQLType(); - - switch (sql_type) - { - case Types.SMALLINT: - return 0; - case Types.INTEGER: - return 0; - case Types.REAL: - return 8; - case Types.FLOAT: - return 16; - case Types.DOUBLE: - return 16; - default: - throw new SQLException("no scale for non-numeric data types"); - } - } - - /** - * Whats a column's table's name? How do I find this out? Both - * getSchemaName() and getCatalogName() rely on knowing the table - * Name, so we need this before we can work on them. - * - * @param column the first column is 1, the second is 2... - * @return column name, or "" if not applicable - * @exception SQLException if a database access error occurs - */ - public String getTableName(int column) throws SQLException - { - return ""; - } - - /** - * What's a column's table's catalog name? As with getSchemaName(), - * we can say that if getTableName() returns n/a, then we can too - - * otherwise, we need to work on it. - * - * @param column the first column is 1, the second is 2... - * @return catalog name, or "" if not applicable - * @exception SQLException if a database access error occurs - */ - 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. - } - - /** - * What is a column's SQL Type? (java.sql.Type int) - * - * @param column the first column is 1, the second is 2, etc. - * @return the java.sql.Type value - * @exception SQLException if a database access error occurs - * @see postgresql.Field#getSQLType - * @see java.sql.Types - */ - public int getColumnType(int column) throws SQLException - { - return getField(column).getSQLType(); - } - - /** - * Whats is the column's data source specific type name? - * - * @param column the first column is 1, the second is 2, etc. - * @return the type name - * @exception SQLException if a database access error occurs - */ - public String getColumnTypeName(int column) throws SQLException - { - return getField(column).getTypeName(); - } - - /** - * Is the column definitely not writable? In reality, we would - * have to check the GRANT/REVOKE stuff for this to be effective, - * and I haven't really looked into that yet, so this will get - * re-visited. - * - * @param column the first column is 1, the second is 2, etc. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isReadOnly(int column) throws SQLException - { - return false; - } - - /** - * Is it possible for a write on the column to succeed? Again, we - * would in reality have to check the GRANT/REVOKE stuff, which - * I haven't worked with as yet. However, if it isn't ReadOnly, then - * it is obviously writable. - * - * @param column the first column is 1, the second is 2, etc. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isWritable(int column) throws SQLException - { - if (isReadOnly(column)) - return true; - else - return false; - } - - /** - * Will a write on this column definately succeed? Hmmm...this - * is a bad one, since the two preceding functions have not been - * really defined. I cannot tell is the short answer. I thus - * return isWritable() just to give us an idea. - * - * @param column the first column is 1, the second is 2, etc.. - * @return true if so - * @exception SQLException if a database access error occurs - */ - public boolean isDefinitelyWritable(int column) throws SQLException - { - return isWritable(column); - } - - // ******************************************************** - // END OF PUBLIC INTERFACE - // ******************************************************** - - /** - * For several routines in this package, we need to convert - * a columnIndex into a Field[] descriptor. Rather than do - * the same code several times, here it is. - * - * @param columnIndex the first column is 1, the second is 2... - * @return the Field description - * @exception SQLException if a database access error occurs - */ - private Field getField(int columnIndex) throws SQLException - { - if (columnIndex < 1 || columnIndex > fields.length) - throw new SQLException("Column index out of range"); - return fields[columnIndex - 1]; - } + Vector rows; + Field[] fields; + + /** + * Initialise for a result with a tuple set and + * a field descriptor set + * + * @param rows the Vector of rows returned by the ResultSet + * @param fields the array of field descriptors + */ + public ResultSetMetaData(Vector rows, Field[] fields) + { + this.rows = rows; + this.fields = fields; + } + + /** + * Whats the number of columns in the ResultSet? + * + * @return the number + * @exception SQLException if a database access error occurs + */ + public int getColumnCount() throws SQLException + { + return fields.length; + } + + /** + * Is the column automatically numbered (and thus read-only) + * I believe that PostgreSQL does not support this feature. + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isAutoIncrement(int column) throws SQLException + { + return false; + } + + /** + * Does a column's case matter? ASSUMPTION: Any field that is + * not obviously case insensitive is assumed to be case sensitive + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isCaseSensitive(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; + default: + return true; + } + } + + /** + * Can the column be used in a WHERE clause? Basically for + * this, I split the functions into two types: recognised + * types (which are always useable), and OTHER types (which + * may or may not be useable). The OTHER types, for now, I + * will assume they are useable. We should really query the + * catalog to see if they are useable. + * + * @param column the first column is 1, the second is 2... + * @return true if they can be used in a WHERE clause + * @exception SQLException if a database access error occurs + */ + public boolean isSearchable(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + // This switch is pointless, I know - but it is a set-up + // for further expansion. + switch (sql_type) + { + case Types.OTHER: + return true; + default: + return true; + } + } + + /** + * Is the column a cash value? 6.1 introduced the cash/money + * type, which haven't been incorporated as of 970414, so I + * just check the type name for both 'cash' and 'money' + * + * @param column the first column is 1, the second is 2... + * @return true if its a cash column + * @exception SQLException if a database access error occurs + */ + public boolean isCurrency(int column) throws SQLException + { + String type_name = getField(column).getTypeName(); + + if (type_name.equals("cash")) + return true; + if (type_name.equals("money")) + return true; + return false; + } + + /** + * Can you put a NULL in this column? I think this is always + * true in 6.1's case. It would only be false if the field had + * been defined NOT NULL (system catalogs could be queried?) + * + * @param column the first column is 1, the second is 2... + * @return one of the columnNullable values + * @exception SQLException if a database access error occurs + */ + public int isNullable(int column) throws SQLException + { + return columnNullable; // We can always put NULL in + } + + /** + * Is the column a signed number? In PostgreSQL, all numbers + * are signed, so this is trivial. However, strings are not + * signed (duh!) + * + * @param column the first column is 1, the second is 2... + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isSigned(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + case Types.INTEGER: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + return true; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return false; // I don't know about these? + default: + return false; + } + } + + /** + * What is the column's normal maximum width in characters? + * + * @param column the first column is 1, the second is 2, etc. + * @return the maximum width + * @exception SQLException if a database access error occurs + */ + public int getColumnDisplaySize(int column) throws SQLException + { + int max = getColumnLabel(column).length(); + int i; + + for (i = 0 ; i < rows.size(); ++i) + { + byte[][] x = (byte[][])(rows.elementAt(i)); + int xl = x[column - 1].length; + if (xl > max) + max = xl; + } + return max; + } + + /** + * What is the suggested column title for use in printouts and + * displays? We suggest the ColumnName! + * + * @param column the first column is 1, the second is 2, etc. + * @return the column label + * @exception SQLException if a database access error occurs + */ + public String getColumnLabel(int column) throws SQLException + { + return getColumnName(column); + } + + /** + * What's a column's name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the column name + * @exception SQLException if a databvase access error occurs + */ + public String getColumnName(int column) throws SQLException + { + return getField(column).name; + } + + /** + * What is a column's table's schema? This relies on us knowing + * the table name....which I don't know how to do as yet. The + * JDBC specification allows us to return "" if this is not + * applicable. + * + * @param column the first column is 1, the second is 2... + * @return the Schema + * @exception SQLException if a database access error occurs + */ + 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. + } + + /** + * What is a column's number of decimal digits. + * + * @param column the first column is 1, the second is 2... + * @return the precision + * @exception SQLException if a database access error occurs + */ + public int getPrecision(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 5; + case Types.INTEGER: + return 10; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + default: + throw new SQLException("no precision for non-numeric data types."); + } + } + + /** + * What is a column's number of digits to the right of the + * decimal point? + * + * @param column the first column is 1, the second is 2... + * @return the scale + * @exception SQLException if a database access error occurs + */ + public int getScale(int column) throws SQLException + { + int sql_type = getField(column).getSQLType(); + + switch (sql_type) + { + case Types.SMALLINT: + return 0; + case Types.INTEGER: + return 0; + case Types.REAL: + return 8; + case Types.FLOAT: + return 16; + case Types.DOUBLE: + return 16; + default: + throw new SQLException("no scale for non-numeric data types"); + } + } + + /** + * Whats a column's table's name? How do I find this out? Both + * getSchemaName() and getCatalogName() rely on knowing the table + * Name, so we need this before we can work on them. + * + * @param column the first column is 1, the second is 2... + * @return column name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + public String getTableName(int column) throws SQLException + { + return ""; + } + + /** + * What's a column's table's catalog name? As with getSchemaName(), + * we can say that if getTableName() returns n/a, then we can too - + * otherwise, we need to work on it. + * + * @param column the first column is 1, the second is 2... + * @return catalog name, or "" if not applicable + * @exception SQLException if a database access error occurs + */ + 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. + } + + /** + * What is a column's SQL Type? (java.sql.Type int) + * + * @param column the first column is 1, the second is 2, etc. + * @return the java.sql.Type value + * @exception SQLException if a database access error occurs + * @see postgresql.Field#getSQLType + * @see java.sql.Types + */ + public int getColumnType(int column) throws SQLException + { + return getField(column).getSQLType(); + } + + /** + * Whats is the column's data source specific type name? + * + * @param column the first column is 1, the second is 2, etc. + * @return the type name + * @exception SQLException if a database access error occurs + */ + public String getColumnTypeName(int column) throws SQLException + { + return getField(column).getTypeName(); + } + + /** + * Is the column definitely not writable? In reality, we would + * have to check the GRANT/REVOKE stuff for this to be effective, + * and I haven't really looked into that yet, so this will get + * re-visited. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isReadOnly(int column) throws SQLException + { + return false; + } + + /** + * Is it possible for a write on the column to succeed? Again, we + * would in reality have to check the GRANT/REVOKE stuff, which + * I haven't worked with as yet. However, if it isn't ReadOnly, then + * it is obviously writable. + * + * @param column the first column is 1, the second is 2, etc. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isWritable(int column) throws SQLException + { + if (isReadOnly(column)) + return true; + else + return false; + } + + /** + * Will a write on this column definately succeed? Hmmm...this + * is a bad one, since the two preceding functions have not been + * really defined. I cannot tell is the short answer. I thus + * return isWritable() just to give us an idea. + * + * @param column the first column is 1, the second is 2, etc.. + * @return true if so + * @exception SQLException if a database access error occurs + */ + public boolean isDefinitelyWritable(int column) throws SQLException + { + return isWritable(column); + } + + // ******************************************************** + // END OF PUBLIC INTERFACE + // ******************************************************** + + /** + * For several routines in this package, we need to convert + * a columnIndex into a Field[] descriptor. Rather than do + * the same code several times, here it is. + * + * @param columnIndex the first column is 1, the second is 2... + * @return the Field description + * @exception SQLException if a database access error occurs + */ + private Field getField(int columnIndex) throws SQLException + { + if (columnIndex < 1 || columnIndex > fields.length) + throw new SQLException("Column index out of range"); + return fields[columnIndex - 1]; + } } +