001 package com.mockrunner.mock.jdbc; 002 003 import java.sql.BatchUpdateException; 004 import java.sql.Connection; 005 import java.sql.ResultSet; 006 import java.sql.SQLException; 007 import java.sql.SQLWarning; 008 import java.sql.Statement; 009 import java.util.ArrayList; 010 import java.util.List; 011 012 import com.mockrunner.base.NestedApplicationException; 013 import com.mockrunner.jdbc.AbstractResultSetHandler; 014 import com.mockrunner.jdbc.SQLUtil; 015 import com.mockrunner.util.common.ArrayUtil; 016 017 /** 018 * Mock implementation of <code>Statement</code>. 019 */ 020 public class MockStatement implements Statement 021 { 022 private AbstractResultSetHandler resultSetHandler; 023 private ResultSet[] currentResultSets = null; 024 private int[] currentUpdateCounts = null; 025 private int currentResultSetIndex = 0; 026 private int currentUpdateCountIndex = 0; 027 private List batches = new ArrayList(); 028 private String cursorName = ""; 029 private int querySeconds = 0; 030 private int maxRows = 0; 031 private int maxFieldSize = 0; 032 private int fetchDirection = ResultSet.FETCH_FORWARD; 033 private int fetchSize = 0; 034 private int resultSetType = ResultSet.TYPE_FORWARD_ONLY; 035 private int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; 036 private int resultSetHoldability = ResultSet.HOLD_CURSORS_OVER_COMMIT; 037 private MockResultSet lastGeneratedKeys = null; 038 private boolean closed = false; 039 private boolean poolable = false; 040 private Connection connection; 041 042 public MockStatement(Connection connection) 043 { 044 this.connection = connection; 045 this.resultSetType = ResultSet.TYPE_FORWARD_ONLY; 046 this.resultSetConcurrency = ResultSet.CONCUR_READ_ONLY; 047 try 048 { 049 this.resultSetHoldability = connection.getMetaData().getResultSetHoldability(); 050 } 051 catch(SQLException exc) 052 { 053 throw new NestedApplicationException(exc); 054 } 055 } 056 057 public MockStatement(Connection connection, int resultSetType, int resultSetConcurrency) 058 { 059 this.connection = connection; 060 this.resultSetType = resultSetType; 061 this.resultSetConcurrency = resultSetConcurrency; 062 try 063 { 064 this.resultSetHoldability = connection.getMetaData().getResultSetHoldability(); 065 } 066 catch(SQLException exc) 067 { 068 throw new NestedApplicationException(exc); 069 } 070 } 071 072 public MockStatement(Connection connection, int resultSetType, int resultSetConcurrency, int resultSetHoldability) 073 { 074 this.connection = connection; 075 this.resultSetType = resultSetType; 076 this.resultSetConcurrency = resultSetConcurrency; 077 this.resultSetHoldability = resultSetHoldability; 078 } 079 080 public boolean isClosed() 081 { 082 return closed; 083 } 084 085 public void setResultSetHandler(AbstractResultSetHandler resultSetHandler) 086 { 087 this.resultSetHandler = resultSetHandler; 088 } 089 090 protected void setResultSets(ResultSet[] resultSets) 091 { 092 closeCurrentResultSets(); 093 this.currentUpdateCounts = null; 094 this.currentResultSets = resultSets; 095 this.currentResultSetIndex = 0; 096 this.currentUpdateCountIndex = 0; 097 } 098 099 protected void setUpdateCounts(int[] updateCounts) 100 { 101 closeCurrentResultSets(); 102 this.currentResultSets = null; 103 this.currentUpdateCounts = updateCounts; 104 this.currentResultSetIndex = 0; 105 this.currentUpdateCountIndex = 0; 106 } 107 108 public String getCursorName() 109 { 110 return cursorName; 111 } 112 113 public ResultSet executeQuery(String sql) throws SQLException 114 { 115 SQLException exception = resultSetHandler.getSQLException(sql); 116 if(null != exception) 117 { 118 throw exception; 119 } 120 resultSetHandler.addExecutedStatement(sql); 121 if(resultSetHandler.hasMultipleResultSets(sql)) 122 { 123 MockResultSet[] results = resultSetHandler.getResultSets(sql); 124 if(null != results) return cloneAndSetMultipleResultSets(results); 125 } 126 else 127 { 128 MockResultSet result = resultSetHandler.getResultSet(sql); 129 if(null != result) return cloneAndSetSingleResultSet(result); 130 } 131 if(resultSetHandler.hasMultipleGlobalResultSets()) 132 { 133 return cloneAndSetMultipleResultSets(resultSetHandler.getGlobalResultSets()); 134 } 135 return cloneAndSetSingleResultSet(resultSetHandler.getGlobalResultSet()); 136 } 137 138 private MockResultSet cloneAndSetSingleResultSet(MockResultSet result) 139 { 140 result = cloneResultSet(result); 141 if(null != result) 142 { 143 resultSetHandler.addReturnedResultSet(result); 144 } 145 setResultSets(new MockResultSet[] {result}); 146 setLastGeneratedKeysResultSet(null); 147 return result; 148 } 149 150 private MockResultSet cloneAndSetMultipleResultSets(MockResultSet[] results) 151 { 152 results = cloneResultSets(results); 153 if(null != results) 154 { 155 resultSetHandler.addReturnedResultSets(results); 156 } 157 setResultSets(results); 158 setLastGeneratedKeysResultSet(null); 159 if(null != results && results.length > 0) 160 { 161 return results[0]; 162 } 163 return null; 164 } 165 166 private void closeCurrentResultSets() 167 { 168 if(null != currentResultSets) 169 { 170 for(int ii = 0; ii < currentResultSets.length; ii++) 171 { 172 try 173 { 174 if(null != currentResultSets[ii]) 175 { 176 currentResultSets[ii].close(); 177 } 178 } 179 catch(SQLException exc) 180 { 181 throw new NestedApplicationException(exc); 182 } 183 } 184 } 185 } 186 187 public int executeUpdate(String sql) throws SQLException 188 { 189 SQLException exception = resultSetHandler.getSQLException(sql); 190 if(null != exception) 191 { 192 throw exception; 193 } 194 resultSetHandler.addExecutedStatement(sql); 195 if(resultSetHandler.hasMultipleUpdateCounts(sql)) 196 { 197 Integer[] returnValues = resultSetHandler.getUpdateCounts(sql); 198 if(null != returnValues) 199 { 200 return setMultipleUpdateCounts((int[])ArrayUtil.convertToPrimitiveArray(returnValues)); 201 } 202 } 203 else 204 { 205 Integer returnValue = resultSetHandler.getUpdateCount(sql); 206 if(null != returnValue) 207 { 208 return setSingleUpdateCount(returnValue.intValue()); 209 } 210 } 211 if(resultSetHandler.hasMultipleGlobalUpdateCounts()) 212 { 213 return setMultipleUpdateCounts(resultSetHandler.getGlobalUpdateCounts()); 214 } 215 return setSingleUpdateCount(resultSetHandler.getGlobalUpdateCount()); 216 } 217 218 private int setSingleUpdateCount(int updateCount) 219 { 220 setUpdateCounts(new int[] {updateCount}); 221 setLastGeneratedKeysResultSet(null); 222 return updateCount; 223 } 224 225 private int setMultipleUpdateCounts(int[] updateCounts) 226 { 227 setUpdateCounts(updateCounts); 228 setLastGeneratedKeysResultSet(null); 229 if(null != updateCounts && updateCounts.length > 0) 230 { 231 return updateCounts[0]; 232 } 233 return 0; 234 } 235 236 public boolean execute(String sql) throws SQLException 237 { 238 boolean callExecuteQuery = isQuery(sql); 239 if(callExecuteQuery) 240 { 241 executeQuery(sql); 242 } 243 else 244 { 245 executeUpdate(sql); 246 } 247 return callExecuteQuery; 248 } 249 250 public int[] executeBatch() throws SQLException 251 { 252 int[] results = new int[batches.size()]; 253 SQLException exception = null; 254 for(int ii = 0; ii < results.length; ii++) 255 { 256 String nextSQL = (String)batches.get(ii); 257 if(isQuery(nextSQL)) 258 { 259 exception = prepareFailedResult(results, ii, "SQL " + batches.get(ii) + " in the list of batches returned a ResultSet.", null); 260 } 261 else 262 { 263 try 264 { 265 results[ii] = executeUpdate(nextSQL); 266 } 267 catch(SQLException exc) 268 { 269 exception = prepareFailedResult(results, ii, null, exc); 270 } 271 } 272 if(null != exception && !resultSetHandler.getContinueProcessingOnBatchFailure()) 273 { 274 throw exception; 275 } 276 } 277 if(null != exception) 278 { 279 throw new BatchUpdateException(exception.getMessage(), exception.getSQLState(), exception.getErrorCode(), results); 280 } 281 return results; 282 } 283 284 protected SQLException prepareFailedResult(int[] actualResults, int index, String message, SQLException caughtException) 285 { 286 actualResults[index] = -3; 287 if(caughtException instanceof BatchUpdateException) 288 { 289 return caughtException; 290 } 291 else 292 { 293 int[] partialResults = (int[])ArrayUtil.truncateArray(actualResults, index); 294 if(null == caughtException) 295 { 296 return new BatchUpdateException(message, partialResults); 297 } 298 else 299 { 300 return new BatchUpdateException(caughtException.getMessage(), caughtException.getSQLState(), caughtException.getErrorCode(), partialResults); 301 } 302 } 303 } 304 305 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException 306 { 307 int updateCount = executeUpdate(sql); 308 setGeneratedKeysResultSet(sql, autoGeneratedKeys); 309 return updateCount; 310 } 311 312 public int executeUpdate(String sql, int[] columnIndexes) throws SQLException 313 { 314 return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); 315 } 316 317 public int executeUpdate(String sql, String[] columnNames) throws SQLException 318 { 319 return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); 320 } 321 322 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException 323 { 324 boolean isQuery = execute(sql); 325 setGeneratedKeysResultSet(sql, autoGeneratedKeys); 326 return isQuery; 327 } 328 329 public boolean execute(String sql, int[] columnIndexes) throws SQLException 330 { 331 return execute(sql, Statement.RETURN_GENERATED_KEYS); 332 } 333 334 public boolean execute(String sql, String[] columnNames) throws SQLException 335 { 336 return execute(sql, Statement.RETURN_GENERATED_KEYS); 337 } 338 339 private void setGeneratedKeysResultSet(String sql, int autoGeneratedKeys) throws SQLException 340 { 341 if(Statement.RETURN_GENERATED_KEYS != autoGeneratedKeys && Statement.NO_GENERATED_KEYS != autoGeneratedKeys) 342 { 343 throw new SQLException("autoGeneratedKeys must be either Statement.RETURN_GENERATED_KEYS or Statement.NO_GENERATED_KEYS"); 344 } 345 if(Statement.RETURN_GENERATED_KEYS == autoGeneratedKeys) 346 { 347 setLastGeneratedKeysResultSet(determineGeneratedKeysResultSet(sql)); 348 } 349 else 350 { 351 setLastGeneratedKeysResultSet(null); 352 } 353 } 354 355 protected void setLastGeneratedKeysResultSet(MockResultSet generatedKeys) 356 { 357 lastGeneratedKeys = generatedKeys; 358 } 359 360 protected MockResultSet determineGeneratedKeysResultSet(String sql) 361 { 362 MockResultSet generatedKeys = resultSetHandler.getGeneratedKeys(sql); 363 if(null != generatedKeys) return generatedKeys; 364 return resultSetHandler.getGlobalGeneratedKeys(); 365 } 366 367 public void close() throws SQLException 368 { 369 closed = true; 370 } 371 372 public int getMaxFieldSize() throws SQLException 373 { 374 return maxFieldSize; 375 } 376 377 public void setMaxFieldSize(int maxFieldSize) throws SQLException 378 { 379 this.maxFieldSize = maxFieldSize; 380 } 381 382 public int getMaxRows() throws SQLException 383 { 384 return maxRows; 385 } 386 387 public void setMaxRows(int maxRows) throws SQLException 388 { 389 this.maxRows = maxRows; 390 } 391 392 public void setEscapeProcessing(boolean enable) throws SQLException 393 { 394 395 } 396 397 public int getQueryTimeout() throws SQLException 398 { 399 return querySeconds; 400 } 401 402 public void setQueryTimeout(int querySeconds) throws SQLException 403 { 404 this.querySeconds = querySeconds; 405 } 406 407 public void cancel() throws SQLException 408 { 409 410 } 411 412 public SQLWarning getWarnings() throws SQLException 413 { 414 return null; 415 } 416 417 public void clearWarnings() throws SQLException 418 { 419 420 } 421 422 public void setCursorName(String cursorName) throws SQLException 423 { 424 this.cursorName = cursorName; 425 } 426 427 protected boolean isQuery(String sql) 428 { 429 boolean isQuery; 430 Boolean returnsResultSet = resultSetHandler.getReturnsResultSet(sql); 431 if(null != returnsResultSet) 432 { 433 isQuery = returnsResultSet.booleanValue(); 434 } 435 else 436 { 437 isQuery = SQLUtil.isSelect(sql); 438 } 439 return isQuery; 440 } 441 442 public ResultSet getResultSet() throws SQLException 443 { 444 if(null == currentResultSets) return null; 445 if(currentResultSetIndex >= currentResultSets.length) return null; 446 return currentResultSets[currentResultSetIndex]; 447 } 448 449 public int getUpdateCount() throws SQLException 450 { 451 if(null == currentUpdateCounts) return -1; 452 if(currentUpdateCountIndex >= currentUpdateCounts.length) return -1; 453 return currentUpdateCounts[currentUpdateCountIndex]; 454 } 455 456 public boolean getMoreResults(int current) throws SQLException 457 { 458 return getMoreResults(current != Statement.KEEP_CURRENT_RESULT); 459 } 460 461 public boolean getMoreResults() throws SQLException 462 { 463 return getMoreResults(true); 464 } 465 466 private boolean getMoreResults(boolean doCloseCurrentResult) throws SQLException 467 { 468 if(null != currentResultSets) 469 { 470 if(currentResultSetIndex < currentResultSets.length) 471 { 472 if(null != currentResultSets[currentResultSetIndex] && doCloseCurrentResult) 473 { 474 currentResultSets[currentResultSetIndex].close(); 475 } 476 currentResultSetIndex++; 477 } 478 return (currentResultSetIndex < currentResultSets.length); 479 } 480 else if(null != currentUpdateCounts) 481 { 482 if(currentUpdateCountIndex < currentUpdateCounts.length) 483 { 484 currentUpdateCountIndex++; 485 } 486 } 487 return false; 488 } 489 490 public void setFetchDirection(int fetchDirection) throws SQLException 491 { 492 this.fetchDirection = fetchDirection; 493 } 494 495 public int getFetchDirection() throws SQLException 496 { 497 return fetchDirection; 498 } 499 500 public void setFetchSize(int fetchSize) throws SQLException 501 { 502 this.fetchSize = fetchSize; 503 } 504 505 public int getFetchSize() throws SQLException 506 { 507 return fetchSize; 508 } 509 510 public void addBatch(String sql) throws SQLException 511 { 512 batches.add(sql); 513 } 514 515 public void clearBatch() throws SQLException 516 { 517 batches.clear(); 518 } 519 520 public Connection getConnection() throws SQLException 521 { 522 return connection; 523 } 524 525 public ResultSet getGeneratedKeys() throws SQLException 526 { 527 if(null == lastGeneratedKeys) 528 { 529 MockResultSet resultSet = new MockResultSet("Last statement did not generate any keys"); 530 resultSet.setStatement(this); 531 return resultSet; 532 } 533 return cloneResultSet(lastGeneratedKeys); 534 } 535 536 public int getResultSetType() throws SQLException 537 { 538 return resultSetType; 539 } 540 541 public int getResultSetConcurrency() throws SQLException 542 { 543 return resultSetConcurrency; 544 } 545 546 public int getResultSetHoldability() throws SQLException 547 { 548 return resultSetHoldability; 549 } 550 551 public boolean isPoolable() throws SQLException 552 { 553 return poolable; 554 } 555 556 public void setPoolable(boolean poolable) throws SQLException 557 { 558 this.poolable = poolable; 559 } 560 561 public boolean isWrapperFor(Class iface) throws SQLException 562 { 563 return false; 564 } 565 566 public Object unwrap(Class iface) throws SQLException 567 { 568 throw new SQLException("No object found for " + iface); 569 } 570 571 protected MockResultSet cloneResultSet(MockResultSet resultSet) 572 { 573 if(null == resultSet) return null; 574 MockResultSet clone = (MockResultSet)resultSet.clone(); 575 clone.setStatement(this); 576 return clone; 577 } 578 579 protected MockResultSet[] cloneResultSets(MockResultSet[] resultSets) 580 { 581 if(null == resultSets) return null; 582 MockResultSet[] clonedResultsSets = new MockResultSet[resultSets.length]; 583 for(int ii = 0; ii < resultSets.length; ii++) 584 { 585 if(null != resultSets[ii]) 586 { 587 clonedResultsSets[ii] = (MockResultSet)resultSets[ii].clone(); 588 clonedResultsSets[ii].setStatement(this); 589 } 590 } 591 return clonedResultsSets; 592 } 593 }