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 }