
Fri Feb 17 15:11:00 GMT 2001 peter@retep.org.uk - Reduced the object overhead in PreparedStatement by reusing the same StringBuffer object throughout. Similarly SimpleDateStamp's are alse reused in a thread save manner. - Implemented in PreparedStatement: setNull(), setDate/Time/Timestamp using Calendar, setBlob(), setCharacterStream() - Clob's are now implemented in ResultSet & PreparedStatement! - Implemented a lot of DatabaseMetaData & ResultSetMetaData methods. We have about 18 unimplemented methods left in JDBC2 at the current time.
460 lines
14 KiB
Java
460 lines
14 KiB
Java
package org.postgresql.jdbc2;
|
|
|
|
// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
|
|
// If you make any modifications to this file, you must make sure that the
|
|
// changes are also made (if relevent) to the related JDBC 1 class in the
|
|
// org.postgresql.jdbc1 package.
|
|
|
|
import java.sql.*;
|
|
import java.util.Vector;
|
|
import org.postgresql.util.*;
|
|
|
|
/**
|
|
* A Statement object is used for executing a static SQL statement and
|
|
* obtaining the results produced by it.
|
|
*
|
|
* <p>Only one ResultSet per Statement can be open at any point in time.
|
|
* Therefore, if the reading of one ResultSet is interleaved with the
|
|
* reading of another, each must have been generated by different
|
|
* Statements. All statement execute methods implicitly close a
|
|
* statement's current ResultSet if an open one exists.
|
|
*
|
|
* @see java.sql.Statement
|
|
* @see ResultSet
|
|
*/
|
|
public class Statement extends org.postgresql.Statement implements java.sql.Statement
|
|
{
|
|
Connection connection; // The connection who created us
|
|
java.sql.ResultSet result = null; // The current results
|
|
SQLWarning warnings = null; // The warnings chain.
|
|
int timeout = 0; // The timeout for a query (not used)
|
|
boolean escapeProcessing = true;// escape processing flag
|
|
private Vector batch=null;
|
|
int resultsettype; // the resultset type to return
|
|
int concurrency; // is it updateable or not?
|
|
int maxrows=0; // the maximum number of rows to return 0=unlimited
|
|
|
|
/**
|
|
* Constructor for a Statement. It simply sets the connection
|
|
* that created us.
|
|
*
|
|
* @param c the Connection instantation that creates us
|
|
*/
|
|
public Statement (Connection c)
|
|
{
|
|
connection = c;
|
|
resultsettype = java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE;
|
|
concurrency = java.sql.ResultSet.CONCUR_READ_ONLY;
|
|
}
|
|
|
|
/**
|
|
* Execute a SQL statement that retruns a single ResultSet
|
|
*
|
|
* @param sql typically a static SQL SELECT statement
|
|
* @return a ResulSet that contains the data produced by the query
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public java.sql.ResultSet executeQuery(String sql) throws SQLException
|
|
{
|
|
this.execute(sql);
|
|
while (result != null && !((org.postgresql.ResultSet)result).reallyResultSet())
|
|
result = ((org.postgresql.ResultSet)result).getNext();
|
|
if (result == null)
|
|
throw new PSQLException("postgresql.stat.noresult");
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Execute a SQL INSERT, UPDATE or DELETE statement. In addition
|
|
* SQL statements that return nothing such as SQL DDL statements
|
|
* can be executed
|
|
*
|
|
* @param sql a SQL statement
|
|
* @return either a row count, or 0 for SQL commands
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public int executeUpdate(String sql) throws SQLException
|
|
{
|
|
this.execute(sql);
|
|
if (((org.postgresql.ResultSet)result).reallyResultSet())
|
|
throw new PSQLException("postgresql.stat.result");
|
|
return this.getUpdateCount();
|
|
}
|
|
|
|
/**
|
|
* In many cases, it is desirable to immediately release a
|
|
* Statement's database and JDBC resources instead of waiting
|
|
* for this to happen when it is automatically closed. The
|
|
* close method provides this immediate release.
|
|
*
|
|
* <p><B>Note:</B> A Statement is automatically closed when it is
|
|
* garbage collected. When a Statement is closed, its current
|
|
* ResultSet, if one exists, is also closed.
|
|
*
|
|
* @exception SQLException if a database access error occurs (why?)
|
|
*/
|
|
public void close() throws SQLException
|
|
{
|
|
// Force the ResultSet to close
|
|
java.sql.ResultSet rs = getResultSet();
|
|
if(rs!=null)
|
|
rs.close();
|
|
|
|
// Disasociate it from us (For Garbage Collection)
|
|
result = null;
|
|
}
|
|
|
|
/**
|
|
* The maxFieldSize limit (in bytes) is the maximum amount of
|
|
* data returned for any column value; it only applies to
|
|
* BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
|
|
* columns. If the limit is exceeded, the excess data is silently
|
|
* discarded.
|
|
*
|
|
* @return the current max column size limit; zero means unlimited
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public int getMaxFieldSize() throws SQLException
|
|
{
|
|
return 8192; // We cannot change this
|
|
}
|
|
|
|
/**
|
|
* Sets the maxFieldSize - NOT! - We throw an SQLException just
|
|
* to inform them to stop doing this.
|
|
*
|
|
* @param max the new max column size limit; zero means unlimited
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setMaxFieldSize(int max) throws SQLException
|
|
{
|
|
throw new PSQLException("postgresql.stat.maxfieldsize");
|
|
}
|
|
|
|
/**
|
|
* The maxRows limit is set to limit the number of rows that
|
|
* any ResultSet can contain. If the limit is exceeded, the
|
|
* excess rows are silently dropped.
|
|
*
|
|
* @return the current maximum row limit; zero means unlimited
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public int getMaxRows() throws SQLException
|
|
{
|
|
return maxrows;
|
|
}
|
|
|
|
/**
|
|
* Set the maximum number of rows
|
|
*
|
|
* @param max the new max rows limit; zero means unlimited
|
|
* @exception SQLException if a database access error occurs
|
|
* @see getMaxRows
|
|
*/
|
|
public void setMaxRows(int max) throws SQLException
|
|
{
|
|
maxrows = max;
|
|
}
|
|
|
|
/**
|
|
* If escape scanning is on (the default), the driver will do escape
|
|
* substitution before sending the SQL to the database.
|
|
*
|
|
* @param enable true to enable; false to disable
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setEscapeProcessing(boolean enable) throws SQLException
|
|
{
|
|
escapeProcessing = enable;
|
|
}
|
|
|
|
/**
|
|
* The queryTimeout limit is the number of seconds the driver
|
|
* will wait for a Statement to execute. If the limit is
|
|
* exceeded, a SQLException is thrown.
|
|
*
|
|
* @return the current query timeout limit in seconds; 0 = unlimited
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public int getQueryTimeout() throws SQLException
|
|
{
|
|
return timeout;
|
|
}
|
|
|
|
/**
|
|
* Sets the queryTimeout limit
|
|
*
|
|
* @param seconds - the new query timeout limit in seconds
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setQueryTimeout(int seconds) throws SQLException
|
|
{
|
|
timeout = seconds;
|
|
}
|
|
|
|
/**
|
|
* Cancel can be used by one thread to cancel a statement that
|
|
* is being executed by another thread. However, PostgreSQL is
|
|
* a sync. sort of thing, so this really has no meaning - we
|
|
* define it as a no-op (i.e. you can't cancel, but there is no
|
|
* error if you try.)
|
|
*
|
|
* 6.4 introduced a cancel operation, but we have not implemented it
|
|
* yet. Sometime before 6.5, this method will be implemented.
|
|
*
|
|
* @exception SQLException only because thats the spec.
|
|
*/
|
|
public void cancel() throws SQLException
|
|
{
|
|
// No-op
|
|
}
|
|
|
|
/**
|
|
* The first warning reported by calls on this Statement is
|
|
* returned. A Statement's execute methods clear its SQLWarning
|
|
* chain. Subsequent Statement warnings will be chained to this
|
|
* SQLWarning.
|
|
*
|
|
* <p>The Warning chain is automatically cleared each time a statement
|
|
* is (re)executed.
|
|
*
|
|
* <p><B>Note:</B> If you are processing a ResultSet then any warnings
|
|
* associated with ResultSet reads will be chained on the ResultSet
|
|
* object.
|
|
*
|
|
* @return the first SQLWarning on 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 Statement.
|
|
*
|
|
* @exception SQLException if a database access error occurs (why?)
|
|
*/
|
|
public void clearWarnings() throws SQLException
|
|
{
|
|
warnings = null;
|
|
}
|
|
|
|
/**
|
|
* setCursorName defines the SQL cursor name that will be used by
|
|
* subsequent execute methods. This name can then be used in SQL
|
|
* positioned update/delete statements to identify the current row
|
|
* in the ResultSet generated by this statement. If a database
|
|
* doesn't support positioned update/delete, this method is a
|
|
* no-op.
|
|
*
|
|
* <p><B>Note:</B> By definition, positioned update/delete execution
|
|
* must be done by a different Statement than the one which
|
|
* generated the ResultSet being used for positioning. Also, cursor
|
|
* names must be unique within a Connection.
|
|
*
|
|
* <p>We throw an additional constriction. There can only be one
|
|
* cursor active at any one time.
|
|
*
|
|
* @param name the new cursor name
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public void setCursorName(String name) throws SQLException
|
|
{
|
|
connection.setCursorName(name);
|
|
}
|
|
|
|
/**
|
|
* Execute a SQL statement that may return multiple results. We
|
|
* don't have to worry about this since we do not support multiple
|
|
* ResultSets. You can use getResultSet or getUpdateCount to
|
|
* retrieve the result.
|
|
*
|
|
* @param sql any SQL statement
|
|
* @return true if the next result is a ResulSet, false if it is
|
|
* an update count or there are no more results
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public boolean execute(String sql) throws SQLException
|
|
{
|
|
if(escapeProcessing)
|
|
sql=connection.EscapeSQL(sql);
|
|
|
|
// New in 7.1, if we have a previous resultset then force it to close
|
|
// This brings us nearer to compliance, and helps memory management.
|
|
// Internal stuff will call ExecSQL directly, bypassing this.
|
|
if(result!=null) {
|
|
java.sql.ResultSet rs = getResultSet();
|
|
if(rs!=null)
|
|
rs.close();
|
|
}
|
|
|
|
// New in 7.1, pass Statement so that ExecSQL can customise to it
|
|
result = connection.ExecSQL(sql,this);
|
|
|
|
// New in 7.1, required for ResultSet.getStatement() to work
|
|
((org.postgresql.jdbc2.ResultSet)result).setStatement(this);
|
|
|
|
return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet());
|
|
}
|
|
|
|
/**
|
|
* getResultSet returns the current result as a ResultSet. It
|
|
* should only be called once per result.
|
|
*
|
|
* @return the current result set; null if there are no more
|
|
* @exception SQLException if a database access error occurs (why?)
|
|
*/
|
|
public java.sql.ResultSet getResultSet() throws SQLException
|
|
{
|
|
if (result != null && ((org.postgresql.ResultSet)result).reallyResultSet())
|
|
return result;
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* getUpdateCount returns the current result as an update count,
|
|
* if the result is a ResultSet or there are no more results, -1
|
|
* is returned. It should only be called once per result.
|
|
*
|
|
* @return the current result as an update count.
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public int getUpdateCount() throws SQLException
|
|
{
|
|
if (result == null) return -1;
|
|
if (((org.postgresql.ResultSet)result).reallyResultSet()) return -1;
|
|
return ((org.postgresql.ResultSet)result).getResultCount();
|
|
}
|
|
|
|
/**
|
|
* getMoreResults moves to a Statement's next result. If it returns
|
|
* true, this result is a ResulSet.
|
|
*
|
|
* @return true if the next ResultSet is valid
|
|
* @exception SQLException if a database access error occurs
|
|
*/
|
|
public boolean getMoreResults() throws SQLException
|
|
{
|
|
result = ((org.postgresql.ResultSet)result).getNext();
|
|
return (result != null && ((org.postgresql.ResultSet)result).reallyResultSet());
|
|
}
|
|
|
|
/**
|
|
* Returns the status message from the current Result.<p>
|
|
* This is used internally by the driver.
|
|
*
|
|
* @return status message from backend
|
|
*/
|
|
public String getResultStatusString()
|
|
{
|
|
if(result == null)
|
|
return null;
|
|
return ((org.postgresql.ResultSet)result).getStatusString();
|
|
}
|
|
|
|
// ** JDBC 2 Extensions **
|
|
|
|
public void addBatch(String sql) throws SQLException
|
|
{
|
|
if(batch==null)
|
|
batch=new Vector();
|
|
batch.addElement(sql);
|
|
}
|
|
|
|
public void clearBatch() throws SQLException
|
|
{
|
|
if(batch!=null)
|
|
batch.removeAllElements();
|
|
}
|
|
|
|
public int[] executeBatch() throws SQLException
|
|
{
|
|
if(batch==null || batch.isEmpty())
|
|
throw new PSQLException("postgresql.stat.batch.empty");
|
|
|
|
int size=batch.size();
|
|
int[] result=new int[size];
|
|
int i=0;
|
|
this.execute("begin"); // PTM: check this when autoCommit is false
|
|
try {
|
|
for(i=0;i<size;i++)
|
|
result[i]=this.executeUpdate((String)batch.elementAt(i));
|
|
this.execute("commit"); // PTM: check this
|
|
} catch(SQLException e) {
|
|
this.execute("abort"); // PTM: check this
|
|
throw new PSQLException("postgresql.stat.batch.error",new Integer(i),batch.elementAt(i));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public java.sql.Connection getConnection() throws SQLException
|
|
{
|
|
return (java.sql.Connection)connection;
|
|
}
|
|
|
|
public int getFetchDirection() throws SQLException
|
|
{
|
|
throw new PSQLException("postgresql.psqlnotimp");
|
|
}
|
|
|
|
public int getFetchSize() throws SQLException
|
|
{
|
|
// This one can only return a valid value when were a cursor?
|
|
throw org.postgresql.Driver.notImplemented();
|
|
}
|
|
|
|
public int getResultSetConcurrency() throws SQLException
|
|
{
|
|
// new in 7.1
|
|
return concurrency;
|
|
}
|
|
|
|
public int getResultSetType() throws SQLException
|
|
{
|
|
// new in 7.1
|
|
return resultsettype;
|
|
}
|
|
|
|
public void setFetchDirection(int direction) throws SQLException
|
|
{
|
|
throw org.postgresql.Driver.notImplemented();
|
|
}
|
|
|
|
public void setFetchSize(int rows) throws SQLException
|
|
{
|
|
throw org.postgresql.Driver.notImplemented();
|
|
}
|
|
|
|
/**
|
|
* New in 7.1
|
|
*/
|
|
public void setResultSetConcurrency(int value) throws SQLException
|
|
{
|
|
concurrency=value;
|
|
}
|
|
|
|
/**
|
|
* New in 7.1
|
|
*/
|
|
public void setResultSetType(int value) throws SQLException
|
|
{
|
|
resultsettype=value;
|
|
}
|
|
|
|
/**
|
|
* New in 7.1: Returns the Last inserted oid. This should be used, rather
|
|
* than the old method using getResultSet, which for executeUpdate returns
|
|
* null.
|
|
* @return OID of last insert
|
|
*/
|
|
public int getInsertedOID() throws SQLException
|
|
{
|
|
if(result!=null)
|
|
return ((org.postgresql.ResultSet)result).getInsertedOID();
|
|
return 0;
|
|
}
|
|
|
|
}
|