mirror of
https://github.com/postgres/postgres.git
synced 2025-05-01 01:04:50 +03:00
of duplicated code between the jdbc1 and jdbc2. This checkin restructures the code so that the duplication is removed so that the jdbc3 support can be added without adding yet another copy of everything. Also many classes were renamed to avoid confusion with multiple different objects having the same name. The timestamp tests were also updated to add support for testing timestamp without time zone in addition to timestamp with time zone Modified Files: jdbc/Makefile jdbc/build.xml jdbc/example/ImageViewer.java jdbc/example/basic.java jdbc/example/blobtest.java jdbc/example/threadsafe.java jdbc/org/postgresql/Driver.java.in jdbc/org/postgresql/Field.java jdbc/org/postgresql/core/QueryExecutor.java jdbc/org/postgresql/fastpath/Fastpath.java jdbc/org/postgresql/jdbc1/CallableStatement.java jdbc/org/postgresql/jdbc1/DatabaseMetaData.java jdbc/org/postgresql/jdbc1/PreparedStatement.java jdbc/org/postgresql/jdbc2/Array.java jdbc/org/postgresql/jdbc2/CallableStatement.java jdbc/org/postgresql/jdbc2/DatabaseMetaData.java jdbc/org/postgresql/jdbc2/PreparedStatement.java jdbc/org/postgresql/jdbc2/UpdateableResultSet.java jdbc/org/postgresql/largeobject/LargeObjectManager.java jdbc/org/postgresql/largeobject/PGblob.java jdbc/org/postgresql/largeobject/PGclob.java jdbc/org/postgresql/test/jdbc2/BlobTest.java jdbc/org/postgresql/test/jdbc2/ConnectionTest.java jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java jdbc/org/postgresql/test/jdbc2/TimestampTest.java jdbc/org/postgresql/test/jdbc2/UpdateableResultTest.java jdbc/org/postgresql/util/Serialize.java Added Files: jdbc/org/postgresql/PGConnection.java jdbc/org/postgresql/PGStatement.java jdbc/org/postgresql/jdbc1/AbstractJdbc1Connection.java jdbc/org/postgresql/jdbc1/AbstractJdbc1ResultSet.java jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java jdbc/org/postgresql/jdbc1/Jdbc1Connection.java jdbc/org/postgresql/jdbc1/Jdbc1ResultSet.java jdbc/org/postgresql/jdbc1/Jdbc1Statement.java jdbc/org/postgresql/jdbc2/AbstractJdbc2Connection.java jdbc/org/postgresql/jdbc2/AbstractJdbc2ResultSet.java jdbc/org/postgresql/jdbc2/AbstractJdbc2Statement.java jdbc/org/postgresql/jdbc2/Jdbc2Connection.java jdbc/org/postgresql/jdbc2/Jdbc2ResultSet.java jdbc/org/postgresql/jdbc2/Jdbc2Statement.java Removed Files: jdbc/org/postgresql/Connection.java jdbc/org/postgresql/ResultSet.java jdbc/org/postgresql/Statement.java jdbc/org/postgresql/jdbc1/Connection.java jdbc/org/postgresql/jdbc1/ResultSet.java jdbc/org/postgresql/jdbc1/Statement.java jdbc/org/postgresql/jdbc2/Connection.java jdbc/org/postgresql/jdbc2/ResultSet.java jdbc/org/postgresql/jdbc2/Statement.java
1390 lines
35 KiB
Java
1390 lines
35 KiB
Java
package org.postgresql.jdbc2;
|
|
|
|
// IMPORTANT NOTE: This is the begining of supporting updateable ResultSets.
|
|
//
|
|
// This is because here we should be updateable, so any unimplemented methods
|
|
// must say so.
|
|
//
|
|
// Also you'll notice that the String columnName based calls are not present.
|
|
// They are not required as they are in the super class.
|
|
//
|
|
|
|
import java.lang.*;
|
|
import java.io.*;
|
|
import java.math.*;
|
|
import java.text.*;
|
|
import java.util.*;
|
|
import java.sql.*;
|
|
import org.postgresql.Field;
|
|
import org.postgresql.largeobject.*;
|
|
import org.postgresql.util.*;
|
|
import org.postgresql.Driver;
|
|
|
|
/*
|
|
* @see ResultSet
|
|
* @see ResultSetMetaData
|
|
* @see java.sql.ResultSet
|
|
*/
|
|
public class UpdateableResultSet extends org.postgresql.jdbc2.Jdbc2ResultSet
|
|
{
|
|
|
|
|
|
class PrimaryKey
|
|
{
|
|
int index; // where in the result set is this primaryKey
|
|
String name; // what is the columnName of this primary Key
|
|
|
|
PrimaryKey( int index, String name)
|
|
{
|
|
this.index = index;
|
|
this.name = name;
|
|
}
|
|
Object getValue() throws SQLException
|
|
{
|
|
return getObject(index);
|
|
}
|
|
};
|
|
|
|
private boolean usingOID = false; // are we using the OID for the primary key?
|
|
|
|
private Vector primaryKeys; // list of primary keys
|
|
|
|
private int numKeys = 0;
|
|
|
|
private boolean singleTable = false;
|
|
|
|
protected String tableName = null;
|
|
|
|
/**
|
|
* PreparedStatement used to delete data
|
|
*/
|
|
|
|
protected java.sql.PreparedStatement updateStatement = null;
|
|
|
|
/**
|
|
* PreparedStatement used to insert data
|
|
*/
|
|
|
|
protected java.sql.PreparedStatement insertStatement = null;
|
|
|
|
/**
|
|
* PreparedStatement used to delete data
|
|
*/
|
|
|
|
protected java.sql.PreparedStatement deleteStatement = null;
|
|
|
|
|
|
/**
|
|
* PreparedStatement used to refresh data
|
|
*/
|
|
private java.sql.PreparedStatement selectStatement = null;
|
|
|
|
|
|
/**
|
|
* Is this result set updateable?
|
|
*/
|
|
|
|
protected boolean updateable = false;
|
|
|
|
/**
|
|
* Are we in the middle of doing updates to the current row?
|
|
*/
|
|
|
|
protected boolean doingUpdates = false;
|
|
|
|
|
|
/**
|
|
* Are we on the insert row?
|
|
*/
|
|
|
|
protected boolean onInsertRow = false;
|
|
|
|
|
|
protected Hashtable updateValues = new Hashtable();
|
|
|
|
// The Row Buffer will be used to cache updated rows..then we shall sync this with the rows vector
|
|
|
|
|
|
/*
|
|
* 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 UpdateableResultSet(Jdbc2Connection conn, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor)
|
|
{
|
|
super(conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void cancelRowUpdates() throws SQLException
|
|
{
|
|
if (doingUpdates)
|
|
{
|
|
doingUpdates = false;
|
|
|
|
clearRowBuffer();
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void deleteRow() throws SQLException
|
|
{
|
|
if ( !isUpdateable() )
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.notupdateable" );
|
|
}
|
|
|
|
if (onInsertRow)
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.oninsertrow" );
|
|
}
|
|
|
|
if (rows.size() == 0)
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.emptydelete" );
|
|
}
|
|
if (isBeforeFirst())
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.beforestartdelete" );
|
|
}
|
|
if (isAfterLast())
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.afterlastdelete" );
|
|
}
|
|
|
|
|
|
int numKeys = primaryKeys.size();
|
|
if ( deleteStatement == null )
|
|
{
|
|
|
|
|
|
StringBuffer deleteSQL= new StringBuffer("DELETE FROM " ).append(tableName).append(" where " );
|
|
|
|
for ( int i=0; i < numKeys ; i++ )
|
|
{
|
|
deleteSQL.append( ((PrimaryKey)primaryKeys.get(i)).name ).append( " = ? " );
|
|
if ( i < numKeys-1 )
|
|
{
|
|
deleteSQL.append( " and " );
|
|
}
|
|
}
|
|
|
|
deleteStatement = ((java.sql.Connection)connection).prepareStatement(deleteSQL.toString());
|
|
}
|
|
deleteStatement.clearParameters();
|
|
|
|
for ( int i =0; i < numKeys; i++ )
|
|
{
|
|
deleteStatement.setObject(i+1, ((PrimaryKey)primaryKeys.get(i)).getValue());
|
|
}
|
|
|
|
|
|
deleteStatement.executeUpdate();
|
|
|
|
rows.removeElementAt(current_row);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @return
|
|
* @throws SQLException
|
|
*/
|
|
public int getConcurrency() throws SQLException
|
|
{
|
|
// New in 7.1 - The updateable ResultSet class will now return
|
|
// CONCURuPDATEABLE.
|
|
return CONCUR_UPDATABLE;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
|
|
public synchronized void insertRow() throws SQLException
|
|
{
|
|
if ( !isUpdateable() )
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.notupdateable" );
|
|
}
|
|
|
|
if (!onInsertRow)
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.notoninsertrow" );
|
|
}
|
|
else
|
|
{
|
|
|
|
// loop through the keys in the insertTable and create the sql statement
|
|
// we have to create the sql every time since the user could insert different
|
|
// columns each time
|
|
|
|
StringBuffer insertSQL=new StringBuffer("INSERT INTO ").append(tableName).append(" (");
|
|
StringBuffer paramSQL = new StringBuffer(") values (" );
|
|
|
|
Enumeration columnNames = updateValues.keys();
|
|
int numColumns = updateValues.size();
|
|
|
|
for ( int i=0; columnNames.hasMoreElements() ; i++ )
|
|
{
|
|
String columnName = (String)columnNames.nextElement();
|
|
|
|
insertSQL.append( columnName );
|
|
if ( i < numColumns - 1 )
|
|
{
|
|
insertSQL.append(", ");
|
|
paramSQL.append("?,");
|
|
}
|
|
else
|
|
{
|
|
paramSQL.append("?)");
|
|
}
|
|
|
|
}
|
|
|
|
insertSQL.append(paramSQL.toString());
|
|
insertStatement = ((java.sql.Connection)connection).prepareStatement(insertSQL.toString());
|
|
|
|
Enumeration keys = updateValues.keys();
|
|
|
|
for( int i=1; keys.hasMoreElements() ; i++)
|
|
{
|
|
String key = (String)keys.nextElement();
|
|
insertStatement.setObject(i, updateValues.get( key ) );
|
|
}
|
|
|
|
insertStatement.executeUpdate();
|
|
|
|
if ( usingOID )
|
|
{
|
|
// we have to get the last inserted OID and put it in the resultset
|
|
|
|
long insertedOID = ((AbstractJdbc2Statement)insertStatement).getLastOID();
|
|
|
|
updateValues.put("oid", new Long(insertedOID) );
|
|
|
|
}
|
|
|
|
// update the underlying row to the new inserted data
|
|
updateRowBuffer();
|
|
|
|
rows.addElement(rowBuffer);
|
|
|
|
// we should now reflect the current data in this_row
|
|
// that way getXXX will get the newly inserted data
|
|
this_row = rowBuffer;
|
|
|
|
// need to clear this in case of another insert
|
|
clearRowBuffer();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
|
|
public synchronized void moveToCurrentRow() throws SQLException
|
|
{
|
|
this_row = (byte [][])rows.elementAt(current_row);
|
|
|
|
rowBuffer=new byte[this_row.length][];
|
|
System.arraycopy(this_row,0,rowBuffer,0,this_row.length);
|
|
|
|
onInsertRow = false;
|
|
doingUpdates = false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void moveToInsertRow() throws SQLException
|
|
{
|
|
// only sub-classes implement CONCURuPDATEABLE
|
|
if (!updateable)
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.notupdateable" );
|
|
}
|
|
|
|
if (insertStatement != null)
|
|
{
|
|
insertStatement = null;
|
|
}
|
|
|
|
|
|
// make sure the underlying data is null
|
|
clearRowBuffer();
|
|
|
|
onInsertRow = true;
|
|
doingUpdates = false;
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
private synchronized void clearRowBuffer() throws SQLException
|
|
{
|
|
// rowBuffer is the temporary storage for the row
|
|
rowBuffer=new byte[fields.length][];
|
|
|
|
// clear the updateValues hashTable for the next set of updates
|
|
updateValues.clear();
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @return
|
|
* @throws SQLException
|
|
*/
|
|
public boolean rowDeleted() throws SQLException
|
|
{
|
|
// only sub-classes implement CONCURuPDATEABLE
|
|
throw Driver.notImplemented();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return
|
|
* @throws SQLException
|
|
*/
|
|
public boolean rowInserted() throws SQLException
|
|
{
|
|
// only sub-classes implement CONCURuPDATEABLE
|
|
throw Driver.notImplemented();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return
|
|
* @throws SQLException
|
|
*/
|
|
public boolean rowUpdated() throws SQLException
|
|
{
|
|
// only sub-classes implement CONCURuPDATEABLE
|
|
throw Driver.notImplemented();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @param length
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateAsciiStream(int columnIndex,
|
|
java.io.InputStream x,
|
|
int length
|
|
) throws SQLException
|
|
{
|
|
|
|
byte[] theData=null;
|
|
|
|
try
|
|
{
|
|
x.read(theData,0,length);
|
|
}
|
|
catch (NullPointerException ex )
|
|
{
|
|
throw new PSQLException("postgresql.updateable.inputstream");
|
|
}
|
|
catch (IOException ie)
|
|
{
|
|
throw new PSQLException("postgresql.updateable.ioerror" + ie);
|
|
}
|
|
|
|
doingUpdates = !onInsertRow;
|
|
|
|
updateValues.put( fields[columnIndex-1].getName(), theData );
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateBigDecimal(int columnIndex,
|
|
java.math.BigDecimal x )
|
|
throws SQLException
|
|
{
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @param length
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateBinaryStream(int columnIndex,
|
|
java.io.InputStream x,
|
|
int length
|
|
) throws SQLException
|
|
{
|
|
|
|
|
|
byte[] theData=null;
|
|
|
|
try {
|
|
x.read(theData,0,length);
|
|
|
|
}
|
|
catch( NullPointerException ex )
|
|
{
|
|
throw new PSQLException("postgresql.updateable.inputstream");
|
|
}
|
|
catch (IOException ie)
|
|
{
|
|
throw new PSQLException("postgresql.updateable.ioerror" + ie);
|
|
}
|
|
|
|
doingUpdates = !onInsertRow;
|
|
|
|
updateValues.put( fields[columnIndex-1].getName(), theData );
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException
|
|
{
|
|
|
|
if ( Driver.logDebug ) Driver.debug("updating boolean "+fields[columnIndex-1].getName()+"="+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), new Boolean(x) );
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateByte(int columnIndex, byte x) throws SQLException
|
|
{
|
|
|
|
doingUpdates = true;
|
|
updateValues.put( fields[columnIndex-1].getName(), String.valueOf(x) );
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException
|
|
{
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param columnIndex
|
|
* @param x
|
|
* @param length
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateCharacterStream(int columnIndex,
|
|
java.io.Reader x,
|
|
int length
|
|
) throws SQLException
|
|
{
|
|
|
|
|
|
char[] theData=null;
|
|
|
|
try
|
|
{
|
|
x.read(theData,0,length);
|
|
|
|
}
|
|
catch (NullPointerException ex)
|
|
{
|
|
throw new PSQLException("postgresql.updateable.inputstream");
|
|
}
|
|
catch (IOException ie)
|
|
{
|
|
throw new PSQLException("postgresql.updateable.ioerror" + ie);
|
|
}
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), theData);
|
|
|
|
}
|
|
|
|
public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException
|
|
{
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
}
|
|
|
|
public synchronized void updateDouble(int columnIndex, double x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("updating double "+fields[columnIndex-1].getName()+"="+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), new Double(x) );
|
|
|
|
}
|
|
|
|
public synchronized void updateFloat(int columnIndex, float x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("updating float "+fields[columnIndex-1].getName()+"="+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
|
|
updateValues.put( fields[columnIndex-1].getName(), new Float(x) );
|
|
|
|
}
|
|
|
|
public synchronized void updateInt(int columnIndex, int x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("updating int "+fields[columnIndex-1].getName()+"="+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), new Integer(x) );
|
|
|
|
}
|
|
|
|
public synchronized void updateLong(int columnIndex, long x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("updating long "+fields[columnIndex-1].getName()+"="+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), new Long(x) );
|
|
|
|
}
|
|
|
|
public synchronized void updateNull(int columnIndex) throws SQLException
|
|
{
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), null);
|
|
|
|
|
|
}
|
|
|
|
public synchronized void updateObject(int columnIndex, Object x) throws SQLException
|
|
{
|
|
|
|
|
|
if ( Driver.logDebug ) Driver.debug("updating object " + fields[columnIndex-1].getName() + " = " + x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
}
|
|
|
|
public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException
|
|
{
|
|
|
|
this.updateObject(columnIndex, x);
|
|
|
|
}
|
|
|
|
|
|
public void refreshRow() throws SQLException
|
|
{
|
|
if ( !isUpdateable() )
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.notupdateable" );
|
|
}
|
|
|
|
try
|
|
{
|
|
StringBuffer selectSQL = new StringBuffer( "select ");
|
|
|
|
final int numColumns = java.lang.reflect.Array.getLength(fields);
|
|
|
|
for (int i=0; i < numColumns ; i++ )
|
|
{
|
|
|
|
selectSQL.append( fields[i].getName() );
|
|
|
|
if ( i < numColumns - 1 )
|
|
{
|
|
|
|
selectSQL.append(", ");
|
|
}
|
|
|
|
}
|
|
selectSQL.append(" from " ).append(tableName).append(" where ");
|
|
|
|
int numKeys = primaryKeys.size();
|
|
|
|
for ( int i = 0; i < numKeys; i++ )
|
|
{
|
|
|
|
PrimaryKey primaryKey = ((PrimaryKey)primaryKeys.get(i));
|
|
selectSQL.append(primaryKey.name).append("= ?");
|
|
|
|
if ( i < numKeys -1 )
|
|
{
|
|
selectSQL.append(" and ");
|
|
}
|
|
}
|
|
if ( Driver.logDebug ) Driver.debug("selecting "+ selectSQL.toString());
|
|
selectStatement = ((java.sql.Connection)connection).prepareStatement(selectSQL.toString());
|
|
|
|
|
|
for( int j=0, i=1; j < numKeys; j++, i++)
|
|
{
|
|
selectStatement.setObject( i, ((PrimaryKey)primaryKeys.get(j)).getValue() );
|
|
}
|
|
|
|
Jdbc2ResultSet rs = (Jdbc2ResultSet) selectStatement.executeQuery();
|
|
|
|
if( rs.first() )
|
|
{
|
|
rowBuffer = rs.rowBuffer;
|
|
}
|
|
|
|
rows.setElementAt( rowBuffer, current_row );
|
|
if ( Driver.logDebug ) Driver.debug("done updates");
|
|
|
|
rs.close();
|
|
selectStatement.close();
|
|
selectStatement = null;
|
|
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug(e.getClass().getName()+e);
|
|
throw new SQLException( e.getMessage() );
|
|
}
|
|
|
|
}
|
|
/**
|
|
*
|
|
* @throws SQLException
|
|
*/
|
|
public synchronized void updateRow() throws SQLException
|
|
{
|
|
if ( !isUpdateable() )
|
|
{
|
|
throw new PSQLException( "postgresql.updateable.notupdateable" );
|
|
}
|
|
|
|
if (doingUpdates)
|
|
{
|
|
|
|
try
|
|
{
|
|
|
|
StringBuffer updateSQL=new StringBuffer("UPDATE "+tableName+" SET ");
|
|
|
|
int numColumns = updateValues.size();
|
|
Enumeration columns = updateValues.keys();
|
|
|
|
for (int i=0; columns.hasMoreElements() ; i++ )
|
|
{
|
|
|
|
String column = (String)columns.nextElement();
|
|
updateSQL.append( column + "= ?");
|
|
|
|
if ( i < numColumns - 1 )
|
|
{
|
|
|
|
updateSQL.append(", ");
|
|
}
|
|
|
|
}
|
|
updateSQL.append( " WHERE " );
|
|
|
|
int numKeys = primaryKeys.size();
|
|
|
|
for ( int i = 0; i < numKeys; i++ )
|
|
{
|
|
|
|
PrimaryKey primaryKey = ((PrimaryKey)primaryKeys.get(i));
|
|
updateSQL.append(primaryKey.name).append("= ?");
|
|
|
|
if ( i < numKeys -1 )
|
|
{
|
|
updateSQL.append(" and ");
|
|
}
|
|
}
|
|
if ( Driver.logDebug ) Driver.debug("updating "+updateSQL.toString());
|
|
updateStatement = ((java.sql.Connection)connection).prepareStatement(updateSQL.toString());
|
|
|
|
int i = 0;
|
|
Iterator iterator = updateValues.values().iterator();
|
|
for (; iterator.hasNext(); i++)
|
|
{
|
|
updateStatement.setObject( i+1, iterator.next() );
|
|
|
|
}
|
|
for( int j=0; j < numKeys; j++, i++)
|
|
{
|
|
updateStatement.setObject( i+1, ((PrimaryKey)primaryKeys.get(j)).getValue() );
|
|
}
|
|
|
|
updateStatement.executeUpdate();
|
|
updateStatement.close();
|
|
|
|
updateStatement = null;
|
|
updateRowBuffer();
|
|
|
|
|
|
if ( Driver.logDebug ) Driver.debug("copying data");
|
|
System.arraycopy(rowBuffer,0,this_row,0,rowBuffer.length);
|
|
|
|
rows.setElementAt( rowBuffer, current_row );
|
|
if ( Driver.logDebug ) Driver.debug("done updates");
|
|
|
|
doingUpdates = false;
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug(e.getClass().getName()+e);
|
|
throw new SQLException( e.getMessage() );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public synchronized void updateShort(int columnIndex, short x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("in update Short "+fields[columnIndex-1].getName()+" = "+x);
|
|
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), new Short(x) );
|
|
|
|
}
|
|
|
|
public synchronized void updateString(int columnIndex, String x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("in update String "+fields[columnIndex-1].getName()+" = "+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
|
|
}
|
|
|
|
public synchronized void updateTime(int columnIndex, Time x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("in update Time "+fields[columnIndex-1].getName()+" = "+x);
|
|
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
|
|
}
|
|
|
|
public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("updating Timestamp "+fields[columnIndex-1].getName()+" = "+x);
|
|
|
|
doingUpdates = !onInsertRow;
|
|
updateValues.put( fields[columnIndex-1].getName(), x );
|
|
|
|
|
|
}
|
|
|
|
public synchronized void updateNull(String columnName) throws SQLException
|
|
{
|
|
updateNull(findColumn(columnName));
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a boolean value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateBoolean(String columnName, boolean x) throws SQLException
|
|
{
|
|
updateBoolean(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a byte value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateByte(String columnName, byte x) throws SQLException
|
|
{
|
|
updateByte(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a short value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateShort(String columnName, short x) throws SQLException
|
|
{
|
|
updateShort(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with an integer value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateInt(String columnName, int x) throws SQLException
|
|
{
|
|
updateInt(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a long value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateLong(String columnName, long x) throws SQLException
|
|
{
|
|
updateLong(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a float value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateFloat(String columnName, float x) throws SQLException
|
|
{
|
|
updateFloat(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a double value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateDouble(String columnName, double x) throws SQLException
|
|
{
|
|
updateDouble(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a BigDecimal value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateBigDecimal(String columnName, BigDecimal x)
|
|
throws SQLException
|
|
{
|
|
updateBigDecimal(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a String value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateString(String columnName, String x) throws SQLException
|
|
{
|
|
updateString(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a byte array value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateBytes(String columnName, byte x[]) throws SQLException
|
|
{
|
|
updateBytes(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a Date value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateDate(String columnName, java.sql.Date x)
|
|
throws SQLException
|
|
{
|
|
updateDate(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a Time value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateTime(String columnName, java.sql.Time x)
|
|
throws SQLException
|
|
{
|
|
updateTime(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a Timestamp value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x)
|
|
throws SQLException
|
|
{
|
|
updateTimestamp(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with an ascii stream value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @param length of the stream
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateAsciiStream(
|
|
String columnName,
|
|
java.io.InputStream x,
|
|
int length)
|
|
throws SQLException
|
|
{
|
|
updateAsciiStream(findColumn(columnName), x, length);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a binary stream value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @param length of the stream
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateBinaryStream(
|
|
String columnName,
|
|
java.io.InputStream x,
|
|
int length)
|
|
throws SQLException
|
|
{
|
|
updateBinaryStream(findColumn(columnName), x, length);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with a character stream value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @param length of the stream
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateCharacterStream(
|
|
String columnName,
|
|
java.io.Reader reader,
|
|
int length)
|
|
throws SQLException
|
|
{
|
|
updateCharacterStream(findColumn(columnName), reader,length);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with an Object value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
|
|
* this is the number of digits after the decimal. For all other
|
|
* types this value will be ignored.
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateObject(String columnName, Object x, int scale)
|
|
throws SQLException
|
|
{
|
|
updateObject(findColumn(columnName), x);
|
|
}
|
|
|
|
/**
|
|
* JDBC 2.0
|
|
*
|
|
* Update a column with an Object value.
|
|
*
|
|
* The updateXXX() methods are used to update column values in the
|
|
* current row, or the insert row. The updateXXX() methods do not
|
|
* update the underlying database, instead the updateRow() or insertRow()
|
|
* methods are called to update the database.
|
|
*
|
|
* @param columnName the name of the column
|
|
* @param x the new column value
|
|
* @exception SQLException if a database-access error occurs
|
|
*/
|
|
|
|
public synchronized void updateObject(String columnName, Object x) throws SQLException
|
|
{
|
|
updateObject(findColumn(columnName), x);
|
|
}
|
|
|
|
|
|
|
|
private int _findColumn( String columnName )
|
|
{
|
|
int i;
|
|
|
|
final int flen = fields.length;
|
|
for (i = 0 ; i < flen; ++i)
|
|
{
|
|
if (fields[i].getName().equalsIgnoreCase(columnName))
|
|
{
|
|
return (i + 1);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* Is this ResultSet updateable?
|
|
*/
|
|
|
|
boolean isUpdateable() throws SQLException
|
|
{
|
|
|
|
if (updateable) return true;
|
|
|
|
if ( Driver.logDebug ) Driver.debug("checking if rs is updateable");
|
|
|
|
parseQuery();
|
|
|
|
if ( singleTable == false )
|
|
{
|
|
if ( Driver.logDebug ) Driver.debug("not a single table");
|
|
return false;
|
|
}
|
|
|
|
if ( Driver.logDebug ) Driver.debug("getting primary keys");
|
|
|
|
//
|
|
// Contains the primary key?
|
|
//
|
|
|
|
primaryKeys = new Vector();
|
|
|
|
// this is not stricty jdbc spec, but it will make things much faster if used
|
|
// the user has to select oid, * from table and then we will just use oid
|
|
|
|
|
|
usingOID = false;
|
|
int oidIndex = _findColumn( "oid" );
|
|
int i = 0;
|
|
|
|
|
|
// if we find the oid then just use it
|
|
|
|
if ( oidIndex > 0 )
|
|
{
|
|
i++;
|
|
primaryKeys.add( new PrimaryKey( oidIndex, "oid" ) );
|
|
usingOID = true;
|
|
}
|
|
else
|
|
{
|
|
// otherwise go and get the primary keys and create a hashtable of keys
|
|
java.sql.ResultSet rs = ((java.sql.Connection)connection).getMetaData().getPrimaryKeys("","",tableName);
|
|
|
|
|
|
for( ; rs.next(); i++ )
|
|
{
|
|
String columnName = rs.getString(4); // get the columnName
|
|
|
|
int index = findColumn( columnName );
|
|
|
|
if ( index > 0 )
|
|
{
|
|
primaryKeys.add( new PrimaryKey(index, columnName ) ); // get the primary key information
|
|
}
|
|
}
|
|
|
|
rs.close();
|
|
}
|
|
|
|
numKeys = primaryKeys.size();
|
|
|
|
if ( Driver.logDebug ) Driver.debug( "no of keys=" + i );
|
|
|
|
if ( i < 1 )
|
|
{
|
|
throw new SQLException("No Primary Keys");
|
|
}
|
|
|
|
updateable = primaryKeys.size() > 0;
|
|
|
|
if ( Driver.logDebug ) Driver.debug( "checking primary key " + updateable );
|
|
|
|
return updateable;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public void parseQuery()
|
|
{
|
|
StringTokenizer st=new StringTokenizer(sqlQuery," \r\t");
|
|
boolean tableFound=false, tablesChecked = false;
|
|
String name="";
|
|
|
|
singleTable = true;
|
|
|
|
while ( !tableFound && !tablesChecked && st.hasMoreTokens() )
|
|
{
|
|
name=st.nextToken();
|
|
if ( !tableFound )
|
|
{
|
|
if (name.toLowerCase().equals("from"))
|
|
{
|
|
tableName=st.nextToken();
|
|
tableFound=true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tablesChecked = true;
|
|
// if the very next token is , then there are multiple tables
|
|
singleTable = !name.equalsIgnoreCase(",");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void updateRowBuffer() throws SQLException
|
|
{
|
|
|
|
Enumeration columns = updateValues.keys();
|
|
|
|
while( columns.hasMoreElements() )
|
|
{
|
|
String columnName = (String)columns.nextElement();
|
|
int columnIndex = _findColumn( columnName ) - 1;
|
|
|
|
switch ( connection.getSQLType( fields[columnIndex].getPGType() ) )
|
|
{
|
|
|
|
case Types.DECIMAL:
|
|
case Types.BIGINT:
|
|
case Types.DOUBLE:
|
|
case Types.BIT:
|
|
case Types.VARCHAR:
|
|
case Types.DATE:
|
|
case Types.TIME:
|
|
case Types.TIMESTAMP:
|
|
case Types.SMALLINT:
|
|
case Types.FLOAT:
|
|
case Types.INTEGER:
|
|
case Types.CHAR:
|
|
case Types.NUMERIC:
|
|
case Types.REAL:
|
|
case Types.TINYINT:
|
|
|
|
try
|
|
{
|
|
rowBuffer[columnIndex] = String.valueOf( updateValues.get( columnName ) ).getBytes(connection.getEncoding().name() );
|
|
}
|
|
catch ( UnsupportedEncodingException ex)
|
|
{
|
|
throw new SQLException("Unsupported Encoding "+connection.getEncoding().name());
|
|
}
|
|
case Types.NULL:
|
|
continue;
|
|
default:
|
|
rowBuffer[columnIndex] = (byte [])updateValues.get( columnName );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|