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