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