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    }