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 }