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