1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 package complex.dbaccess; 24 25 import com.sun.star.beans.PropertyVetoException; 26 import com.sun.star.beans.UnknownPropertyException; 27 import com.sun.star.beans.XPropertySet; 28 import com.sun.star.container.XIndexAccess; 29 import com.sun.star.lang.WrappedTargetException; 30 import com.sun.star.lang.XComponent; 31 import com.sun.star.sdb.CommandType; 32 import com.sun.star.sdb.XParametersSupplier; 33 import com.sun.star.sdb.XResultSetAccess; 34 import com.sun.star.sdb.XRowSetApproveBroadcaster; 35 import com.sun.star.sdbc.SQLException; 36 import com.sun.star.sdbc.XParameters; 37 import com.sun.star.sdbc.XPreparedStatement; 38 import com.sun.star.sdbc.XResultSet; 39 import com.sun.star.sdbc.XResultSetUpdate; 40 import com.sun.star.sdbc.XRow; 41 import com.sun.star.sdbc.XRowSet; 42 import com.sun.star.sdbc.XRowUpdate; 43 import com.sun.star.sdbcx.XColumnsSupplier; 44 import com.sun.star.sdbcx.XDeleteRows; 45 import com.sun.star.sdbcx.XRowLocate; 46 import com.sun.star.uno.UnoRuntime; 47 48 import connectivity.tools.CRMDatabase; 49 import connectivity.tools.DataSource; 50 import connectivity.tools.HsqlDatabase; 51 import connectivity.tools.sdb.Connection; 52 import java.lang.reflect.Method; 53 import java.util.Random; 54 55 // ---------- junit imports ----------------- 56 import org.junit.Test; 57 import static org.junit.Assert.*; 58 // ------------------------------------------ 59 60 public class RowSet extends TestCase 61 { 62 63 static final int MAX_TABLE_ROWS = 100; 64 static final int MAX_FETCH_ROWS = 10; 65 private static final String NEXT = "next"; 66 private static final String TEST21 = "Test21"; 67 HsqlDatabase m_database; 68 DataSource m_dataSource; 69 XRowSet m_rowSet; 70 XResultSet m_resultSet; 71 XResultSetUpdate m_resultSetUpdate; 72 XRow m_row; 73 XRowLocate m_rowLocate; 74 XPropertySet m_rowSetProperties; 75 XParametersSupplier m_paramsSupplier; 76 77 // -------------------------------------------------------------------------------------------------------- 78 class ResultSetMovementStress implements Runnable 79 { 80 81 XResultSet m_resultSet; 82 XRow m_row; 83 int m_id; 84 ResultSetMovementStress(XResultSet _resultSet, int _id)85 ResultSetMovementStress(XResultSet _resultSet, int _id) throws java.lang.Exception 86 { 87 m_resultSet = _resultSet; 88 m_row = UnoRuntime.queryInterface( XRow.class, m_resultSet ); 89 m_id = _id; 90 } 91 run()92 public void run() 93 { 94 try 95 { 96 m_resultSet.beforeFirst(); 97 for (int i = 0; m_resultSet.next(); ++i) 98 { 99 int pos = m_resultSet.getRow(); 100 // final int val = m_row.getInt(1); 101 // System.out.println("Clone Move(" + m_id +") before i: " + (i+1) + " Pos: " + pos + " Val: " + val); 102 testPosition(m_resultSet, m_row, i + 1, "clone move(" + m_id + ")"); 103 // val = m_row.getInt(1); 104 // System.out.println("Clone Move(" + m_id +") after i: " + (i+1) + " Pos: " + pos + " Val: " + val); 105 int pos2 = m_resultSet.getRow(); 106 assertTrue("ResultSetMovementStress wrong position: " + i + " Pos1: " + pos + " Pos2: " + pos2, pos == pos2); 107 } 108 } 109 catch (Exception e) 110 { 111 fail("ResultSetMovementStress(" + m_id + ") failed: " + e); 112 } 113 } 114 } 115 // -------------------------------------------------------------------------------------------------------- createTestCase(boolean _defaultRowSet)116 private void createTestCase(boolean _defaultRowSet) 117 { 118 if (m_database == null) 119 { 120 try 121 { 122 final CRMDatabase database = new CRMDatabase( getMSF(), false ); 123 m_database = database.getDatabase(); 124 m_dataSource = m_database.getDataSource(); 125 } 126 catch (Exception e) 127 { 128 fail("could not create the embedded HSQL database: " + e.getMessage()); 129 } 130 } 131 132 try 133 { 134 createStruture(); 135 } 136 catch (SQLException e) 137 { 138 fail("could not connect to the database/table structure, error message:\n" + e.getMessage()); 139 } 140 141 if (_defaultRowSet) 142 { 143 createRowSet("TEST1", CommandType.TABLE, true, true); 144 } 145 } 146 147 // -------------------------------------------------------------------------------------------------------- 148 /** creates a com.sun.star.sdb.RowSet to use during the test 149 * @param command 150 * the command to use for the RowSet 151 * @param commandType 152 * the command type to use for the RowSet 153 * @param execute 154 * determines whether the RowSet should be executed 155 */ createRowSet(String command, int commandType, boolean execute)156 private void createRowSet(String command, int commandType, boolean execute) 157 { 158 createRowSet(command, commandType, execute, false); 159 } 160 161 // -------------------------------------------------------------------------------------------------------- 162 /** creates a com.sun.star.sdb.RowSet to use during the test 163 * @param command 164 * the command to use for the RowSet 165 * @param commandType 166 * the command type to use for the RowSet 167 * @param limitFetchSize 168 * determines whether the fetch size of the RowSet should be limited to MAX_FETCH_ROWS 169 * @param execute 170 * determines whether the RowSet should be executed 171 */ createRowSet(String command, int commandType, boolean execute, boolean limitFetchSize)172 private void createRowSet(String command, int commandType, boolean execute, boolean limitFetchSize) 173 { 174 try 175 { 176 m_rowSet = UnoRuntime.queryInterface( XRowSet.class, getMSF().createInstance( "com.sun.star.sdb.RowSet" ) ); 177 final XPropertySet rowSetProperties = UnoRuntime.queryInterface( XPropertySet.class, m_rowSet ); 178 rowSetProperties.setPropertyValue("Command", command); 179 rowSetProperties.setPropertyValue("CommandType", Integer.valueOf(commandType)); 180 rowSetProperties.setPropertyValue("ActiveConnection", m_database.defaultConnection().getXConnection()); 181 if (limitFetchSize) 182 { 183 rowSetProperties.setPropertyValue("FetchSize", Integer.valueOf(MAX_FETCH_ROWS)); 184 } 185 186 m_resultSet = UnoRuntime.queryInterface( XResultSet.class, m_rowSet ); 187 m_resultSetUpdate = UnoRuntime.queryInterface( XResultSetUpdate.class, m_rowSet ); 188 m_row = UnoRuntime.queryInterface( XRow.class, m_rowSet ); 189 m_rowLocate = UnoRuntime.queryInterface( XRowLocate.class, m_resultSet ); 190 m_rowSetProperties = UnoRuntime.queryInterface( XPropertySet.class, m_rowSet ); 191 m_paramsSupplier = UnoRuntime.queryInterface( XParametersSupplier.class, m_rowSet ); 192 193 if (execute) 194 { 195 m_rowSet.execute(); 196 } 197 } 198 catch (Exception e) 199 { 200 fail("caught an exception while creating the RowSet. Type:\n" + e.getClass().toString() + "\nMessage:\n" + e.getMessage()); 201 } 202 } 203 204 // -------------------------------------------------------------------------------------------------------- 205 @Test testRowSet()206 public void testRowSet() throws java.lang.Exception 207 { 208 209 System.out.println("testing testRowSet"); 210 createTestCase(true); 211 212 // sequential postioning 213 m_resultSet.beforeFirst(); 214 testSequentialPositining(m_resultSet, m_row); 215 216 // absolute positioning 217 testAbsolutePositioning(m_resultSet, m_row); 218 219 // 3rd test 220 test3(createClone(), m_resultSet); 221 // 4th test 222 test4(m_resultSet); 223 224 // concurrent (multi threaded) access to the row set and its clones 225 testConcurrentAccess(m_resultSet); 226 } 227 228 // -------------------------------------------------------------------------------------------------------- createClone()229 XResultSet createClone() throws SQLException 230 { 231 final XResultSetAccess rowAcc = UnoRuntime.queryInterface( XResultSetAccess.class, m_rowSet ); 232 return rowAcc.createResultSet(); 233 } 234 235 // -------------------------------------------------------------------------------------------------------- createStruture()236 void createStruture() throws SQLException 237 { 238 m_database.executeSQL("DROP TABLE \"TEST1\" IF EXISTS"); 239 m_database.executeSQL("CREATE TABLE \"TEST1\" (\"ID\" integer not null primary key, \"col2\" varchar(50) )"); 240 241 final Connection connection = m_database.defaultConnection(); 242 final XPreparedStatement prep = connection.prepareStatement("INSERT INTO \"TEST1\" values (?,?)"); 243 final XParameters para = UnoRuntime.queryInterface( XParameters.class, prep ); 244 for (int i = 1; i <= MAX_TABLE_ROWS; ++i) 245 { 246 para.setInt(1, i); 247 para.setString(2, "Test" + i); 248 prep.executeUpdate(); 249 } 250 251 connection.refreshTables(); 252 } 253 254 // -------------------------------------------------------------------------------------------------------- testPosition(XResultSet m_resultSet, XRow m_row, int expectedValue, String location)255 void testPosition(XResultSet m_resultSet, XRow m_row, int expectedValue, String location) throws SQLException 256 { 257 final int val = m_row.getInt(1); 258 final int pos = m_resultSet.getRow(); 259 assertTrue(location + ": value/position do not match: " + pos + " (pos) != " + val + " (val)", val == pos); 260 assertTrue(location + ": value/position are not as expected: " + val + " (val) != " + expectedValue + " (expected)", val == expectedValue); 261 } 262 263 // -------------------------------------------------------------------------------------------------------- testSequentialPositining(XResultSet _resultSet, XRow _row)264 void testSequentialPositining(XResultSet _resultSet, XRow _row) 265 { 266 try 267 { 268 // 1st test 269 int i = 1; 270 while (_resultSet.next()) 271 { 272 testPosition(_resultSet, _row, i, "testSequentialPositining"); 273 ++i; 274 } 275 } 276 catch (Exception e) 277 { 278 fail("testSequentialPositining failed: " + e); 279 } 280 } 281 282 // -------------------------------------------------------------------------------------------------------- testAbsolutePositioning(XResultSet _resultSet, XRow _row)283 void testAbsolutePositioning(XResultSet _resultSet, XRow _row) 284 { 285 try 286 { 287 for (int i = 1; i <= MAX_FETCH_ROWS; ++i) 288 { 289 final int calcPos = (MAX_TABLE_ROWS % i) + 1; 290 assertTrue("testAbsolutePositioning failed", _resultSet.absolute(calcPos)); 291 testPosition(_resultSet, _row, calcPos, "testAbsolutePositioning"); 292 } 293 } 294 catch (Exception e) 295 { 296 fail("testAbsolutePositioning failed: " + e); 297 } 298 } 299 300 // -------------------------------------------------------------------------------------------------------- test3(XResultSet clone, XResultSet _resultSet)301 void test3(XResultSet clone, XResultSet _resultSet) 302 { 303 try 304 { 305 final XRow _row = UnoRuntime.queryInterface( XRow.class, _resultSet ); 306 final XRow cloneRow = UnoRuntime.queryInterface( XRow.class, clone ); 307 for (int i = 1; i <= MAX_FETCH_ROWS; ++i) 308 { 309 final int calcPos = (MAX_TABLE_ROWS % i) + 1; 310 if (clone.absolute(calcPos)) 311 { 312 testPosition(clone, cloneRow, calcPos, "test3"); 313 testAbsolutePositioning(_resultSet, _row); 314 testAbsolutePositioning(clone, cloneRow); 315 } 316 } 317 } 318 catch (Exception e) 319 { 320 fail("test3 failed: " + e); 321 } 322 } 323 324 // -------------------------------------------------------------------------------------------------------- test4(XResultSet _resultSet)325 void test4(XResultSet _resultSet) 326 { 327 try 328 { 329 final XRow _row = UnoRuntime.queryInterface( XRow.class, _resultSet ); 330 _resultSet.beforeFirst(); 331 332 for (int i = 1; i <= MAX_TABLE_ROWS; ++i) 333 { 334 _resultSet.next(); 335 final XResultSet clone = createClone(); 336 final XRow cloneRow = UnoRuntime.queryInterface( XRow.class, clone ); 337 final int calcPos = MAX_TABLE_ROWS - 1; 338 if (calcPos != 0 && clone.absolute(calcPos)) 339 { 340 testPosition(clone, cloneRow, calcPos, "test4: clone"); 341 testPosition(_resultSet, _row, i, "test4: rowset"); 342 } 343 } 344 } 345 catch (Exception e) 346 { 347 fail("test4 failed: " + e); 348 } 349 } 350 351 // -------------------------------------------------------------------------------------------------------- testConcurrentAccess(XResultSet _resultSet)352 void testConcurrentAccess(XResultSet _resultSet) 353 { 354 System.out.println("testing Thread"); 355 try 356 { 357 // final XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet); 358 _resultSet.beforeFirst(); 359 360 final int numberOfThreads = 10; 361 362 final Thread threads[] = new Thread[numberOfThreads]; 363 for (int i = 0; i < numberOfThreads; ++i) 364 { 365 threads[i] = new Thread(new ResultSetMovementStress(createClone(), i)); 366 System.out.println("starting thread " + (i + 1) + " of " + (numberOfThreads)); 367 threads[i].start(); 368 } 369 370 for (int i = 0; i < numberOfThreads; ++i) 371 { 372 threads[i].join(); 373 } 374 } 375 catch (Exception e) 376 { 377 fail("testConcurrentAccess failed: " + e); 378 } 379 } 380 // -------------------------------------------------------------------------------------------------------- 381 382 @Test testRowSetEvents()383 public void testRowSetEvents() throws java.lang.Exception 384 { 385 System.out.println("testing RowSet Events"); 386 createTestCase(true); 387 388 // first we create our RowSet object 389 final RowSetEventListener pRow = new RowSetEventListener(); 390 391 final XColumnsSupplier colSup = UnoRuntime.queryInterface( XColumnsSupplier.class, m_rowSet ); 392 final XPropertySet col = UnoRuntime.queryInterface( XPropertySet.class, colSup.getColumns().getByName( "ID" ) ); 393 col.addPropertyChangeListener("Value", pRow); 394 m_rowSetProperties.addPropertyChangeListener("IsModified", pRow); 395 m_rowSetProperties.addPropertyChangeListener("IsNew", pRow); 396 m_rowSetProperties.addPropertyChangeListener("IsRowCountFinal", pRow); 397 m_rowSetProperties.addPropertyChangeListener("RowCount", pRow); 398 399 final XRowSetApproveBroadcaster xApBroad = UnoRuntime.queryInterface( XRowSetApproveBroadcaster.class, m_resultSet ); 400 xApBroad.addRowSetApproveListener(pRow); 401 m_rowSet.addRowSetListener(pRow); 402 403 // do some movements to check if we got all notifications 404 final Class cResSet = Class.forName("com.sun.star.sdbc.XResultSet"); 405 final boolean moves[] = new boolean[9]; 406 for (int i = 0; i < moves.length; ++i) 407 { 408 moves[i] = false; 409 } 410 moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true; 411 moves[RowSetEventListener.COLUMN_VALUE] = true; 412 moves[RowSetEventListener.CURSOR_MOVED] = true; 413 moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = true; 414 moves[RowSetEventListener.ROW_COUNT] = true; 415 416 testCursorMove(m_resultSet, cResSet.getMethod("afterLast", (Class[]) null), pRow, moves, null); 417 418 moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = false; 419 moves[RowSetEventListener.ROW_COUNT] = false; 420 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 421 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 422 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 423 testCursorMove(m_resultSet, cResSet.getMethod("last", (Class[]) null), pRow, moves, null); 424 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 425 testCursorMove(m_resultSet, cResSet.getMethod("first", (Class[]) null), pRow, moves, null); 426 testCursorMove(m_resultSet, cResSet.getMethod("previous", (Class[]) null), pRow, moves, null); 427 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 428 moves[RowSetEventListener.IS_MODIFIED] = true; 429 final XRowUpdate updRow = UnoRuntime.queryInterface( XRowUpdate.class, m_resultSet ); 430 updRow.updateString(2, TEST21); 431 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 432 433 moves[RowSetEventListener.IS_MODIFIED] = false; 434 updRow.updateString(2, m_row.getString(2)); 435 testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null); 436 437 moves[RowSetEventListener.IS_MODIFIED] = false; 438 final Class cupd = Class.forName("com.sun.star.sdbc.XResultSetUpdate"); 439 final XResultSetUpdate upd = UnoRuntime.queryInterface( XResultSetUpdate.class, m_resultSet ); 440 testCursorMove(upd, cupd.getMethod("moveToInsertRow", (Class[]) null), pRow, moves, null); 441 442 updRow.updateInt(1, MAX_TABLE_ROWS + 2); 443 updRow.updateString(2, "HHHH"); 444 moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = false; 445 moves[RowSetEventListener.CURSOR_MOVED] = false; 446 moves[RowSetEventListener.IS_MODIFIED] = true; 447 moves[RowSetEventListener.IS_NEW] = true; 448 moves[RowSetEventListener.ROW_COUNT] = true; 449 moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true; 450 moves[RowSetEventListener.ROW_CHANGED] = true; 451 testCursorMove(upd, cupd.getMethod("insertRow", (Class[]) null), pRow, moves, null); 452 453 moves[RowSetEventListener.IS_NEW] = false; 454 moves[RowSetEventListener.ROW_COUNT] = false; 455 m_resultSet.first(); 456 updRow.updateInt(1, MAX_TABLE_ROWS + 3); 457 updRow.updateString(2, "__"); 458 testCursorMove(upd, cupd.getMethod("updateRow", (Class[]) null), pRow, moves, null); 459 460 moves[RowSetEventListener.IS_NEW] = true; 461 moves[RowSetEventListener.ROW_COUNT] = true; 462 m_resultSet.first(); 463 testCursorMove(upd, cupd.getMethod("deleteRow", (Class[]) null), pRow, moves, null); 464 465 moves[RowSetEventListener.IS_NEW] = false; 466 moves[RowSetEventListener.COLUMN_VALUE] = true; 467 moves[RowSetEventListener.ROW_COUNT] = false; 468 m_resultSet.first(); 469 updRow.updateString(2, TEST21); 470 testCursorMove(m_resultSet, cResSet.getMethod("refreshRow", (Class[]) null), pRow, moves, null); 471 472 m_resultSet.first(); 473 updRow.updateString(2, TEST21); 474 testCursorMove(upd, cupd.getMethod("cancelRowUpdates", (Class[]) null), pRow, moves, null); 475 476 for (int i = 0; i < moves.length; ++i) 477 { 478 moves[i] = false; 479 } 480 moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true; 481 moves[RowSetEventListener.COLUMN_VALUE] = true; 482 moves[RowSetEventListener.CURSOR_MOVED] = true; 483 484 final Class cloc = Class.forName("com.sun.star.sdbcx.XRowLocate"); 485 m_resultSet.first(); 486 final Object bookmark = m_rowLocate.getBookmark(); 487 m_resultSet.next(); 488 final Object temp[] = new Object[1]; 489 temp[0] = bookmark; 490 Class ctemp[] = new Class[1]; 491 ctemp[0] = Object.class; 492 testCursorMove(m_rowLocate, cloc.getMethod("moveToBookmark", ctemp), pRow, moves, temp); 493 494 final Object temp2[] = new Object[2]; 495 temp2[0] = bookmark; 496 temp2[1] = Integer.valueOf(1); 497 final Class ctemp2[] = new Class[2]; 498 ctemp2[0] = Object.class; 499 ctemp2[1] = int.class; 500 testCursorMove(m_rowLocate, cloc.getMethod("moveRelativeToBookmark", ctemp2), pRow, moves, temp2); 501 502 for (int i = 0; i < moves.length; ++i) 503 { 504 moves[i] = false; 505 } 506 moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true; 507 moves[RowSetEventListener.ROW_CHANGED] = true; 508 moves[RowSetEventListener.ROW_COUNT] = true; 509 final Class cdelRows = Class.forName("com.sun.star.sdbcx.XDeleteRows"); 510 ctemp[0] = Object[].class; 511 final XDeleteRows delRows = UnoRuntime.queryInterface( XDeleteRows.class, m_resultSet ); 512 final Object bookmarks[] = new Object[5]; 513 m_resultSet.first(); 514 for (int i = 0; i < bookmarks.length; ++i) 515 { 516 m_resultSet.next(); 517 bookmarks[i] = m_rowLocate.getBookmark(); 518 } 519 520 temp[0] = bookmarks; 521 testCursorMove(delRows, cdelRows.getMethod("deleteRows", ctemp), pRow, moves, temp); 522 523 // now destroy the RowSet 524 final XComponent xComp = UnoRuntime.queryInterface( XComponent.class, m_resultSet ); 525 xComp.dispose(); 526 } 527 528 // -------------------------------------------------------------------------------------------------------- testCursorMove(Object res, Method _method, RowSetEventListener _evt, boolean _must[], Object args[])529 private void testCursorMove(Object res, Method _method, RowSetEventListener _evt, boolean _must[], Object args[]) throws java.lang.Exception 530 { 531 _evt.clearCalling(); 532 _method.invoke(res, args); 533 534 System.out.println("testing events for " + _method.getName()); 535 final int calling[] = _evt.getCalling(); 536 int pos = 1; 537 assertTrue("Callings are not in the correct order for APPROVE_CURSOR_MOVE ", 538 (!_must[RowSetEventListener.APPROVE_CURSOR_MOVE] || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == -1) || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == pos++); 539 assertTrue("Callings are not in the correct order for APPROVE_ROW_CHANGE", 540 (!_must[RowSetEventListener.APPROVE_ROW_CHANGE] || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == -1) || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == pos++); 541 assertTrue("Callings are not in the correct order for COLUMN_VALUE", 542 (!_must[RowSetEventListener.COLUMN_VALUE] || calling[RowSetEventListener.COLUMN_VALUE] == -1) || calling[RowSetEventListener.COLUMN_VALUE] == pos++); 543 assertTrue("Callings are not in the correct order for CURSOR_MOVED", 544 (!_must[RowSetEventListener.CURSOR_MOVED] || calling[RowSetEventListener.CURSOR_MOVED] == -1) || calling[RowSetEventListener.CURSOR_MOVED] == pos++); 545 assertTrue("Callings are not in the correct order for ROW_CHANGED", 546 (!_must[RowSetEventListener.ROW_CHANGED] || calling[RowSetEventListener.ROW_CHANGED] == -1) || calling[RowSetEventListener.ROW_CHANGED] == pos++); 547 assertTrue("Callings are not in the correct order for IS_MODIFIED", 548 (!_must[RowSetEventListener.IS_MODIFIED] || calling[RowSetEventListener.IS_MODIFIED] == -1) || calling[RowSetEventListener.IS_MODIFIED] == pos++); 549 assertTrue("Callings are not in the correct order for IS_NEW", 550 (!_must[RowSetEventListener.IS_NEW] || calling[RowSetEventListener.IS_NEW] == -1) || calling[RowSetEventListener.IS_NEW] == pos++); 551 assertTrue("Callings are not in the correct order for ROW_COUNT", 552 (!_must[RowSetEventListener.ROW_COUNT] || calling[RowSetEventListener.ROW_COUNT] == -1) || calling[RowSetEventListener.ROW_COUNT] == pos++); 553 assertTrue("Callings are not in the correct order for IS_ROW_COUNT_FINAL", 554 (!_must[RowSetEventListener.IS_ROW_COUNT_FINAL] || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == -1) || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == pos); 555 556 _evt.clearCalling(); 557 } 558 559 // -------------------------------------------------------------------------------------------------------- 560 /** returns the current row count of the RowSet 561 */ currentRowCount()562 private int currentRowCount() throws UnknownPropertyException, WrappedTargetException 563 { 564 final Integer rowCount = (Integer) m_rowSetProperties.getPropertyValue("RowCount"); 565 return rowCount.intValue(); 566 } 567 568 // -------------------------------------------------------------------------------------------------------- 569 /** positions the row set at an arbitrary position between 2 and (current row count - 1) 570 */ positionRandom()571 private int positionRandom() throws SQLException, UnknownPropertyException, WrappedTargetException 572 { 573 final int position = (new Random()).nextInt(currentRowCount() - 2) + 2; 574 assertTrue("sub task failed: could not position to row no. " + (Integer.valueOf(position)).toString(), 575 m_resultSet.absolute(position)); 576 return m_resultSet.getRow(); 577 } 578 579 // -------------------------------------------------------------------------------------------------------- 580 /** moves the result set to a random record between 2 and (current row count - 1), and deletes this record 581 * 582 * After returning from this method, the row set is still positioned at the deleted record 583 * @return 584 * the number/position of the record which has been deleted 585 */ deleteRandom()586 private int deleteRandom() throws SQLException, UnknownPropertyException, WrappedTargetException 587 { 588 // check if the current position and the row count in the result set is changed by a deletion (it should not) 589 final int positionBefore = positionRandom(); 590 final int rowCountBefore = currentRowCount(); 591 592 m_resultSetUpdate.deleteRow(); 593 594 final int positionAfter = m_resultSet.getRow(); 595 final int rowCountAfter = currentRowCount(); 596 assertTrue("position changed during |deleteRow| (it should not)", positionAfter == positionBefore); 597 assertTrue("row count changed with a |deleteRow| (it should not)", rowCountBefore == rowCountAfter); 598 assertTrue("RowSet does not report the current row as deleted after |deleteRow|", m_resultSet.rowDeleted()); 599 600 return positionBefore; 601 } 602 603 // -------------------------------------------------------------------------------------------------------- 604 @Test testDeleteBehavior()605 public void testDeleteBehavior() throws Exception 606 { 607 createTestCase(true); 608 609 // ensure that all records are known 610 m_resultSet.last(); 611 final int initialRowCount = currentRowCount(); 612 613 // delete a random row 614 int deletedRow = deleteRandom(); 615 616 // ..................................................................................................... 617 // asking for the bookmark of a deleted row should fail 618 boolean caughtException = false; 619 try 620 { 621 m_rowLocate.getBookmark(); 622 } 623 catch (SQLException e) 624 { 625 caughtException = true; 626 } 627 assertTrue("asking for the bookmark of a deleted row should throw an exception", caughtException); 628 629 // ..................................................................................................... 630 // isXXX methods should return |false| on a deleted row 631 assertTrue("one of the isFoo failed after |deleteRow|", !m_resultSet.isBeforeFirst() && !m_resultSet.isAfterLast() && !m_resultSet.isFirst() && !m_resultSet.isLast()); 632 // note that we can assume that isFirst / isLast also return |false|, since deleteRandom did 633 // not position on the first or last record, but inbetween 634 635 // ..................................................................................................... 636 // check if moving away from this row in either direction yields the expected results 637 assertTrue("|previous| after |deleteRow| failed", m_resultSet.previous()); 638 final int positionPrevious = m_resultSet.getRow(); 639 assertTrue("position after |previous| after |deleteRow| is not as expected", positionPrevious == deletedRow - 1); 640 641 deletedRow = deleteRandom(); 642 assertTrue("|next| after |deleteRow| failed", m_resultSet.next()); 643 final int positionAfter = m_resultSet.getRow(); 644 assertTrue("position after |next| after |deleteRow| is not as expected", positionAfter == deletedRow); 645 // since the deleted record "vanishs" as soon as the cursor is moved away from it, the absolute position does 646 // not change with a |next| call here 647 648 // ..................................................................................................... 649 // check if the deleted rows really vanished after moving away from them 650 assertTrue("row count did not change as expected after two deletions", initialRowCount - 2 == currentRowCount()); 651 652 // ..................................................................................................... 653 // check if the deleted row vanishes after moving to the insertion row 654 final int rowCountBefore = currentRowCount(); 655 final int deletedPos = deleteRandom(); 656 m_resultSetUpdate.moveToInsertRow(); 657 assertTrue("moving to the insertion row immediately after |deleteRow| does not adjust the row count", rowCountBefore == currentRowCount() + 1); 658 659 m_resultSetUpdate.moveToCurrentRow(); 660 assertTrue("|moveToCurrentRow| after |deleteRow| + |moveToInsertRow| results in unexpected position", 661 (m_resultSet.getRow() == deletedPos) && !m_resultSet.rowDeleted()); 662 663 // the same, but this time with deleting the first row (which is not covered by deleteRandom) 664 m_resultSet.last(); 665 m_resultSetUpdate.deleteRow(); 666 m_resultSetUpdate.moveToInsertRow(); 667 m_resultSetUpdate.moveToCurrentRow(); 668 assertTrue("|last| + |deleteRow| + |moveToInsertRow| + |moveToCurrentRow| results in wrong state", m_resultSet.isAfterLast()); 669 670 // ..................................................................................................... 671 // check if deleting a deleted row fails as expected 672 deleteRandom(); 673 caughtException = false; 674 try 675 { 676 m_resultSetUpdate.deleteRow(); 677 } 678 catch (SQLException e) 679 { 680 caughtException = true; 681 } 682 assertTrue("deleting a deleted row succeeded - it shouldn't", caughtException); 683 684 // ..................................................................................................... 685 // check if deleteRows fails if it contains the bookmark of a previously-deleted row 686 m_resultSet.first(); 687 final Object firstBookmark = m_rowLocate.getBookmark(); 688 positionRandom(); 689 final Object deleteBookmark = m_rowLocate.getBookmark(); 690 m_resultSetUpdate.deleteRow(); 691 final XDeleteRows multiDelete = UnoRuntime.queryInterface( XDeleteRows.class, m_resultSet ); 692 final int[] deleteSuccess = multiDelete.deleteRows(new Object[] 693 { 694 firstBookmark, deleteBookmark 695 }); 696 assertTrue("XDeleteRows::deleteRows with the bookmark of an already-deleted row failed", 697 (deleteSuccess.length == 2) && (deleteSuccess[0] != 0) && (deleteSuccess[1] == 0)); 698 699 // ..................................................................................................... 700 // check if refreshing a deleted row fails as expected 701 deleteRandom(); 702 caughtException = false; 703 try 704 { 705 m_resultSet.refreshRow(); 706 } 707 catch (SQLException e) 708 { 709 caughtException = true; 710 } 711 assertTrue("refreshing a deleted row succeeded - it shouldn't", caughtException); 712 713 // ..................................................................................................... 714 // rowUpdated/rowDeleted 715 deleteRandom(); 716 assertTrue("rowDeleted and/or rowUpdated are wrong on a deleted row", !m_resultSet.rowUpdated() && !m_resultSet.rowInserted()); 717 718 // ..................................................................................................... 719 // updating values in a deleted row should fail 720 deleteRandom(); 721 final XRowUpdate rowUpdated = UnoRuntime.queryInterface( XRowUpdate.class, m_resultSet ); 722 caughtException = false; 723 try 724 { 725 rowUpdated.updateString(2, TEST21); 726 } 727 catch (SQLException e) 728 { 729 caughtException = true; 730 } 731 assertTrue("updating values in a deleted row should not succeed", caughtException); 732 } 733 734 // -------------------------------------------------------------------------------------------------------- 735 /** checks whether deletions on the main RowSet properly interfere (or don't interfere) with the movement 736 * on a clone of the RowSet 737 */ 738 @Test testCloneMovesPlusDeletions()739 public void testCloneMovesPlusDeletions() throws SQLException, UnknownPropertyException, WrappedTargetException 740 { 741 createTestCase(true); 742 // ensure that all records are known 743 m_resultSet.last(); 744 745 final XResultSet clone = createClone(); 746 final XRowLocate cloneRowLocate = UnoRuntime.queryInterface( XRowLocate.class, clone ); 747 748 positionRandom(); 749 750 // ..................................................................................................... 751 // move the clone to the same record as the RowSet, and delete this record 752 cloneRowLocate.moveToBookmark(m_rowLocate.getBookmark()); 753 final int clonePosition = clone.getRow(); 754 m_resultSetUpdate.deleteRow(); 755 756 assertTrue("clone doesn't know that its current row has been deleted via the RowSet", clone.rowDeleted()); 757 assertTrue("clone's position changed somehow during deletion", clonePosition == clone.getRow()); 758 759 // ..................................................................................................... 760 // move the row set away from the deleted record. This should still not touch the state of the clone 761 m_resultSet.previous(); 762 763 assertTrue("clone doesn't know (anymore) that its current row has been deleted via the RowSet", clone.rowDeleted()); 764 assertTrue("clone's position changed somehow during deletion and RowSet-movement", clonePosition == clone.getRow()); 765 766 // ..................................................................................................... 767 // move the clone away from the deleted record 768 clone.next(); 769 assertTrue("clone still assumes that its row is deleted - but we already moved it", !clone.rowDeleted()); 770 771 // ..................................................................................................... 772 // check whether deleting the extremes (first / last) work 773 m_resultSet.first(); 774 cloneRowLocate.moveToBookmark(m_rowLocate.getBookmark()); 775 m_resultSetUpdate.deleteRow(); 776 clone.previous(); 777 assertTrue("deleting the first record left the clone in a strange state (after |previous|)", clone.isBeforeFirst()); 778 clone.next(); 779 assertTrue("deleting the first record left the clone in a strange state (after |previous| + |next|)", clone.isFirst()); 780 781 m_resultSet.last(); 782 cloneRowLocate.moveToBookmark(m_rowLocate.getBookmark()); 783 m_resultSetUpdate.deleteRow(); 784 clone.next(); 785 assertTrue("deleting the last record left the clone in a strange state (after |next|)", clone.isAfterLast()); 786 clone.previous(); 787 assertTrue("deleting the first record left the clone in a strange state (after |next| + |previous|)", clone.isLast()); 788 789 // ..................................................................................................... 790 // check whether movements of the clone interfere with movements of the RowSet, if the latter is on a deleted row 791 final int positionBefore = positionRandom(); 792 m_resultSetUpdate.deleteRow(); 793 assertTrue("|deleteRow|, but no |rowDeleted| (this should have been found much earlier!)", m_resultSet.rowDeleted()); 794 clone.beforeFirst(); 795 while (clone.next()); 796 assertTrue("row set forgot that the current row is deleted", m_resultSet.rowDeleted()); 797 798 assertTrue("moving to the next record after |deleteRow| and clone moves failed", m_resultSet.next()); 799 assertTrue("wrong position after |deleteRow| and clone movement", !m_resultSet.isAfterLast() && !m_resultSet.isBeforeFirst()); 800 assertTrue("wrong absolute position after |deleteRow| and clone movement", m_resultSet.getRow() == positionBefore); 801 } 802 803 // -------------------------------------------------------------------------------------------------------- 804 /** checks whether insertions on the main RowSet properly interfere (or don't interfere) with the movement 805 * on a clone of the RowSet 806 */ 807 @Test testCloneMovesPlusInsertions()808 public void testCloneMovesPlusInsertions() throws SQLException, UnknownPropertyException, WrappedTargetException, PropertyVetoException, com.sun.star.lang.IllegalArgumentException 809 { 810 createTestCase(true); 811 // ensure that all records are known 812 m_rowSetProperties.setPropertyValue("FetchSize", Integer.valueOf(10)); 813 814 final XResultSet clone = createClone(); 815 final XRow cloneRow = UnoRuntime.queryInterface( XRow.class, clone ); 816 817 // ..................................................................................................... 818 // first check the basic scenario without the |moveToInsertRow| |moveToCurrentRow|, to ensure that 819 // really those are broken, if at all 820 m_resultSet.last(); 821 clone.first(); 822 clone.absolute(11); 823 clone.first(); 824 825 final int rowValue1 = m_row.getInt(1); 826 final int rowPos = m_resultSet.getRow(); 827 final int rowValue2 = m_row.getInt(1); 828 assertTrue("repeated query for the same column value delivers different values (" + rowValue1 + " and " + rowValue2 + ") on row: " + rowPos, 829 rowValue1 == rowValue2); 830 831 testPosition(clone, cloneRow, 1, "mixed clone/rowset move: clone check"); 832 testPosition(m_resultSet, m_row, MAX_TABLE_ROWS, "mixed clone/rowset move: rowset check"); 833 834 // ..................................................................................................... 835 // now the complete scenario 836 m_resultSet.last(); 837 m_resultSetUpdate.moveToInsertRow(); 838 clone.first(); 839 clone.absolute(11); 840 clone.first(); 841 m_resultSetUpdate.moveToCurrentRow(); 842 843 testPosition(clone, cloneRow, 1, "mixed clone/rowset move/insertion: clone check"); 844 testPosition(m_resultSet, m_row, 100, "mixed clone/rowset move/insertion: rowset check"); 845 } 846 847 // -------------------------------------------------------------------------------------------------------- testTableParameters()848 private void testTableParameters() 849 { 850 // for a row set simply based on a table, there should be not parameters at all 851 createRowSet("products", CommandType.TABLE, false); 852 try 853 { 854 verifyParameters(new String[] 855 { 856 }, "testTableParameters"); 857 } 858 catch (Exception e) 859 { 860 fail("testing the parameters of a table failed" + e.getMessage()); 861 } 862 } 863 // -------------------------------------------------------------------------------------------------------- 864 testParametersAfterNormalExecute()865 private void testParametersAfterNormalExecute() 866 { 867 try 868 { 869 createRowSet("SELECT * FROM \"customers\"", CommandType.COMMAND, true); 870 m_rowSetProperties.setPropertyValue("Command", "SELECT * FROM \"customers\" WHERE \"City\" = :city"); 871 final XParameters rowsetParams = UnoRuntime.queryInterface( XParameters.class, m_rowSet ); 872 rowsetParams.setString(1, "London"); 873 m_rowSet.execute(); 874 } 875 catch (Exception e) 876 { 877 fail("testing the parameters of a table failed" + e.getMessage()); 878 } 879 } 880 881 // -------------------------------------------------------------------------------------------------------- verifyParameters(String[] _paramNames, String _context)882 private void verifyParameters(String[] _paramNames, String _context) throws com.sun.star.uno.Exception 883 { 884 final XIndexAccess params = m_paramsSupplier.getParameters(); 885 final int expected = _paramNames.length; 886 final int found = params != null ? params.getCount() : 0; 887 888 assertTrue("wrong number of parameters (expected: " + expected + ", found: " + found + ") in " + _context, 889 found == expected); 890 891 if (found == 0) 892 { 893 return; 894 } 895 896 for (int i = 0; i < expected; ++i) 897 { 898 final XPropertySet parameter = UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex( i ) ); 899 900 final String expectedName = _paramNames[i]; 901 final String foundName = (String) parameter.getPropertyValue("Name"); 902 assertTrue("wrong parameter name (expected: " + expectedName + ", found: " + foundName + ") in" + _context, 903 expectedName.equals(foundName)); 904 } 905 } 906 907 // -------------------------------------------------------------------------------------------------------- testParametrizedQuery()908 private void testParametrizedQuery() 909 { 910 try 911 { 912 // for a row set based on a parametrized query, those parameters should be properly 913 // recognized 914 m_dataSource.createQuery("products like", "SELECT * FROM \"products\" WHERE \"Name\" LIKE :product_name"); 915 createRowSet("products like", CommandType.QUERY, false); 916 verifyParameters(new String[] 917 { 918 "product_name" 919 }, "testParametrizedQuery"); 920 } 921 catch (Exception e) 922 { 923 fail("testing the parameters of a parametrized query failed" + e.getMessage()); 924 } 925 } 926 927 // -------------------------------------------------------------------------------------------------------- testParametersInteraction()928 private void testParametersInteraction() 929 { 930 try 931 { 932 createRowSet("products like", CommandType.QUERY, false); 933 934 // let's fill in a parameter value via XParameters, and see whether it is respected by the parameters container 935 final XParameters rowsetParams = UnoRuntime.queryInterface(XParameters.class, m_rowSet); 936 rowsetParams.setString(1, "Apples"); 937 938 XIndexAccess params = m_paramsSupplier.getParameters(); 939 XPropertySet firstParam = UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex( 0 ) ); 940 Object firstParamValue = firstParam.getPropertyValue("Value"); 941 942 assertTrue("XParameters and the parameters container do not properly interact", 943 "Apples".equals(firstParamValue)); 944 945 // let's see whether this also survices an execute of the row set 946 rowsetParams.setString(1, "Oranges"); 947 m_rowSet.execute(); 948 { 949 // TODO: the following would not be necessary if the parameters container would *survive* 950 // the execution of the row set. It currently doesn't (though the values it represents do). 951 // It would be nice, but not strictly necessary, if it would. 952 params = m_paramsSupplier.getParameters(); 953 firstParam = UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex( 0 ) ); 954 } 955 firstParamValue = firstParam.getPropertyValue("Value"); 956 assertTrue("XParameters and the parameters container do not properly interact, after the row set has been executed", 957 "Oranges".equals(firstParamValue)); 958 } 959 catch (Exception e) 960 { 961 fail("could not test the relationship between XParameters and XParametersSupplier" + e.getMessage()); 962 } 963 } 964 965 // -------------------------------------------------------------------------------------------------------- testParametersInFilter()966 private void testParametersInFilter() 967 { 968 try 969 { 970 createRowSet("SELECT * FROM \"customers\"", CommandType.COMMAND, false); 971 m_rowSetProperties.setPropertyValue("Filter", "\"City\" = :city"); 972 973 m_rowSetProperties.setPropertyValue("ApplyFilter", Boolean.TRUE); 974 verifyParameters(new String[] 975 { 976 "city" 977 }, "testParametersInFilter"); 978 979 m_rowSetProperties.setPropertyValue("ApplyFilter", Boolean.FALSE); 980 verifyParameters(new String[] 981 { 982 }, "testParametersInFilter"); 983 } 984 catch (Exception e) 985 { 986 fail("testing the parameters within a WHERE clause failed" + e.getMessage()); 987 } 988 } 989 990 // -------------------------------------------------------------------------------------------------------- 991 /** checks the XParametersSupplier functionality of a RowSet 992 */ 993 @Test testParameters()994 public void testParameters() 995 { 996 createTestCase(false); 997 // use an own RowSet instance, not the one which is also used for the other cases 998 999 testTableParameters(); 1000 testParametrizedQuery(); 1001 testParametersInFilter(); 1002 1003 testParametersAfterNormalExecute(); 1004 1005 testParametersInteraction(); 1006 } 1007 } 1008 1009