001 /**
002 * Copyright 2007-2008 Arthur Blake
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package net.sf.log4jdbc;
017
018 import java.sql.CallableStatement;
019 import java.sql.Connection;
020 import java.sql.PreparedStatement;
021 import java.sql.ResultSet;
022 import java.sql.SQLException;
023 import java.sql.SQLWarning;
024 import java.sql.Statement;
025 import java.util.List;
026 import java.util.ArrayList;
027
028 /**
029 * Wraps a Statement and reports method calls, returns and exceptions.
030 *
031 * jdbc 4 version
032 *
033 * @author Arthur Blake
034 */
035 public class StatementSpy implements Statement, Spy
036 {
037 protected final SpyLogDelegator log;
038
039 /**
040 * The Connection that created this Statement.
041 */
042 protected ConnectionSpy connectionSpy;
043
044 /**
045 * The real statement that this StatementSpy wraps.
046 */
047 protected Statement realStatement;
048
049 /**
050 * Create a StatementSpy that wraps another Statement
051 * for the purpose of logging all method calls, sql, exceptions and return values.
052 *
053 * @param connectionSpy Connection that created this Statement.
054 * @param realStatement real underlying Statement that this StatementSpy wraps.
055 */
056 public StatementSpy(ConnectionSpy connectionSpy, Statement realStatement)
057 {
058 if (realStatement == null)
059 {
060 throw new IllegalArgumentException("Must pass in a non null real Statement");
061 }
062 if (connectionSpy == null)
063 {
064 throw new IllegalArgumentException("Must pass in a non null ConnectionSpy");
065 }
066 this.realStatement = realStatement;
067 this.connectionSpy = connectionSpy;
068
069 log = SpyLogFactory.getSpyLogDelegator();
070
071 if (realStatement instanceof CallableStatement)
072 {
073 reportReturn("new CallableStatement");
074 }
075 else if (realStatement instanceof PreparedStatement)
076 {
077 reportReturn("new PreparedStatement");
078 }
079 else
080 {
081 reportReturn("new Statement");
082 }
083 }
084
085 public String getClassType()
086 {
087 return "Statement";
088 }
089
090 public Integer getConnectionNumber()
091 {
092 return connectionSpy.getConnectionNumber();
093 }
094
095 /**
096 * Report an exception to be logged which includes timing data on a sql failure.
097 * @param methodCall description of method call and arguments passed to it that generated the exception.
098 * @param exception exception that was generated
099 * @param sql SQL associated with the call.
100 * @param execTime amount of time that the jdbc driver was chugging on the SQL before it threw an exception.
101 */
102 protected void reportException(String methodCall, SQLException exception, String sql, long execTime)
103 {
104 log.exceptionOccured(this, methodCall, exception, sql, execTime);
105 }
106
107 /**
108 * Report an exception to be logged.
109 * @param methodCall description of method call and arguments passed to it that generated the exception.
110 * @param exception exception that was generated
111 * @param sql SQL associated with the call.
112 */
113 protected void reportException(String methodCall, SQLException exception, String sql)
114 {
115 log.exceptionOccured(this, methodCall, exception, sql, -1L);
116 }
117
118 /**
119 * Report an exception to be logged.
120 *
121 * @param methodCall description of method call and arguments passed to it that generated the exception.
122 * @param exception exception that was generated
123 */
124 protected void reportException(String methodCall, SQLException exception)
125 {
126 log.exceptionOccured(this, methodCall, exception, null, -1L);
127 }
128
129 /**
130 * Report (for logging) that a method returned. All the other reportReturn methods are conveniance methods that call this method.
131 *
132 * @param methodCall description of method call and arguments passed to it that returned.
133 * @param msg description of what the return value that was returned. may be an empty String for void return types.
134 */
135 protected void reportAllReturns(String methodCall, String msg)
136 {
137 log.methodReturned(this, methodCall, msg);
138 }
139
140 /**
141 * Conveniance method to report (for logging) that a method returned a boolean value.
142 *
143 * @param methodCall description of method call and arguments passed to it that returned.
144 * @param value boolean return value.
145 * @return the boolean return value as passed in.
146 */
147 protected boolean reportReturn(String methodCall, boolean value)
148 {
149 reportAllReturns(methodCall, "" + value);
150 return value;
151 }
152
153 /**
154 * Conveniance method to report (for logging) that a method returned a byte value.
155 *
156 * @param methodCall description of method call and arguments passed to it that returned.
157 * @param value byte return value.
158 * @return the byte return value as passed in.
159 */
160 protected byte reportReturn(String methodCall, byte value)
161 {
162 reportAllReturns(methodCall, "" + value);
163 return value;
164 }
165
166 /**
167 * Conveniance method to report (for logging) that a method returned a int value.
168 *
169 * @param methodCall description of method call and arguments passed to it that returned.
170 * @param value int return value.
171 * @return the int return value as passed in.
172 */
173 protected int reportReturn(String methodCall, int value)
174 {
175 reportAllReturns(methodCall, "" + value);
176 return value;
177 }
178
179 /**
180 * Conveniance method to report (for logging) that a method returned a double value.
181 *
182 * @param methodCall description of method call and arguments passed to it that returned.
183 * @param value double return value.
184 * @return the double return value as passed in.
185 */
186 protected double reportReturn(String methodCall, double value)
187 {
188 reportAllReturns(methodCall, "" + value);
189 return value;
190 }
191
192 /**
193 * Conveniance method to report (for logging) that a method returned a short value.
194 *
195 * @param methodCall description of method call and arguments passed to it that returned.
196 * @param value short return value.
197 * @return the short return value as passed in.
198 */
199 protected short reportReturn(String methodCall, short value)
200 {
201 reportAllReturns(methodCall, "" + value);
202 return value;
203 }
204
205 /**
206 * Conveniance method to report (for logging) that a method returned a long value.
207 *
208 * @param methodCall description of method call and arguments passed to it that returned.
209 * @param value long return value.
210 * @return the long return value as passed in.
211 */
212 protected long reportReturn(String methodCall, long value)
213 {
214 reportAllReturns(methodCall, "" + value);
215 return value;
216 }
217
218 /**
219 * Conveniance method to report (for logging) that a method returned a float value.
220 *
221 * @param methodCall description of method call and arguments passed to it that returned.
222 * @param value float return value.
223 * @return the float return value as passed in.
224 */
225 protected float reportReturn(String methodCall, float value)
226 {
227 reportAllReturns(methodCall, "" + value);
228 return value;
229 }
230
231 /**
232 * Conveniance method to report (for logging) that a method returned an Object.
233 *
234 * @param methodCall description of method call and arguments passed to it that returned.
235 * @param value return Object.
236 * @return the return Object as passed in.
237 */
238 protected Object reportReturn(String methodCall, Object value)
239 {
240 reportAllReturns(methodCall, "" + value);
241 return value;
242 }
243
244 /**
245 * Conveniance method to report (for logging) that a method returned (void return type).
246 *
247 * @param methodCall description of method call and arguments passed to it that returned.
248 */
249 protected void reportReturn(String methodCall)
250 {
251 reportAllReturns(methodCall, "");
252 }
253
254 /**
255 * Running one-off statement sql is generally inefficient and a bad idea for various reasons,
256 * so give a warning when this is done.
257 */
258 private static final String StatementSqlWarning = "{WARNING: Statement used to run SQL} ";
259
260 /**
261 * Report SQL for logging with a warning that it was generated from a statement.
262 *
263 * @param sql the SQL being run
264 * @param methodCall the name of the method that was running the SQL
265 */
266 protected void reportStatementSql(String sql, String methodCall)
267 {
268 // redirect to one more method call ONLY so that stack trace search is consistent
269 // with the reportReturn calls
270 _reportSql(StatementSqlWarning + sql, methodCall);
271 }
272
273 /**
274 * Report SQL for logging with a warning that it was generated from a statement.
275 *
276 * @param execTime execution time in msec.
277 * @param sql the SQL being run
278 * @param methodCall the name of the method that was running the SQL
279 */
280 protected void reportStatementSqlTiming(long execTime, String sql, String methodCall)
281 {
282 // redirect to one more method call ONLY so that stack trace search is consistent
283 // with the reportReturn calls
284 _reportSqlTiming(execTime, StatementSqlWarning + sql, methodCall);
285 }
286
287 /**
288 * Report SQL for logging.
289 *
290 * @param execTime execution time in msec.
291 * @param sql the SQL being run
292 * @param methodCall the name of the method that was running the SQL
293 */
294 protected void reportSqlTiming(long execTime, String sql, String methodCall)
295 {
296 // redirect to one more method call ONLY so that stack trace search is consistent
297 // with the reportReturn calls
298 _reportSqlTiming(execTime, sql, methodCall);
299 }
300
301 /**
302 * Report SQL for logging.
303 *
304 * @param sql the SQL being run
305 * @param methodCall the name of the method that was running the SQL
306 */
307 protected void reportSql(String sql, String methodCall)
308 {
309 // redirect to one more method call ONLY so that stack trace search is consistent
310 // with the reportReturn calls
311 _reportSql(sql, methodCall);
312 }
313
314 private void _reportSql(String sql, String methodCall)
315 {
316 log.sqlOccured(this, methodCall, sql);
317 }
318
319 private void _reportSqlTiming(long execTime, String sql, String methodCall)
320 {
321 log.sqlTimingOccured(this, execTime, methodCall, sql);
322 }
323
324 // implementation of interface methods
325 public SQLWarning getWarnings() throws SQLException
326 {
327 String methodCall = "getWarnings()";
328 try
329 {
330 return (SQLWarning) reportReturn(methodCall, realStatement.getWarnings());
331 }
332 catch (SQLException s)
333 {
334 reportException(methodCall, s);
335 throw s;
336 }
337 }
338
339 public int executeUpdate(String sql, String[] columnNames) throws SQLException
340 {
341 String methodCall = "executeUpdate(" + sql + ", " + columnNames + ")";
342 reportStatementSql(sql, methodCall);
343 long tstart = System.currentTimeMillis();
344 try
345 {
346 int result = realStatement.executeUpdate(sql, columnNames);
347 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
348 return reportReturn(methodCall, result);
349 }
350 catch (SQLException s)
351 {
352 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
353 throw s;
354 }
355 }
356
357 public boolean execute(String sql, String[] columnNames) throws SQLException
358 {
359 String methodCall = "execute(" + sql + ", " + columnNames + ")";
360 reportStatementSql(sql, methodCall);
361 long tstart = System.currentTimeMillis();
362 try
363 {
364 boolean result = realStatement.execute(sql, columnNames);
365 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
366 return reportReturn(methodCall, result);
367 }
368 catch (SQLException s)
369 {
370 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
371 throw s;
372 }
373 }
374
375 public void setMaxRows(int max) throws SQLException
376 {
377 String methodCall = "setMaxRows(" + max + ")";
378 try
379 {
380 realStatement.setMaxRows(max);
381 }
382 catch (SQLException s)
383 {
384 reportException(methodCall, s);
385 throw s;
386 }
387 reportReturn(methodCall);
388 }
389
390 public boolean getMoreResults() throws SQLException
391 {
392 String methodCall = "getMoreResults()";
393
394 try
395 {
396 return reportReturn(methodCall, realStatement.getMoreResults());
397 }
398 catch (SQLException s)
399 {
400 reportException(methodCall, s);
401 throw s;
402 }
403 }
404
405 public void clearWarnings() throws SQLException
406 {
407 String methodCall = "clearWarnings()";
408 try
409 {
410 realStatement.clearWarnings();
411 }
412 catch (SQLException s)
413 {
414 reportException(methodCall, s);
415 throw s;
416 }
417 reportReturn(methodCall);
418 }
419
420 /**
421 * Tracking of current batch (see addBatch, clearBatch and executeBatch)
422 * //todo: should access to this List be synchronized?
423 */
424 protected List currentBatch = new ArrayList();
425
426 public void addBatch(String sql) throws SQLException
427 {
428 String methodCall = "addBatch(" + sql + ")";
429
430 currentBatch.add(StatementSqlWarning + sql);
431 try
432 {
433 realStatement.addBatch(sql);
434 }
435 catch (SQLException s)
436 {
437 reportException(methodCall,s);
438 throw s;
439 }
440 reportReturn(methodCall);
441 }
442
443 public int getResultSetType() throws SQLException
444 {
445 String methodCall = "getResultSetType()";
446 try
447 {
448 return reportReturn(methodCall, realStatement.getResultSetType());
449 }
450 catch (SQLException s)
451 {
452 reportException(methodCall, s);
453 throw s;
454 }
455 }
456
457 public void clearBatch() throws SQLException
458 {
459 String methodCall = "clearBatch()";
460 try
461 {
462 realStatement.clearBatch();
463 }
464 catch (SQLException s)
465 {
466 reportException(methodCall, s);
467 throw s;
468 }
469 currentBatch.clear();
470 reportReturn(methodCall);
471 }
472
473 public void setFetchDirection(int direction) throws SQLException
474 {
475 String methodCall = "setFetchDirection(" + direction + ")";
476 try
477 {
478 realStatement.setFetchDirection(direction);
479 }
480 catch (SQLException s)
481 {
482 reportException(methodCall, s);
483 throw s;
484 }
485 reportReturn(methodCall);
486 }
487
488 public int[] executeBatch() throws SQLException
489 {
490 String methodCall = "executeBatch()";
491
492 int j=currentBatch.size();
493 StringBuffer batchReport = new StringBuffer("batching " + j + " statements:");
494
495 int fieldSize = (""+j).length();
496
497 String sql;
498 for (int i=0; i < j;)
499 {
500 sql = (String) currentBatch.get(i);
501 batchReport.append("\n");
502 batchReport.append(Utilities.rightJustify(fieldSize,""+(++i)));
503 batchReport.append(": ");
504 batchReport.append(sql);
505 }
506
507 sql = batchReport.toString();
508 reportSql(sql, methodCall);
509 long tstart = System.currentTimeMillis();
510
511 int[] updateResults;
512 try
513 {
514 updateResults = realStatement.executeBatch();
515 reportSqlTiming(System.currentTimeMillis()-tstart, sql, methodCall);
516 }
517 catch (SQLException s)
518 {
519 reportException(methodCall, s, sql, System.currentTimeMillis()-tstart);
520 throw s;
521 }
522 return (int[])reportReturn(methodCall,updateResults);
523 }
524
525 public void setFetchSize(int rows) throws SQLException
526 {
527 String methodCall = "setFetchSize(" + rows + ")";
528 try
529 {
530 realStatement.setFetchSize(rows);
531 }
532 catch (SQLException s)
533 {
534 reportException(methodCall, s);
535 throw s;
536 }
537 reportReturn(methodCall);
538 }
539
540 public int getQueryTimeout() throws SQLException
541 {
542 String methodCall = "getQueryTimeout()";
543 try
544 {
545 return reportReturn(methodCall, realStatement.getQueryTimeout());
546 }
547 catch (SQLException s)
548 {
549 reportException(methodCall, s);
550 throw s;
551 }
552 }
553
554 public Connection getConnection() throws SQLException
555 {
556 String methodCall = "getConnection()";
557 return (Connection) reportReturn(methodCall, connectionSpy);
558 }
559
560 public ResultSet getGeneratedKeys() throws SQLException
561 {
562 String methodCall = "getGeneratedKeys()";
563 try
564 {
565 ResultSet r = realStatement.getGeneratedKeys();
566 if (r == null)
567 {
568 return (ResultSet) reportReturn(methodCall, r);
569 }
570 else
571 {
572 return (ResultSet) reportReturn(methodCall, new ResultSetSpy(this, r));
573 }
574 }
575 catch (SQLException s)
576 {
577 reportException(methodCall, s);
578 throw s;
579 }
580 }
581
582 public void setEscapeProcessing(boolean enable) throws SQLException
583 {
584 String methodCall = "setEscapeProcessing(" + enable + ")";
585 try
586 {
587 realStatement.setEscapeProcessing(enable);
588 }
589 catch (SQLException s)
590 {
591 reportException(methodCall, s);
592 throw s;
593 }
594 reportReturn(methodCall);
595 }
596
597 public int getFetchDirection() throws SQLException
598 {
599 String methodCall = "getFetchDirection()";
600 try
601 {
602 return reportReturn(methodCall, realStatement.getFetchDirection());
603 }
604 catch (SQLException s)
605 {
606 reportException(methodCall, s);
607 throw s;
608 }
609 }
610
611 public void setQueryTimeout(int seconds) throws SQLException
612 {
613 String methodCall = "setQueryTimeout(" + seconds + ")";
614 try
615 {
616 realStatement.setQueryTimeout(seconds);
617 }
618 catch (SQLException s)
619 {
620 reportException(methodCall, s);
621 throw s;
622 }
623 reportReturn(methodCall);
624 }
625
626 public boolean getMoreResults(int current) throws SQLException
627 {
628 String methodCall = "getMoreResults(" + current + ")";
629
630 try
631 {
632 return reportReturn(methodCall, realStatement.getMoreResults(current));
633 }
634 catch (SQLException s)
635 {
636 reportException(methodCall, s);
637 throw s;
638 }
639 }
640
641 public ResultSet executeQuery(String sql) throws SQLException
642 {
643 String methodCall = "executeQuery(" + sql + ")";
644 reportStatementSql(sql, methodCall);
645 long tstart = System.currentTimeMillis();
646 try
647 {
648 ResultSet result = realStatement.executeQuery(sql);
649 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
650 ResultSetSpy r = new ResultSetSpy(this, result);
651 return (ResultSet) reportReturn(methodCall, r);
652 }
653 catch (SQLException s)
654 {
655 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
656 throw s;
657 }
658 }
659
660 public int getMaxFieldSize() throws SQLException
661 {
662 String methodCall = "getMaxFieldSize()";
663 try
664 {
665 return reportReturn(methodCall, realStatement.getMaxFieldSize());
666 }
667 catch (SQLException s)
668 {
669 reportException(methodCall, s);
670 throw s;
671 }
672 }
673
674 public int executeUpdate(String sql) throws SQLException
675 {
676 String methodCall = "executeUpdate(" + sql + ")";
677 reportStatementSql(sql, methodCall);
678 long tstart = System.currentTimeMillis();
679 try
680 {
681 int result = realStatement.executeUpdate(sql);
682 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
683 return reportReturn(methodCall, result);
684 }
685 catch (SQLException s)
686 {
687 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
688 throw s;
689 }
690 }
691
692 public void cancel() throws SQLException
693 {
694 String methodCall = "cancel()";
695 try
696 {
697 realStatement.cancel();
698 }
699 catch (SQLException s)
700 {
701 reportException(methodCall, s);
702 throw s;
703 }
704 reportReturn(methodCall);
705 }
706
707 public void setCursorName(String name) throws SQLException
708 {
709 String methodCall = "setCursorName(" + name + ")";
710 try
711 {
712 realStatement.setCursorName(name);
713 }
714 catch (SQLException s)
715 {
716 reportException(methodCall, s);
717 throw s;
718 }
719 reportReturn(methodCall);
720 }
721
722 public int getFetchSize() throws SQLException
723 {
724 String methodCall = "getFetchSize()";
725 try
726 {
727 return reportReturn(methodCall, realStatement.getFetchSize());
728 }
729 catch (SQLException s)
730 {
731 reportException(methodCall, s);
732 throw s;
733 }
734 }
735
736 public int getResultSetConcurrency() throws SQLException
737 {
738 String methodCall = "getResultSetConcurrency()";
739 try
740 {
741 return reportReturn(methodCall, realStatement.getResultSetConcurrency());
742 }
743 catch (SQLException s)
744 {
745 reportException(methodCall, s);
746 throw s;
747 }
748 }
749
750 public int getResultSetHoldability() throws SQLException
751 {
752 String methodCall = "getResultSetHoldability()";
753 try
754 {
755 return reportReturn(methodCall, realStatement.getResultSetHoldability());
756 }
757 catch (SQLException s)
758 {
759 reportException(methodCall, s);
760 throw s;
761 }
762 }
763
764 public boolean isClosed() throws SQLException {
765 String methodCall = "isClosed()";
766 try
767 {
768 return reportReturn(methodCall, realStatement.isClosed());
769 }
770 catch (SQLException s)
771 {
772 reportException(methodCall, s);
773 throw s;
774 }
775 }
776
777 public void setPoolable(boolean poolable) throws SQLException {
778 String methodCall = "setPoolable(" + poolable + ")";
779 try
780 {
781 realStatement.setPoolable(poolable);
782 }
783 catch (SQLException s)
784 {
785 reportException(methodCall, s);
786 throw s;
787 }
788 reportReturn(methodCall);
789 }
790
791 public boolean isPoolable() throws SQLException {
792 String methodCall = "isPoolable()";
793 try
794 {
795 return reportReturn(methodCall, realStatement.isPoolable());
796 }
797 catch (SQLException s)
798 {
799 reportException(methodCall, s);
800 throw s;
801 }
802 }
803
804 public void setMaxFieldSize(int max) throws SQLException
805 {
806 String methodCall = "setMaxFieldSize(" + max + ")";
807 try
808 {
809 realStatement.setMaxFieldSize(max);
810 }
811 catch (SQLException s)
812 {
813 reportException(methodCall, s);
814 throw s;
815 }
816 reportReturn(methodCall);
817 }
818
819 public boolean execute(String sql) throws SQLException
820 {
821 String methodCall = "execute(" + sql + ")";
822 reportStatementSql(sql, methodCall);
823 long tstart = System.currentTimeMillis();
824 try
825 {
826 boolean result = realStatement.execute(sql);
827 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
828 return reportReturn(methodCall, result);
829 }
830 catch (SQLException s)
831 {
832 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
833 throw s;
834 }
835 }
836
837 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
838 {
839 String methodCall = "executeUpdate(" + sql + ", " + autoGeneratedKeys + ")";
840 reportStatementSql(sql, methodCall);
841 long tstart = System.currentTimeMillis();
842 try
843 {
844 int result = realStatement.executeUpdate(sql, autoGeneratedKeys);
845 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
846 return reportReturn(methodCall, result);
847 }
848 catch (SQLException s)
849 {
850 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
851 throw s;
852 }
853 }
854
855 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
856 {
857 String methodCall = "execute(" + sql + ", " + autoGeneratedKeys + ")";
858 reportStatementSql(sql, methodCall);
859 long tstart = System.currentTimeMillis();
860 try
861 {
862 boolean result = realStatement.execute(sql, autoGeneratedKeys);
863 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
864 return reportReturn(methodCall, result);
865 }
866 catch (SQLException s)
867 {
868 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
869 throw s;
870 }
871 }
872
873 public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
874 {
875 String methodCall = "executeUpdate(" + sql + ", " + columnIndexes + ")";
876 reportStatementSql(sql, methodCall);
877 long tstart = System.currentTimeMillis();
878 try
879 {
880 int result = realStatement.executeUpdate(sql, columnIndexes);
881 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
882 return reportReturn(methodCall, result);
883 }
884 catch (SQLException s)
885 {
886 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
887 throw s;
888 }
889 }
890
891 public boolean execute(String sql, int[] columnIndexes) throws SQLException
892 {
893 String methodCall = "execute(" + sql + ", " + columnIndexes + ")";
894 reportStatementSql(sql, methodCall);
895 long tstart = System.currentTimeMillis();
896 try
897 {
898 boolean result = realStatement.execute(sql, columnIndexes);
899 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall);
900 return reportReturn(methodCall, result);
901 }
902 catch (SQLException s)
903 {
904 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart);
905 throw s;
906 }
907 }
908
909 public ResultSet getResultSet() throws SQLException
910 {
911 String methodCall = "getResultSet()";
912 try
913 {
914 ResultSet r = realStatement.getResultSet();
915 if (r == null)
916 {
917 return (ResultSet) reportReturn(methodCall, r);
918 }
919 else
920 {
921 return (ResultSet) reportReturn(methodCall, new ResultSetSpy(this, r));
922 }
923 }
924 catch (SQLException s)
925 {
926 reportException(methodCall, s);
927 throw s;
928 }
929 }
930
931 public int getMaxRows() throws SQLException
932 {
933 String methodCall = "getMaxRows()";
934 try
935 {
936 return reportReturn(methodCall, realStatement.getMaxRows());
937 }
938 catch (SQLException s)
939 {
940 reportException(methodCall, s);
941 throw s;
942 }
943 }
944
945 public void close() throws SQLException
946 {
947 String methodCall = "close()";
948 try
949 {
950 realStatement.close();
951 }
952 catch (SQLException s)
953 {
954 reportException(methodCall, s);
955 throw s;
956 }
957 reportReturn(methodCall);
958 }
959
960 public int getUpdateCount() throws SQLException
961 {
962 String methodCall = "getUpdateCount()";
963 try
964 {
965 return reportReturn(methodCall, realStatement.getUpdateCount());
966 }
967 catch (SQLException s)
968 {
969 reportException(methodCall, s);
970 throw s;
971 }
972 }
973
974 public <T> T unwrap(Class<T> iface) throws SQLException {
975 String methodCall = "unwrap(" + (iface==null?"null":iface.getName()) + ")";
976 try
977 {
978 //todo: double check this logic
979 return (T)reportReturn(methodCall, (iface != null && (iface == Connection.class || iface == Spy.class))?(T)this:realStatement.unwrap(iface));
980 }
981 catch (SQLException s)
982 {
983 reportException(methodCall,s);
984 throw s;
985 }
986 }
987
988 public boolean isWrapperFor(Class<?> iface) throws SQLException
989 {
990 String methodCall = "isWrapperFor(" + (iface==null?"null":iface.getName()) + ")";
991 try
992 {
993 return reportReturn(methodCall, (iface != null && (iface == Statement.class || iface == Spy.class)) ||
994 realStatement.isWrapperFor(iface));
995 }
996 catch (SQLException s)
997 {
998 reportException(methodCall,s);
999 throw s;
1000 }
1001 }
1002
1003 }