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