/*************************************************************************
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright 2008 by Sun Microsystems, Inc.
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: RowSet.java,v $
* $Revision: 1.12 $
*
* This file is part of OpenOffice.org.
*
* OpenOffice.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* OpenOffice.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details
* (a copy is included in the LICENSE file that accompanied this code).
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with OpenOffice.org. If not, see
* <http://www.openoffice.org/license.html>
* for a copy of the LGPLv3 License.
*
************************************************************************/
package complex.dbaccess;
import com.sun.star.container.XIndexAccess;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.beans.*;
import com.sun.star.lang.*;
import com.sun.star.sdbcx.*;
import com.sun.star.sdbc.*;
import com.sun.star.sdb.*;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.util.XRefreshable;
import connectivity.tools.HsqlDatabase;
import complexlib.ComplexTestCase;
import complexlib.ComplexTestCase.AssureException;
public class RowSet extends ComplexTestCase {
static final int MAX_TABLE_ROWS = 100;
static final int MAX_FETCH_ROWS = 10;
HsqlDatabase m_database;
connectivity.tools.DataSource
m_dataSource;
XRowSet m_rowSet;
XResultSet m_resultSet;
XResultSetUpdate m_resultSetUpdate;
XRow m_row;
XRowLocate m_rowLocate;
XPropertySet m_rowSetProperties;
XParametersSupplier m_paramsSupplier;
// --------------------------------------------------------------------------------------------------------
class ResultSetMovementStress implements Runnable
{
XResultSet m_resultSet;
XRow m_row;
int m_id;
public ResultSetMovementStress(XResultSet _resultSet,int _id) throws java.lang.Exception {
m_resultSet = _resultSet;
m_row = (XRow)UnoRuntime.queryInterface(XRow.class,m_resultSet);
m_id = _id;
}
public void run()
{
try
{
m_resultSet.beforeFirst();
for ( int i = 0; m_resultSet.next(); ++i )
{
int pos = m_resultSet.getRow();
int val = m_row.getInt(1);
// log.println("Clone Move(" + m_id +") before i: " + (i+1) + " Pos: " + pos + " Val: " + val);
testPosition( m_resultSet, m_row, i + 1, "clone move(" + m_id +")" );
// val = m_row.getInt(1);
// log.println("Clone Move(" + m_id +") after i: " + (i+1) + " Pos: " + pos + " Val: " + val);
int pos2 = m_resultSet.getRow();
assure("ResultSetMovementStress wrong position: " + i + " Pos1: " + pos + " Pos2: " + pos2,pos == pos2);
}
}catch(AssureException e){
}catch(Exception e){
assure("ResultSetMovementStress(" + m_id + ") failed: " + e,false);
}
}
}
// --------------------------------------------------------------------------------------------------------
public String[] getTestMethodNames() {
return new String[]
{
"testRowSet",
"testRowSetEvents",
"testDeleteBehavior",
"testCloneMovesPlusDeletions",
"testCloneMovesPlusInsertions",
"testParameters"
};
}
// --------------------------------------------------------------------------------------------------------
public String getTestObjectName() {
return "RowSet";
}
// --------------------------------------------------------------------------------------------------------
private void createTestCase( boolean _defaultRowSet )
{
if ( m_database == null )
{
try
{
CRMDatabase database = new CRMDatabase( getFactory() );
m_database = database.getDatabase();
m_dataSource = m_database.getDataSource();
}
catch(Exception e)
{
assure( "could not create the embedded HSQL database: " + e.getMessage(), false );
}
}
try
{
createStruture();
}
catch( SQLException e )
{
assure( "could not connect to the database/table structure, error message:\n" + e.getMessage(), false );
}
if ( _defaultRowSet )
createRowSet( "TEST1", CommandType.TABLE, true, true );
}
// --------------------------------------------------------------------------------------------------------
private XMultiServiceFactory getFactory()
{
return (XMultiServiceFactory)param.getMSF();
}
// --------------------------------------------------------------------------------------------------------
/** creates a com.sun.star.sdb.RowSet to use during the test
* @param command
* the command to use for the RowSet
* @param commandType
* the command type to use for the RowSet
* @param execute
* determines whether the RowSet should be executed
*/
private void createRowSet( String command, int commandType, boolean execute )
{
createRowSet( command, commandType, execute, false );
}
// --------------------------------------------------------------------------------------------------------
/** creates a com.sun.star.sdb.RowSet to use during the test
* @param command
* the command to use for the RowSet
* @param commandType
* the command type to use for the RowSet
* @param limitFetchSize
* determines whether the fetch size of the RowSet should be limited to MAX_FETCH_ROWS
* @param execute
* determines whether the RowSet should be executed
*/
private void createRowSet( String command, int commandType, boolean execute, boolean limitFetchSize )
{
try
{
m_rowSet = (XRowSet)UnoRuntime.queryInterface(XRowSet.class,
getFactory().createInstance("com.sun.star.sdb.RowSet"));
XPropertySet rowSetProperties = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, m_rowSet );
rowSetProperties.setPropertyValue( "Command", command );
rowSetProperties.setPropertyValue( "CommandType", new Integer( commandType ) );
rowSetProperties.setPropertyValue( "ActiveConnection",m_database.defaultConnection() );
if ( limitFetchSize )
rowSetProperties.setPropertyValue( "FetchSize", new Integer( MAX_FETCH_ROWS ) );
m_resultSet = (XResultSet)UnoRuntime.queryInterface( XResultSet.class, m_rowSet );
m_resultSetUpdate = (XResultSetUpdate)UnoRuntime.queryInterface( XResultSetUpdate.class, m_rowSet );
m_row = (XRow)UnoRuntime.queryInterface( XRow.class, m_rowSet );
m_rowLocate = (XRowLocate)UnoRuntime.queryInterface( XRowLocate.class, m_resultSet );
m_rowSetProperties = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, m_rowSet );
m_paramsSupplier = (XParametersSupplier)UnoRuntime.queryInterface( XParametersSupplier.class, m_rowSet );
if ( execute )
m_rowSet.execute();
}
catch ( java.lang.Exception e )
{
assure( "caught an exception while creating the RowSet. Type:\n" + e.getClass().toString() + "\nMessage:\n" + e.getMessage(), false );
}
}
// --------------------------------------------------------------------------------------------------------
public void testRowSet() throws java.lang.Exception {
log.println("testing testRowSet");
createTestCase( true );
// sequential postioning
m_resultSet.beforeFirst();
testSequentialPositining(m_resultSet,m_row);
// absolute positioning
testAbsolutePositioning(m_resultSet,m_row);
// 3rd test
test3(createClone(),m_resultSet);
// 4th test
test4(m_resultSet);
// concurrent (multi threaded) access to the row set and its clones
testConcurrentAccess(m_resultSet);
}
// --------------------------------------------------------------------------------------------------------
XResultSet createClone() throws SQLException
{
XResultSetAccess rowAcc = (XResultSetAccess)UnoRuntime.queryInterface( XResultSetAccess.class, m_rowSet );
return rowAcc.createResultSet();
}
// --------------------------------------------------------------------------------------------------------
void createStruture() throws SQLException
{
m_database.executeSQL( "DROP TABLE \"TEST1\" IF EXISTS" );
m_database.executeSQL( "CREATE TABLE \"TEST1\" (\"ID\" integer not null primary key, \"col2\" varchar(50) )" );
XConnection connection = m_database.defaultConnection();
XPreparedStatement prep = connection.prepareStatement("INSERT INTO \"TEST1\" values (?,?)");
XParameters para = (XParameters)UnoRuntime.queryInterface(XParameters.class,prep);
for(int i=1 ; i <= MAX_TABLE_ROWS ; ++i){
para.setInt(1, i );
para.setString(2, "Test" + i);
prep.executeUpdate();
}
XTablesSupplier suppTables = (XTablesSupplier)UnoRuntime.queryInterface( XTablesSupplier.class, connection );
XRefreshable refresh = (XRefreshable)UnoRuntime.queryInterface( XRefreshable.class, suppTables.getTables() );
refresh.refresh();
}
// --------------------------------------------------------------------------------------------------------
void testPosition(XResultSet m_resultSet,XRow m_row,int expectedValue,String location) throws SQLException
{
int val = m_row.getInt(1);
int pos = m_resultSet.getRow();
assure( location + ": value/position do not match: " + pos + " (pos) != " + val + " (val)", val == pos );
assure( location + ": value/position are not as expected: " + val + " (val) != " + expectedValue + " (expected)", val == expectedValue );
}
// --------------------------------------------------------------------------------------------------------
void testSequentialPositining(XResultSet _resultSet,XRow _row)
{
try
{
// 1st test
int i=1;
while(_resultSet.next())
{
testPosition( _resultSet, _row, i, "testSequentialPositining" );
++i;
}
}
catch(AssureException e)
{
}
catch(Exception e)
{
assure("testSequentialPositining failed: " + e,false);
}
}
// --------------------------------------------------------------------------------------------------------
void testAbsolutePositioning(XResultSet _resultSet,XRow _row){
try{
for(int i = 1 ; i <= MAX_FETCH_ROWS ; ++i){
int calcPos = (MAX_TABLE_ROWS % i) + 1;
assure( "testAbsolutePositioning failed", _resultSet.absolute(calcPos) );
testPosition( _resultSet, _row, calcPos, "testAbsolutePositioning" );
}
}catch(AssureException e){
}catch(Exception e){
assure("testAbsolutePositioning failed: " + e,false);
}
}
// --------------------------------------------------------------------------------------------------------
void test3(XResultSet clone,XResultSet _resultSet){
try{
XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet);
XRow cloneRow = (XRow)UnoRuntime.queryInterface(XRow.class,clone);
for(int i = 1 ; i <= MAX_FETCH_ROWS ; ++i){
int calcPos = (MAX_TABLE_ROWS % i) + 1;
if ( clone.absolute(calcPos) )
{
testPosition( clone, cloneRow, calcPos, "test3" );
testAbsolutePositioning(_resultSet,_row);
testAbsolutePositioning(clone,cloneRow);
}
}
}catch(AssureException e){
}catch(Exception e){
assure("test3 failed: " + e,false);
}
}
// --------------------------------------------------------------------------------------------------------
void test4(XResultSet _resultSet){
try{
XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet);
_resultSet.beforeFirst();
for(int i = 1 ; i <= MAX_TABLE_ROWS ; ++i){
_resultSet.next();
XResultSet clone = createClone();
XRow cloneRow = (XRow)UnoRuntime.queryInterface(XRow.class,clone);
int calcPos = MAX_TABLE_ROWS - 1;
if ( calcPos != 0 && clone.absolute(calcPos) )
{
testPosition( clone, cloneRow, calcPos, "test4: clone" );
testPosition( _resultSet, _row, i, "test4: rowset" );
}
}
}catch(AssureException e){
}catch(Exception e){
assure("test4 failed: " + e,false);
}
}
// --------------------------------------------------------------------------------------------------------
void testConcurrentAccess(XResultSet _resultSet)
{
log.println("testing Thread");
try
{
XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet);
_resultSet.beforeFirst();
final int numberOfThreads = 10;
Thread threads[] = new Thread[numberOfThreads];
for ( int i=0; i<numberOfThreads; ++i )
{
threads[i] = new Thread( new ResultSetMovementStress( createClone(), i ) );
System.out.println( "starting thread " + String.valueOf(i+1) + " of " + String.valueOf( numberOfThreads ) );
threads[i].start();
}
for ( int i=0; i<numberOfThreads; ++i )
threads[i].join();
}
catch(AssureException e)
{
}
catch(Exception e)
{
e.printStackTrace();
assure("testConcurrentAccess failed: " + e,false);
}
}
// --------------------------------------------------------------------------------------------------------
public void testRowSetEvents() throws java.lang.Exception {
log.println("testing RowSet Events");
createTestCase( true );
// first we create our RowSet object
RowSetEventListener pRow = new RowSetEventListener(this);
XColumnsSupplier colSup = (XColumnsSupplier)UnoRuntime.queryInterface(XColumnsSupplier.class,m_rowSet);
XPropertySet col = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class,colSup.getColumns().getByName("ID"));
col.addPropertyChangeListener("Value", pRow);
m_rowSetProperties.addPropertyChangeListener("IsModified", pRow);
m_rowSetProperties.addPropertyChangeListener("IsNew", pRow);
m_rowSetProperties.addPropertyChangeListener("IsRowCountFinal", pRow);
m_rowSetProperties.addPropertyChangeListener("RowCount", pRow);
XRowSetApproveBroadcaster xApBroad = (XRowSetApproveBroadcaster)UnoRuntime.queryInterface(XRowSetApproveBroadcaster.class,m_resultSet);
xApBroad.addRowSetApproveListener(pRow);
m_rowSet.addRowSetListener(pRow);
// do some movements to check if we got all notifications
Class cResSet = java.lang.Class.forName("com.sun.star.sdbc.XResultSet");
boolean moves[] = new boolean[9];
for( int i = 0; i < moves.length; ++i)
moves[i] = false;
moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true;
moves[RowSetEventListener.COLUMN_VALUE] = true;
moves[RowSetEventListener.CURSOR_MOVED] = true;
moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = true;
moves[RowSetEventListener.ROW_COUNT] = true;
testCursorMove(m_resultSet,cResSet.getMethod("afterLast",(Class[])null),pRow,moves,null);
moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = false;
moves[RowSetEventListener.ROW_COUNT] = false;
testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("last",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("first",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("previous",(Class[])null),pRow,moves,null);
testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null);
moves[RowSetEventListener.IS_MODIFIED] = true;
XRowUpdate updRow = (XRowUpdate)UnoRuntime.queryInterface(XRowUpdate.class,m_resultSet);
updRow.updateString(2,"Test21");
testCursorMove(m_resultSet,cResSet.getMethod("next",(Class[])null),pRow,moves,null);
moves[RowSetEventListener.IS_MODIFIED] = false;
Class cupd = java.lang.Class.forName("com.sun.star.sdbc.XResultSetUpdate");
XResultSetUpdate upd = (XResultSetUpdate)UnoRuntime.queryInterface(XResultSetUpdate.class,m_resultSet);
testCursorMove(upd,cupd.getMethod("moveToInsertRow",(Class[])null),pRow,moves,null);
updRow.updateInt(1, MAX_TABLE_ROWS + 2);
updRow.updateString(2, "HHHH");
moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = false;
moves[RowSetEventListener.CURSOR_MOVED] = false;
moves[RowSetEventListener.IS_MODIFIED] = true;
moves[RowSetEventListener.IS_NEW] = true;
moves[RowSetEventListener.ROW_COUNT] = true;
moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true;
moves[RowSetEventListener.ROW_CHANGED] = true;
testCursorMove(upd,cupd.getMethod("insertRow",(Class[])null),pRow,moves,null);
moves[RowSetEventListener.IS_NEW] = false;
moves[RowSetEventListener.ROW_COUNT] = false;
m_resultSet.first();
updRow.updateInt(1, MAX_TABLE_ROWS + 3);
updRow.updateString(2, "__");
testCursorMove(upd,cupd.getMethod("updateRow",(Class[])null),pRow,moves,null);
moves[RowSetEventListener.IS_NEW] = true;
moves[RowSetEventListener.ROW_COUNT] = true;
m_resultSet.first();
testCursorMove(upd,cupd.getMethod("deleteRow",(Class[])null),pRow,moves,null);
moves[RowSetEventListener.IS_NEW] = false;
moves[RowSetEventListener.COLUMN_VALUE] = true;
moves[RowSetEventListener.ROW_COUNT] = false;
m_resultSet.first();
updRow.updateString(2,"Test21");
testCursorMove(m_resultSet,cResSet.getMethod("refreshRow",(Class[])null),pRow,moves,null);
m_resultSet.first();
updRow.updateString(2,"Test21");
testCursorMove(upd,cupd.getMethod("cancelRowUpdates",(Class[])null),pRow,moves,null);
for( int i = 0; i < moves.length; ++i)
moves[i] = false;
moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true;
moves[RowSetEventListener.COLUMN_VALUE] = true;
moves[RowSetEventListener.CURSOR_MOVED] = true;
Class cloc = java.lang.Class.forName("com.sun.star.sdbcx.XRowLocate");
m_resultSet.first();
Object bookmark = m_rowLocate.getBookmark();
m_resultSet.next();
Object temp[] = new Object[1];
temp[0] = bookmark;
Class ctemp[] = new Class[1];
ctemp[0] = Object.class;
testCursorMove(m_rowLocate,cloc.getMethod("moveToBookmark",ctemp),pRow,moves,temp);
Object temp2[] = new Object[2];
temp2[0] = bookmark;
temp2[1] = new Integer(1);
Class ctemp2[] = new Class[2];
ctemp2[0] = Object.class;
ctemp2[1] = int.class;
testCursorMove(m_rowLocate,cloc.getMethod("moveRelativeToBookmark",ctemp2),pRow,moves,temp2);
for( int i = 0; i < moves.length; ++i)
moves[i] = false;
moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true;
moves[RowSetEventListener.ROW_CHANGED] = true;
moves[RowSetEventListener.ROW_COUNT] = true;
Class cdelRows = java.lang.Class.forName("com.sun.star.sdbcx.XDeleteRows");
ctemp[0] = Object[].class;
XDeleteRows delRows = (XDeleteRows)UnoRuntime.queryInterface(XDeleteRows.class,m_resultSet);
Object bookmarks[] = new Object[5];
m_resultSet.first();
for ( int i = 0; i < bookmarks.length ; ++i ){
m_resultSet.next();
bookmarks[i] = m_rowLocate.getBookmark();
}
temp[0] = bookmarks;
testCursorMove(delRows,cdelRows.getMethod("deleteRows",ctemp),pRow,moves,temp);
// now destroy the RowSet
XComponent xComp = (XComponent)UnoRuntime.queryInterface(XComponent.class,m_resultSet);
xComp.dispose();
}
// --------------------------------------------------------------------------------------------------------
private void testCursorMove(Object res
,java.lang.reflect.Method _method
, RowSetEventListener _evt
, boolean _must[]
, Object args[]) throws java.lang.Exception {
_evt.clearCalling();
_method.invoke(res,args);
log.println("testing events for " + _method.getName());
int calling[] = _evt.getCalling();
int pos = 1;
assure("Callings are not in the correct order for APPROVE_CURSOR_MOVE " ,
( !_must[RowSetEventListener.APPROVE_CURSOR_MOVE] || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == -1) || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == pos++ );
assure("Callings are not in the correct order for APPROVE_ROW_CHANGE" ,
( !_must[ RowSetEventListener.APPROVE_ROW_CHANGE] || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == -1) || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == pos++);
assure("Callings are not in the correct order for COLUMN_VALUE" ,
( !_must[RowSetEventListener.COLUMN_VALUE] || calling[RowSetEventListener.COLUMN_VALUE] == -1) || calling[RowSetEventListener.COLUMN_VALUE] == pos++);
assure("Callings are not in the correct order for CURSOR_MOVED" ,
( !_must[RowSetEventListener.CURSOR_MOVED] || calling[RowSetEventListener.CURSOR_MOVED] == -1) || calling[RowSetEventListener.CURSOR_MOVED] == pos++);
assure("Callings are not in the correct order for ROW_CHANGED" ,
( !_must[ RowSetEventListener.ROW_CHANGED] || calling[RowSetEventListener.ROW_CHANGED] == -1) || calling[RowSetEventListener.ROW_CHANGED] == pos++);
assure("Callings are not in the correct order for IS_MODIFIED" ,
( !_must[ RowSetEventListener.IS_MODIFIED] || calling[RowSetEventListener.IS_MODIFIED] == -1) || calling[RowSetEventListener.IS_MODIFIED] == pos++);
assure("Callings are not in the correct order for IS_NEW" ,
( !_must[ RowSetEventListener.IS_NEW] || calling[RowSetEventListener.IS_NEW] == -1) || calling[RowSetEventListener.IS_NEW] == pos++);
assure("Callings are not in the correct order for ROW_COUNT" ,
( !_must[ RowSetEventListener.ROW_COUNT] || calling[RowSetEventListener.ROW_COUNT] == -1) || calling[RowSetEventListener.ROW_COUNT] == pos++);
assure("Callings are not in the correct order for IS_ROW_COUNT_FINAL" ,
( !_must[ RowSetEventListener.IS_ROW_COUNT_FINAL] || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == -1) || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == pos++);
_evt.clearCalling();
}
// --------------------------------------------------------------------------------------------------------
/** returns the current row count of the RowSet
*/
private int currentRowCount() throws UnknownPropertyException, WrappedTargetException
{
Integer rowCount = (Integer)m_rowSetProperties.getPropertyValue( "RowCount" );
return rowCount.intValue();
}
// --------------------------------------------------------------------------------------------------------
/** positions the row set at an arbitrary position between 2 and (current row count - 1)
*/
private int positionRandom() throws SQLException, UnknownPropertyException, WrappedTargetException
{
int position = (new java.util.Random()).nextInt( currentRowCount() - 2 ) + 2;
assure( "sub task failed: could not position to row no. " + (new Integer( position )).toString(),
m_resultSet.absolute( position ) );
return m_resultSet.getRow();
}
// --------------------------------------------------------------------------------------------------------
/** moves the result set to a random record between 2 and (current row count - 1), and deletes this record
*
* After returning from this method, the row set is still positioned at the deleted record
* @return
* the number/position of the record which has been deleted
*/
private int deleteRandom() throws SQLException, UnknownPropertyException, WrappedTargetException
{
// check if the current position and the row count in the result set is changed by a deletion (it should not)
int positionBefore = positionRandom();
int rowCountBefore = currentRowCount();
m_resultSetUpdate.deleteRow();
int positionAfter = m_resultSet.getRow();
int rowCountAfter = currentRowCount();
assure( "position changed during |deleteRow| (it should not)", positionAfter == positionBefore );
assure( "row count changed with a |deleteRow| (it should not)", rowCountBefore == rowCountAfter );
assure( "RowSet does not report the current row as deleted after |deleteRow|", m_resultSet.rowDeleted() );
return positionBefore;
}
// --------------------------------------------------------------------------------------------------------
public void testDeleteBehavior() throws Exception
{
createTestCase( true );
// ensure that all records are known
m_resultSet.last();
int initialRowCount = currentRowCount();
// delete a random row
int deletedRow = deleteRandom();
// .....................................................................................................
// asking for the bookmark of a deleted row should fail
boolean caughtException = false;
try { m_rowLocate.getBookmark(); }
catch ( SQLException e ) { caughtException = true; }
assure( "asking for the bookmark of a deleted row should throw an exception", caughtException );
// .....................................................................................................
// isXXX methods should return |false| on a deleted row
assure( "one of the isFoo failed after |deleteRow|", !m_resultSet.isBeforeFirst() && !m_resultSet.isAfterLast() && !m_resultSet.isFirst() && !m_resultSet.isLast() );
// note that we can assume that isFirst / isLast also return |false|, since deleteRandom did
// not position on the first or last record, but inbetween
// .....................................................................................................
// check if moving away from this row in either direction yields the expected results
assure( "|previous| after |deleteRow| failed", m_resultSet.previous() );
int positionPrevious = m_resultSet.getRow();
assure( "position after |previous| after |deleteRow| is not as expected", positionPrevious == deletedRow - 1 );
deletedRow = deleteRandom();
assure( "|next| after |deleteRow| failed", m_resultSet.next() );
int positionAfter = m_resultSet.getRow();
assure( "position after |next| after |deleteRow| is not as expected", positionAfter == deletedRow );
// since the deleted record "vanishs" as soon as the cursor is moved away from it, the absolute position does
// not change with a |next| call here
// .....................................................................................................
// check if the deleted rows really vanished after moving away from them
assure( "row count did not change as expected after two deletions", initialRowCount - 2 == currentRowCount() );
// .....................................................................................................
// check if the deleted row vanishes after moving to the insertion row
int rowCountBefore = currentRowCount();
int deletedPos = deleteRandom();
m_resultSetUpdate.moveToInsertRow();
assure( "moving to the insertion row immediately after |deleteRow| does not adjust the row count", rowCountBefore == currentRowCount() + 1 );
m_resultSetUpdate.moveToCurrentRow();
assure( "|moveToCurrentRow| after |deleteRow| + |moveToInsertRow| results in unexpected position",
( m_resultSet.getRow() == deletedPos ) && !m_resultSet.rowDeleted() );
// the same, but this time with deleting the first row (which is not covered by deleteRandom)
m_resultSet.last();
m_resultSetUpdate.deleteRow();
m_resultSetUpdate.moveToInsertRow();
m_resultSetUpdate.moveToCurrentRow();
assure( "|last| + |deleteRow| + |moveToInsertRow| + |moveToCurrentRow| results in wrong state", m_resultSet.isAfterLast() );
// .....................................................................................................
// check if deleting a deleted row fails as expected
deleteRandom();
caughtException = false;
try { m_resultSetUpdate.deleteRow(); }
catch( SQLException e ) { caughtException = true; }
assure( "deleting a deleted row succeeded - it shouldn't", caughtException );
// .....................................................................................................
// check if deleteRows fails if it contains the bookmark of a previously-deleted row
m_resultSet.first();
Object firstBookmark = m_rowLocate.getBookmark();
positionRandom();
Object deleteBookmark = m_rowLocate.getBookmark();
m_resultSetUpdate.deleteRow();
XDeleteRows multiDelete = (XDeleteRows)UnoRuntime.queryInterface( XDeleteRows.class, m_resultSet );
int[] deleteSuccess = multiDelete.deleteRows(new Object[]{firstBookmark, deleteBookmark});
assure( "XDeleteRows::deleteRows with the bookmark of an already-deleted row failed",
( deleteSuccess.length == 2 ) && ( deleteSuccess[0] != 0 ) && ( deleteSuccess[1] == 0 ) );
// .....................................................................................................
// check if refreshing a deleted row fails as expected
deleteRandom();
caughtException = false;
try { m_resultSet.refreshRow(); }
catch( SQLException e ) { caughtException = true; }
assure( "refreshing a deleted row succeeded - it shouldn't", caughtException );
// .....................................................................................................
// rowUpdated/rowDeleted
deleteRandom();
assure( "rowDeleted and/or rowUpdated are wrong on a deleted row", !m_resultSet.rowUpdated() && !m_resultSet.rowInserted() );
// .....................................................................................................
// updating values in a deleted row should fail
deleteRandom();
XRowUpdate rowUpdated = (XRowUpdate)UnoRuntime.queryInterface( XRowUpdate.class, m_resultSet );
caughtException = false;
try { rowUpdated.updateString( 2, "Test21" ); }
catch( SQLException e ) { caughtException = true; }
assure( "updating values in a deleted row should not succeed", caughtException );
}
// --------------------------------------------------------------------------------------------------------
/** checks whether deletions on the main RowSet properly interfere (or don't interfere) with the movement
* on a clone of the RowSet
*/
public void testCloneMovesPlusDeletions() throws SQLException, UnknownPropertyException, WrappedTargetException
{
createTestCase( true );
// ensure that all records are known
m_resultSet.last();
XResultSet clone = createClone();
XRowLocate cloneRowLocate = (XRowLocate)UnoRuntime.queryInterface( XRowLocate.class, clone );
positionRandom();
// .....................................................................................................
// move the clone to the same record as the RowSet, and delete this record
cloneRowLocate.moveToBookmark( m_rowLocate.getBookmark() );
int clonePosition = clone.getRow();
m_resultSetUpdate.deleteRow();
assure( "clone doesn't know that its current row has been deleted via the RowSet", clone.rowDeleted() );
assure( "clone's position changed somehow during deletion", clonePosition == clone.getRow() );
// .....................................................................................................
// move the row set away from the deleted record. This should still not touch the state of the clone
m_resultSet.previous();
assure( "clone doesn't know (anymore) that its current row has been deleted via the RowSet", clone.rowDeleted() );
assure( "clone's position changed somehow during deletion and RowSet-movement", clonePosition == clone.getRow() );
// .....................................................................................................
// move the clone away from the deleted record
clone.next();
assure( "clone still assumes that its row is deleted - but we already moved it", !clone.rowDeleted() );
// .....................................................................................................
// check whether deleting the extremes (first / last) work
m_resultSet.first();
cloneRowLocate.moveToBookmark( m_rowLocate.getBookmark() );
m_resultSetUpdate.deleteRow();
clone.previous();
assure( "deleting the first record left the clone in a strange state (after |previous|)", clone.isBeforeFirst() );
clone.next();
assure( "deleting the first record left the clone in a strange state (after |previous| + |next|)", clone.isFirst() );
m_resultSet.last();
cloneRowLocate.moveToBookmark( m_rowLocate.getBookmark() );
m_resultSetUpdate.deleteRow();
clone.next();
assure( "deleting the last record left the clone in a strange state (after |next|)", clone.isAfterLast() );
clone.previous();
assure( "deleting the first record left the clone in a strange state (after |next| + |previous|)", clone.isLast() );
// .....................................................................................................
// check whether movements of the clone interfere with movements of the RowSet, if the latter is on a deleted row
int positionBefore = positionRandom();
m_resultSetUpdate.deleteRow();
assure( "|deleteRow|, but no |rowDeleted| (this should have been found much earlier!)", m_resultSet.rowDeleted() );
clone.beforeFirst();
while ( clone.next() )
;
assure( "row set forgot that the current row is deleted", m_resultSet.rowDeleted() );
assure( "moving to the next record after |deleteRow| and clone moves failed", m_resultSet.next() );
assure( "wrong position after |deleteRow| and clone movement", !m_resultSet.isAfterLast() && !m_resultSet.isBeforeFirst() );
assure( "wrong absolute position after |deleteRow| and clone movement", m_resultSet.getRow() == positionBefore );
}
// --------------------------------------------------------------------------------------------------------
/** checks whether insertions on the main RowSet properly interfere (or don't interfere) with the movement
* on a clone of the RowSet
*/
public void testCloneMovesPlusInsertions() throws SQLException, UnknownPropertyException, WrappedTargetException, PropertyVetoException, com.sun.star.lang.IllegalArgumentException
{
createTestCase( true );
// ensure that all records are known
m_rowSetProperties.setPropertyValue( "FetchSize", new Integer( 10 ) );
XResultSet clone = createClone();
XRow cloneRow = (XRow)UnoRuntime.queryInterface( XRow.class, clone );
// .....................................................................................................
// first check the basic scenario without the |moveToInsertRow| |moveToCurrentRow|, to ensure that
// really those are broken, if at all
m_resultSet.last();
clone.first();
clone.absolute( 11 );
clone.first();
int rowValue1 = m_row.getInt(1);
int rowPos = m_resultSet.getRow();
int rowValue2 = m_row.getInt(1);
assure( "repeated query for the same column value delivers different values (" + rowValue1 + " and " + rowValue2 + ") on row: " + rowPos,
rowValue1 == rowValue2 );
testPosition( clone, cloneRow, 1, "mixed clone/rowset move: clone check" );
testPosition( m_resultSet, m_row, MAX_TABLE_ROWS, "mixed clone/rowset move: rowset check" );
// .....................................................................................................
// now the complete scenario
m_resultSet.last();
m_resultSetUpdate.moveToInsertRow();
clone.first();
clone.absolute( 11 );
clone.first();
m_resultSetUpdate.moveToCurrentRow();
testPosition( clone, cloneRow, 1, "mixed clone/rowset move/insertion: clone check" );
testPosition( m_resultSet, m_row, 100, "mixed clone/rowset move/insertion: rowset check" );
}
// --------------------------------------------------------------------------------------------------------
private void testTableParameters()
{
// for a row set simply based on a table, there should be not parameters at all
createRowSet( "products", CommandType.TABLE, false );
try
{
verifyParameters( new String[] {}, "testTableParameters" );
}
catch( AssureException e ) { throw e; }
catch( Exception e )
{
assure( "testing the parameters of a table failed" + e.getMessage(), false );
}
}
// --------------------------------------------------------------------------------------------------------
private void testParametersAfterNormalExecute()
{
try
{
createRowSet( "SELECT * FROM \"customers\"", CommandType.COMMAND, true );
m_rowSetProperties.setPropertyValue( "Command", "SELECT * FROM \"customers\" WHERE \"City\" = :city");
XParameters rowsetParams = (XParameters)UnoRuntime.queryInterface( XParameters.class,
m_rowSet );
rowsetParams.setString( 1, "London" );
m_rowSet.execute();
}
catch( AssureException e ) { throw e; }
catch( Exception e )
{
assure( "testing the parameters of a table failed" + e.getMessage(), false );
}
}
// --------------------------------------------------------------------------------------------------------
private void verifyParameters( String[] _paramNames, String _context ) throws com.sun.star.uno.Exception
{
XIndexAccess params = m_paramsSupplier.getParameters();
int expected = _paramNames.length;
int found = params != null ? params.getCount() : 0;
assure( "wrong number of parameters (expected: " + expected + ", found: " + found + ") in " + _context,
found == expected );
if ( found == 0 )
return;
for ( int i=0; i<expected; ++i )
{
XPropertySet parameter = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class,
params.getByIndex(i) );
String expectedName = _paramNames[i];
String foundName = (String)parameter.getPropertyValue( "Name" );
assure( "wrong parameter name (expected: " + expectedName + ", found: " + foundName + ") in" + _context,
expectedName.equals( foundName ) );
}
}
// --------------------------------------------------------------------------------------------------------
private void testParametrizedQuery()
{
try
{
// for a row set based on a parametrized query, those parameters should be properly
// recognized
m_dataSource.createQuery( "products like", "SELECT * FROM \"products\" WHERE \"Name\" LIKE :product_name" );
createRowSet( "products like", CommandType.QUERY, false );
verifyParameters( new String[] { "product_name" }, "testParametrizedQuery" );
}
catch( AssureException e ) { throw e; }
catch( Exception e )
{
assure( "testing the parameters of a parametrized query failed" + e.getMessage(), false );
}
}
// --------------------------------------------------------------------------------------------------------
private void testParametersInteraction()
{
try
{
createRowSet( "products like", CommandType.QUERY, false );
// let's fill in a parameter value via XParameters, and see whether it is respected by the parameters container
XParameters rowsetParams = (XParameters)UnoRuntime.queryInterface( XParameters.class,
m_rowSet );
rowsetParams.setString( 1, "Apples" );
XIndexAccess params = m_paramsSupplier.getParameters();
XPropertySet firstParam = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex(0) );
Object firstParamValue = firstParam.getPropertyValue( "Value" );
assure( "XParameters and the parameters container do not properly interact",
firstParamValue.equals( "Apples" ) );
// let's see whether this also survices an execute of the row set
rowsetParams.setString( 1, "Oranges" );
m_rowSet.execute();
{
// TODO: the following would not be necessary if the parameters container would *survive*
// the execution of the row set. It currently doesn't (though the values it represents do).
// It would be nice, but not strictly necessary, if it would.
params = m_paramsSupplier.getParameters();
firstParam = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex(0) );
}
firstParamValue = firstParam.getPropertyValue( "Value" );
assure( "XParameters and the parameters container do not properly interact, after the row set has been executed",
firstParamValue.equals( "Oranges" ) );
}
catch( AssureException e ) { throw e; }
catch( Exception e )
{
assure( "could not text the relationship between XParameters and XParametersSupplier" + e.getMessage(), false );
}
}
// --------------------------------------------------------------------------------------------------------
private void testParametersInFilter()
{
try
{
createRowSet( "SELECT * FROM \"customers\"", CommandType.COMMAND, false );
m_rowSetProperties.setPropertyValue( "Filter", "\"City\" = :city" );
m_rowSetProperties.setPropertyValue( "ApplyFilter", new Boolean( true ) );
verifyParameters( new String[] { "city" }, "testParametersInFilter" );
m_rowSetProperties.setPropertyValue( "ApplyFilter", new Boolean( false ) );
verifyParameters( new String[] {}, "testParametersInFilter" );
}
catch( AssureException e ) { throw e; }
catch( Exception e )
{
assure( "testing the parameters within a WHERE clause failed" + e.getMessage(), false );
}
}
// --------------------------------------------------------------------------------------------------------
/** checks the XParametersSupplier functionality of a RowSet
*/
public void testParameters()
{
createTestCase( false );
// use an own RowSet instance, not the one which is also used for the other cases
testTableParameters();
testParametrizedQuery();
testParametersInFilter();
testParametersAfterNormalExecute();
testParametersInteraction();
}
}