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.io.InputStream;
019    import java.io.Reader;
020    import java.math.BigDecimal;
021    import java.net.URL;
022    import java.sql.Array;
023    import java.sql.Blob;
024    import java.sql.Clob;
025    import java.sql.Date;
026    import java.sql.NClob;
027    import java.sql.ParameterMetaData;
028    import java.sql.PreparedStatement;
029    import java.sql.Ref;
030    import java.sql.RowId;
031    import java.sql.ResultSet;
032    import java.sql.ResultSetMetaData;
033    import java.sql.SQLException;
034    import java.sql.SQLXML;
035    import java.sql.Statement;
036    import java.sql.Time;
037    import java.sql.Timestamp;
038    import java.util.ArrayList;
039    import java.util.Calendar;
040    import java.util.List;
041    
042    /**
043     * Wraps a PreparedStatement and reports method calls, returns and exceptions.
044     *
045     * @author Arthur Blake
046     */
047    public class PreparedStatementSpy extends StatementSpy implements PreparedStatement
048    {
049    
050      /**
051       * holds list of bind variables for tracing
052       */
053      protected final List argTrace = new ArrayList();
054    
055      // a way to turn on and off type help...
056      // todo:  make this a configurable parameter
057      // todo, debug arrays and streams in a more useful manner.... if possible
058      private static final boolean showTypeHelp = false;
059    
060      /**
061       * Store an argument (bind variable) into the argTrace list (above) for later dumping.
062       *
063       * @param i          index of argument being set.
064       * @param typeHelper optional additional info about the type that is being set in the arg
065       * @param arg        argument being bound.
066       */
067      protected void argTraceSet(int i, String typeHelper, Object arg)
068      {
069        String tracedArg = rdbmsSpecifics.formatParameterObject(arg);
070    
071        i--;  // make the index 0 based
072        synchronized (argTrace)
073        {
074          // if an object is being inserted out of sequence, fill up missing values with null...
075          while (i >= argTrace.size())
076          {
077            argTrace.add(argTrace.size(), null);
078          }
079          if (!showTypeHelp || typeHelper == null)
080          {
081            argTrace.set(i, tracedArg);
082          }
083          else
084          {
085            argTrace.set(i, typeHelper + tracedArg);
086          }
087        }
088      }
089    
090      private String sql;
091    
092      protected String dumpedSql()
093      {
094        StringBuffer dumpSql = new StringBuffer();
095        int lastPos = 0;
096        int Qpos = sql.indexOf('?', lastPos);  // find position of first question mark
097        int argIdx = 0;
098        String arg;
099    
100        while (Qpos != -1)
101        {
102          // get stored argument
103          synchronized (argTrace)
104          {
105            try
106            {
107              arg = (String) argTrace.get(argIdx);
108            }
109            catch (IndexOutOfBoundsException e)
110            {
111              arg = "?";
112            }
113          }
114          if (arg == null)
115          {
116            arg = "?";
117          }
118    
119          argIdx++;
120    
121          dumpSql.append(sql.substring(lastPos, Qpos));  // dump segment of sql up to question mark.
122          lastPos = Qpos + 1;
123          Qpos = sql.indexOf('?', lastPos);
124          dumpSql.append(arg);
125        }
126        if (lastPos < sql.length())
127        {
128          dumpSql.append(sql.substring(lastPos, sql.length()));  // dump last segment
129        }
130    
131        return dumpSql.toString();
132      }
133    
134      protected void reportAllReturns(String methodCall, String msg)
135      {
136        log.methodReturned(this, methodCall, msg);
137      }
138    
139      /**
140       * The real PreparedStatement that this PreparedStatementSpy wraps.
141       */
142      protected PreparedStatement realPreparedStatement;
143    
144      /**
145       * RdbmsSpecifics for formatting SQL for the given RDBMS.
146       */
147      protected RdbmsSpecifics rdbmsSpecifics;
148    
149      /**
150       * Create a PreparedStatementSpy (JDBC 4 version) for logging activity of another PreparedStatement.
151       *
152       * @param sql                   SQL for the prepared statement that is being spied upon.
153       * @param connectionSpy         ConnectionSpy that was called to produce this PreparedStatement.
154       * @param realPreparedStatement The actual PreparedStatement that is being spied upon.
155       */
156      public PreparedStatementSpy(String sql, ConnectionSpy connectionSpy, PreparedStatement realPreparedStatement)
157      {
158        super(connectionSpy, realPreparedStatement);  // does null check for us
159        this.sql = sql;
160        this.realPreparedStatement = realPreparedStatement;
161        rdbmsSpecifics = connectionSpy.getRdbmsSpecifics();
162      }
163    
164      public String getClassType()
165      {
166        return "PreparedStatement";
167      }
168    
169      // forwarding methods
170    
171      public void setTime(int parameterIndex, Time x) throws SQLException
172      {
173        String methodCall = "setTime(" + parameterIndex + ", " + x + ")";
174        argTraceSet(parameterIndex, "(Time)", x);
175        try
176        {
177          realPreparedStatement.setTime(parameterIndex, x);
178        }
179        catch (SQLException s)
180        {
181          reportException(methodCall, s);
182          throw s;
183        }
184        reportReturn(methodCall);
185      }
186    
187      public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException
188      {
189        String methodCall = "setTime(" + parameterIndex + ", " + x + ", " + cal + ")";
190        argTraceSet(parameterIndex, "(Time)", x);
191        try
192        {
193          realPreparedStatement.setTime(parameterIndex, x, cal);
194        }
195        catch (SQLException s)
196        {
197          reportException(methodCall, s);
198          throw s;
199        }
200        reportReturn(methodCall);
201      }
202    
203      public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException
204      {
205        String methodCall = "setCharacterStream(" + parameterIndex + ", " + reader + ", " + length + ")";
206        argTraceSet(parameterIndex, "(Reader)", "<Reader of length " + length + ">");
207        try
208        {
209          realPreparedStatement.setCharacterStream(parameterIndex, reader, length);
210        }
211        catch (SQLException s)
212        {
213          reportException(methodCall, s);
214          throw s;
215        }
216        reportReturn(methodCall);
217      }
218    
219      public void setNull(int parameterIndex, int sqlType) throws SQLException
220      {
221        String methodCall = "setNull(" + parameterIndex + ", " + sqlType + ")";
222        argTraceSet(parameterIndex, null, null);
223        try
224        {
225          realPreparedStatement.setNull(parameterIndex, sqlType);
226        }
227        catch (SQLException s)
228        {
229          reportException(methodCall, s);
230          throw s;
231        }
232        reportReturn(methodCall);
233      }
234    
235      public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException
236      {
237        String methodCall = "setNull(" + paramIndex + ", " + sqlType + ", " + typeName + ")";
238        argTraceSet(paramIndex, null, null);
239        try
240        {
241          realPreparedStatement.setNull(paramIndex, sqlType, typeName);
242        }
243        catch (SQLException s)
244        {
245          reportException(methodCall, s);
246          throw s;
247        }
248        reportReturn(methodCall);
249      }
250    
251      public void setRef(int i, Ref x) throws SQLException
252      {
253        String methodCall = "setRef(" + i + ", " + x + ")";
254        argTraceSet(i, "(Ref)", x);
255        try
256        {
257          realPreparedStatement.setRef(i, x);
258        }
259        catch (SQLException s)
260        {
261          reportException(methodCall, s);
262          throw s;
263        }
264        reportReturn(methodCall);
265      }
266    
267      public void setBoolean(int parameterIndex, boolean x) throws SQLException
268      {
269        String methodCall = "setBoolean(" + parameterIndex + ", " + x + ")";
270        argTraceSet(parameterIndex, "(boolean)", x?Boolean.TRUE:Boolean.FALSE);
271        try
272        {
273          realPreparedStatement.setBoolean(parameterIndex, x);
274        }
275        catch (SQLException s)
276        {
277          reportException(methodCall, s);
278          throw s;
279        }
280        reportReturn(methodCall);
281      }
282    
283      public void setBlob(int i, Blob x) throws SQLException
284      {
285        String methodCall = "setBlob(" + i + ", " + x + ")";
286        argTraceSet(i, "(Blob)", "<Blob of size " + x.length() + ">");
287        try
288        {
289          realPreparedStatement.setBlob(i, x);
290        }
291        catch (SQLException s)
292        {
293          reportException(methodCall, s);
294          throw s;
295        }
296        reportReturn(methodCall);
297      }
298    
299      public void setClob(int i, Clob x) throws SQLException
300      {
301        String methodCall = "setClob(" + i + ", " + x + ")";
302        argTraceSet(i, "(Clob)", "<Clob of size " + x.length() + ">");
303        try
304        {
305          realPreparedStatement.setClob(i, x);
306        }
307        catch (SQLException s)
308        {
309          reportException(methodCall, s);
310          throw s;
311        }
312        reportReturn(methodCall);
313      }
314    
315      public void setArray(int i, Array x) throws SQLException
316      {
317        String methodCall = "setArray(" + i + ", " + x + ")";
318        argTraceSet(i, "(Array)", "<Array>");
319        try
320        {
321          realPreparedStatement.setArray(i, x);
322        }
323        catch (SQLException s)
324        {
325          reportException(methodCall, s);
326          throw s;
327        }
328        reportReturn(methodCall);
329      }
330    
331      public void setByte(int parameterIndex, byte x) throws SQLException
332      {
333        String methodCall = "setByte(" + parameterIndex + ", " + x + ")";
334        argTraceSet(parameterIndex, "(byte)", new Byte(x));
335        try
336        {
337          realPreparedStatement.setByte(parameterIndex, x);
338        }
339        catch (SQLException s)
340        {
341          reportException(methodCall, s);
342          throw s;
343        }
344        reportReturn(methodCall);
345      }
346    
347      /**
348       * @deprecated
349       */
350      public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
351      {
352        String methodCall = "setUnicodeStream(" + parameterIndex + ", " + x + ", " + length + ")";
353        argTraceSet(parameterIndex, "(Unicode InputStream)", "<Unicode InputStream of length " + length + ">");
354        try
355        {
356          realPreparedStatement.setUnicodeStream(parameterIndex, x, length);
357        }
358        catch (SQLException s)
359        {
360          reportException(methodCall, s);
361          throw s;
362        }
363        reportReturn(methodCall);
364      }
365    
366      public void setShort(int parameterIndex, short x) throws SQLException
367      {
368        String methodCall = "setShort(" + parameterIndex + ", " + x + ")";
369        argTraceSet(parameterIndex, "(short)", new Short(x));
370        try
371        {
372          realPreparedStatement.setShort(parameterIndex, x);
373        }
374        catch (SQLException s)
375        {
376          reportException(methodCall, s);
377          throw s;
378        }
379        reportReturn(methodCall);
380      }
381    
382      public boolean execute() throws SQLException
383      {
384        String methodCall = "execute()";
385        String dumpedSql = dumpedSql();
386        reportSql(dumpedSql, methodCall);
387        long tstart = System.currentTimeMillis();
388        try
389        {
390          boolean result = realPreparedStatement.execute();
391          reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
392          return reportReturn(methodCall, result);
393        }
394        catch (SQLException s)
395        {
396          reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
397          throw s;
398        }
399      }
400    
401      public void setInt(int parameterIndex, int x) throws SQLException
402      {
403        String methodCall = "setInt(" + parameterIndex + ", " + x + ")";
404        argTraceSet(parameterIndex, "(int)", new Integer(x));
405        try
406        {
407          realPreparedStatement.setInt(parameterIndex, x);
408        }
409        catch (SQLException s)
410        {
411          reportException(methodCall, s);
412          throw s;
413        }
414        reportReturn(methodCall);
415      }
416    
417      public void setLong(int parameterIndex, long x) throws SQLException
418      {
419        String methodCall = "setLong(" + parameterIndex + ", " + x + ")";
420        argTraceSet(parameterIndex, "(long)", new Long(x));
421        try
422        {
423          realPreparedStatement.setLong(parameterIndex, x);
424        }
425        catch (SQLException s)
426        {
427          reportException(methodCall, s);
428          throw s;
429        }
430        reportReturn(methodCall);
431      }
432    
433      public void setFloat(int parameterIndex, float x) throws SQLException
434      {
435        String methodCall = "setFloat(" + parameterIndex + ", " + x + ")";
436        argTraceSet(parameterIndex, "(float)", new Float(x));
437        try
438        {
439          realPreparedStatement.setFloat(parameterIndex, x);
440        }
441        catch (SQLException s)
442        {
443          reportException(methodCall, s);
444          throw s;
445        }
446        reportReturn(methodCall);
447      }
448    
449      public void setDouble(int parameterIndex, double x) throws SQLException
450      {
451        String methodCall = "setDouble(" + parameterIndex + ", " + x + ")";
452        argTraceSet(parameterIndex, "(double)", new Double(x));
453        try
454        {
455          realPreparedStatement.setDouble(parameterIndex, x);
456        }
457        catch (SQLException s)
458        {
459          reportException(methodCall, s);
460          throw s;
461        }
462        reportReturn(methodCall);
463      }
464    
465      public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
466      {
467        String methodCall = "setBigDecimal(" + parameterIndex + ", " + x + ")";
468        argTraceSet(parameterIndex, "(BigDecimal)", x);
469        try
470        {
471          realPreparedStatement.setBigDecimal(parameterIndex, x);
472        }
473        catch (SQLException s)
474        {
475          reportException(methodCall, s);
476          throw s;
477        }
478        reportReturn(methodCall);
479      }
480    
481      public void setURL(int parameterIndex, URL x) throws SQLException
482      {
483        String methodCall = "setURL(" + parameterIndex + ", " + x + ")";
484        argTraceSet(parameterIndex, "(URL)", x);
485    
486        try
487        {
488          realPreparedStatement.setURL(parameterIndex, x);
489        }
490        catch (SQLException s)
491        {
492          reportException(methodCall, s);
493          throw s;
494        }
495        reportReturn(methodCall);
496      }
497    
498      public void setString(int parameterIndex, String x) throws SQLException
499      {
500        String methodCall = "setString(" + parameterIndex + ", \"" + x + "\")";
501        argTraceSet(parameterIndex, "(String)", x);
502    
503        try
504        {
505          realPreparedStatement.setString(parameterIndex, x);
506        }
507        catch (SQLException s)
508        {
509          reportException(methodCall, s);
510          throw s;
511        }
512        reportReturn(methodCall);
513      }
514    
515      public void setBytes(int parameterIndex, byte[] x) throws SQLException
516      {
517        //todo: dump array?
518        String methodCall = "setBytes(" + parameterIndex + ", " + x + ")";
519        argTraceSet(parameterIndex, "(byte[])", "<byte[]>");
520        try
521        {
522          realPreparedStatement.setBytes(parameterIndex, x);
523        }
524        catch (SQLException s)
525        {
526          reportException(methodCall, s);
527          throw s;
528        }
529        reportReturn(methodCall);
530      }
531    
532      public void setDate(int parameterIndex, Date x) throws SQLException
533      {
534        String methodCall = "setDate(" + parameterIndex + ", " + x + ")";
535        argTraceSet(parameterIndex, "(Date)", x);
536        try
537        {
538          realPreparedStatement.setDate(parameterIndex, x);
539        }
540        catch (SQLException s)
541        {
542          reportException(methodCall, s);
543          throw s;
544        }
545        reportReturn(methodCall);
546      }
547    
548      public ParameterMetaData getParameterMetaData() throws SQLException
549      {
550        String methodCall = "getParameterMetaData()";
551        try
552        {
553          return (ParameterMetaData) reportReturn(methodCall, realPreparedStatement.getParameterMetaData());
554        }
555        catch (SQLException s)
556        {
557          reportException(methodCall, s);
558          throw s;
559        }
560      }
561    
562      public void setRowId(int parameterIndex, RowId x) throws SQLException {
563        String methodCall = "setRowId(" + parameterIndex + ", " + x + ")";
564        argTraceSet(parameterIndex, "(RowId)", x);
565        try
566        {
567          realPreparedStatement.setRowId(parameterIndex, x);
568        }
569        catch (SQLException s)
570        {
571          reportException(methodCall, s);
572          throw s;
573        }
574        reportReturn(methodCall);
575      }
576    
577      public void setNString(int parameterIndex, String value) throws SQLException {
578        String methodCall = "setNString(" + parameterIndex + ", " + value + ")";
579        argTraceSet(parameterIndex, "(String)", value);
580        try
581        {
582          realPreparedStatement.setNString(parameterIndex, value);
583        }
584        catch (SQLException s)
585        {
586          reportException(methodCall, s);
587          throw s;
588        }
589        reportReturn(methodCall);
590      }
591    
592      public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
593        String methodCall = "setNCharacterStream(" + parameterIndex + ", " + value + ", " + length + ")";
594        argTraceSet(parameterIndex, "(Reader)", "<Reader of length " + length + ">");
595        try
596        {
597          realPreparedStatement.setNCharacterStream(parameterIndex, value, length);
598        }
599        catch (SQLException s)
600        {
601          reportException(methodCall, s);
602          throw s;
603        }
604        reportReturn(methodCall);
605      }
606    
607      public void setNClob(int parameterIndex, NClob value) throws SQLException {
608        String methodCall = "setNClob(" + parameterIndex + ", " + value + ")";
609        argTraceSet(parameterIndex, "(NClob)", "<NClob>");
610        try
611        {
612          realPreparedStatement.setNClob(parameterIndex, value);
613        }
614        catch (SQLException s)
615        {
616          reportException(methodCall, s);
617          throw s;
618        }
619        reportReturn(methodCall);
620      }
621    
622      public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
623        String methodCall = "setClob(" + parameterIndex + ", " + reader + ", " + length + ")";
624        argTraceSet(parameterIndex, "(Reader)", "<Reader of length " + length + ">");
625        try
626        {
627          realPreparedStatement.setClob(parameterIndex, reader, length);
628        }
629        catch (SQLException s)
630        {
631          reportException(methodCall, s);
632          throw s;
633        }
634        reportReturn(methodCall);
635      }
636    
637      public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
638        String methodCall = "setBlob(" + parameterIndex + ", " + inputStream + ", " + length + ")";
639        argTraceSet(parameterIndex, "(InputStream)", "<InputStream of length " + length + ">");
640        try
641        {
642          realPreparedStatement.setBlob(parameterIndex, inputStream, length);
643        }
644        catch (SQLException s)
645        {
646          reportException(methodCall, s);
647          throw s;
648        }
649        reportReturn(methodCall);
650      }
651    
652      public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
653        String methodCall = "setNClob(" + parameterIndex + ", " + reader + ", " + length + ")";
654        argTraceSet(parameterIndex, "(Reader)", "<Reader of length " + length + ">");
655        try
656        {
657          realPreparedStatement.setNClob(parameterIndex, reader, length);
658        }
659        catch (SQLException s)
660        {
661          reportException(methodCall, s);
662          throw s;
663        }
664        reportReturn(methodCall);
665      }
666    
667      public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
668        String methodCall = "setSQLXML(" + parameterIndex + ", " + xmlObject + ")";
669        argTraceSet(parameterIndex, "(SQLXML)", xmlObject);
670        try
671        {
672          realPreparedStatement.setSQLXML(parameterIndex, xmlObject);
673        }
674        catch (SQLException s)
675        {
676          reportException(methodCall, s);
677          throw s;
678        }
679        reportReturn(methodCall);
680      }
681    
682      public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException
683      {
684        String methodCall = "setDate(" + parameterIndex + ", " + x + ", " + cal + ")";
685        argTraceSet(parameterIndex, "(Date)", x);
686    
687        try
688        {
689          realPreparedStatement.setDate(parameterIndex, x, cal);
690        }
691        catch (SQLException s)
692        {
693          reportException(methodCall, s);
694          throw s;
695        }
696        reportReturn(methodCall);
697      }
698    
699      public ResultSet executeQuery() throws SQLException
700      {
701        String methodCall = "executeQuery()";
702        String dumpedSql = dumpedSql();
703        reportSql(dumpedSql, methodCall);
704        long tstart = System.currentTimeMillis();
705        try
706        {
707          ResultSet r = realPreparedStatement.executeQuery();
708          reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
709          ResultSetSpy rsp = new ResultSetSpy(this, r);
710          return (ResultSet) reportReturn(methodCall, rsp);
711        }
712        catch (SQLException s)
713        {
714          reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
715          throw s;
716        }
717      }
718    
719      private String getTypeHelp(Object x)
720      {
721        if (x==null)
722        {
723          return "(null)";
724        }
725        else
726        {
727          return "(" + x.getClass().getName() + ")";
728        }
729      }
730    
731      public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
732      {
733        String methodCall = "setObject(" + parameterIndex + ", " + x + ", " + targetSqlType + ", " + scale + ")";
734        argTraceSet(parameterIndex, getTypeHelp(x), x);
735    
736        try
737        {
738          realPreparedStatement.setObject(parameterIndex, x, targetSqlType, scale);
739        }
740        catch (SQLException s)
741        {
742          reportException(methodCall, s);
743          throw s;
744        }
745        reportReturn(methodCall);
746      }
747    
748      /**
749       * Sets the designated parameter to the given input stream, which will have
750       * the specified number of bytes.
751       * When a very large ASCII value is input to a <code>LONGVARCHAR</code>
752       * parameter, it may be more practical to send it via a
753       * <code>java.io.InputStream</code>. Data will be read from the stream
754       * as needed until end-of-file is reached.  The JDBC driver will
755       * do any necessary conversion from ASCII to the database char format.
756       * <p/>
757       * <P><B>Note:</B> This stream object can either be a standard
758       * Java stream object or your own subclass that implements the
759       * standard interface.
760       *
761       * @param parameterIndex the first parameter is 1, the second is 2, ...
762       * @param x              the Java input stream that contains the ASCII parameter value
763       * @param length         the number of bytes in the stream
764       * @throws java.sql.SQLException if parameterIndex does not correspond to a parameter
765       *                               marker in the SQL statement; if a database access error occurs or
766       *                               this method is called on a closed <code>PreparedStatement</code>
767       * @since 1.6
768       */
769      public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
770        String methodCall = "setAsciiStream(" + parameterIndex + ", " + x + ", " + length + ")";
771        argTraceSet(parameterIndex, "(Ascii InputStream)", "<Ascii InputStream of length " + length + ">");
772        try
773        {
774          realPreparedStatement.setAsciiStream(parameterIndex, x, length);
775        }
776        catch (SQLException s)
777        {
778          reportException(methodCall, s);
779          throw s;
780        }
781        reportReturn(methodCall);
782      }
783    
784      public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
785        String methodCall = "setBinaryStream(" + parameterIndex + ", " + x + ", " + length + ")";
786        argTraceSet(parameterIndex, "(Binary InputStream)", "<Binary InputStream of length " + length + ">");
787        try
788        {
789          realPreparedStatement.setBinaryStream(parameterIndex, x, length);
790        }
791        catch (SQLException s)
792        {
793          reportException(methodCall, s);
794          throw s;
795        }
796        reportReturn(methodCall);
797      }
798    
799      public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
800        String methodCall = "setCharacterStream(" + parameterIndex + ", " + reader + ", " + length + ")";
801        argTraceSet(parameterIndex, "(Reader)", "<Reader of length " + length + ">");
802        try
803        {
804          realPreparedStatement.setCharacterStream(parameterIndex, reader, length);
805        }
806        catch (SQLException s)
807        {
808          reportException(methodCall, s);
809          throw s;
810        }
811        reportReturn(methodCall);
812    
813      }
814    
815      public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
816        String methodCall = "setAsciiStream(" + parameterIndex + ", " + x + ")";
817        argTraceSet(parameterIndex, "(Ascii InputStream)", "<Ascii InputStream>");
818        try
819        {
820          realPreparedStatement.setAsciiStream(parameterIndex, x);
821        }
822        catch (SQLException s)
823        {
824          reportException(methodCall, s);
825          throw s;
826        }
827        reportReturn(methodCall);
828      }
829    
830      public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
831        String methodCall = "setBinaryStream(" + parameterIndex + ", " + x + ")";
832        argTraceSet(parameterIndex, "(Binary InputStream)", "<Binary InputStream>");
833        try
834        {
835          realPreparedStatement.setBinaryStream(parameterIndex, x);
836        }
837        catch (SQLException s)
838        {
839          reportException(methodCall, s);
840          throw s;
841        }
842        reportReturn(methodCall);
843    
844      }
845    
846      public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
847        String methodCall = "setCharacterStream(" + parameterIndex + ", " + reader + ")";
848        argTraceSet(parameterIndex, "(Reader)", "<Reader>");
849        try
850        {
851          realPreparedStatement.setCharacterStream(parameterIndex, reader);
852        }
853        catch (SQLException s)
854        {
855          reportException(methodCall, s);
856          throw s;
857        }
858        reportReturn(methodCall);
859      }
860    
861      public void setNCharacterStream(int parameterIndex, Reader reader) throws SQLException {
862        String methodCall = "setNCharacterStream(" + parameterIndex + ", " + reader + ")";
863        argTraceSet(parameterIndex, "(Reader)", "<Reader>");
864        try
865        {
866          realPreparedStatement.setNCharacterStream(parameterIndex, reader);
867        }
868        catch (SQLException s)
869        {
870          reportException(methodCall, s);
871          throw s;
872        }
873        reportReturn(methodCall);
874      }
875    
876      public void setClob(int parameterIndex, Reader reader) throws SQLException {
877        String methodCall = "setClob(" + parameterIndex + ", " + reader + ")";
878        argTraceSet(parameterIndex, "(Reader)", "<Reader>");
879        try
880        {
881          realPreparedStatement.setClob(parameterIndex, reader);
882        }
883        catch (SQLException s)
884        {
885          reportException(methodCall, s);
886          throw s;
887        }
888        reportReturn(methodCall);
889      }
890    
891      public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
892        String methodCall = "setBlob(" + parameterIndex + ", " + inputStream + ")";
893        argTraceSet(parameterIndex, "(InputStream)", "<InputStream>");
894        try
895        {
896          realPreparedStatement.setBlob(parameterIndex, inputStream);
897        }
898        catch (SQLException s)
899        {
900          reportException(methodCall, s);
901          throw s;
902        }
903        reportReturn(methodCall);
904      }
905    
906      public void setNClob(int parameterIndex, Reader reader) throws SQLException {
907        String methodCall = "setNClob(" + parameterIndex + ", " + reader + ")";
908        argTraceSet(parameterIndex, "(Reader)", "<Reader>");
909        try
910        {
911          realPreparedStatement.setNClob(parameterIndex, reader);
912        }
913        catch (SQLException s)
914        {
915          reportException(methodCall, s);
916          throw s;
917        }
918        reportReturn(methodCall);
919    
920      }
921    
922      public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
923      {
924        String methodCall = "setObject(" + parameterIndex + ", " + x + ", " + targetSqlType + ")";
925        argTraceSet(parameterIndex, getTypeHelp(x), x);
926        try
927        {
928          realPreparedStatement.setObject(parameterIndex, x, targetSqlType);
929        }
930        catch (SQLException s)
931        {
932          reportException(methodCall, s);
933          throw s;
934        }
935        reportReturn(methodCall);
936      }
937    
938      public void setObject(int parameterIndex, Object x) throws SQLException
939      {
940        String methodCall = "setObject(" + parameterIndex + ", " + x + ")";
941        argTraceSet(parameterIndex, getTypeHelp(x), x);
942        try
943        {
944          realPreparedStatement.setObject(parameterIndex, x);
945        }
946        catch (SQLException s)
947        {
948          reportException(methodCall, s);
949          throw s;
950        }
951        reportReturn(methodCall);
952      }
953    
954      public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
955      {
956        String methodCall = "setTimestamp(" + parameterIndex + ", " + x + ")";
957        argTraceSet(parameterIndex, "(Date)", x);
958        try
959        {
960          realPreparedStatement.setTimestamp(parameterIndex, x);
961        }
962        catch (SQLException s)
963        {
964          reportException(methodCall, s);
965          throw s;
966        }
967        reportReturn(methodCall);
968      }
969    
970      public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
971      {
972        String methodCall = "setTimestamp(" + parameterIndex + ", " + x + ", " + cal + ")";
973        argTraceSet(parameterIndex, "(Timestamp)", x);
974        try
975        {
976          realPreparedStatement.setTimestamp(parameterIndex, x, cal);
977        }
978        catch (SQLException s)
979        {
980          reportException(methodCall, s);
981          throw s;
982        }
983        reportReturn(methodCall);
984      }
985    
986      public int executeUpdate() throws SQLException
987      {
988        String methodCall = "executeUpdate()";
989        String dumpedSql = dumpedSql();
990        reportSql(dumpedSql, methodCall);
991        long tstart = System.currentTimeMillis();
992        try
993        {
994          int result = realPreparedStatement.executeUpdate();
995          reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
996          return reportReturn(methodCall, result);
997        }
998        catch (SQLException s)
999        {
1000          reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
1001          throw s;
1002        }
1003      }
1004    
1005      public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
1006      {
1007        String methodCall = "setAsciiStream(" + parameterIndex + ", " + x + ", " + length + ")";
1008        argTraceSet(parameterIndex, "(Ascii InputStream)", "<Ascii InputStream of length " + length + ">");
1009        try
1010        {
1011          realPreparedStatement.setAsciiStream(parameterIndex, x, length);
1012        }
1013        catch (SQLException s)
1014        {
1015          reportException(methodCall, s);
1016          throw s;
1017        }
1018        reportReturn(methodCall);
1019      }
1020    
1021      public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
1022      {
1023        String methodCall = "setBinaryStream(" + parameterIndex + ", " + x + ", " + length + ")";
1024        argTraceSet(parameterIndex, "(Binary InputStream)", "<Binary InputStream of length " + length + ">");
1025        try
1026        {
1027          realPreparedStatement.setBinaryStream(parameterIndex, x, length);
1028        }
1029        catch (SQLException s)
1030        {
1031          reportException(methodCall, s);
1032          throw s;
1033        }
1034        reportReturn(methodCall);
1035      }
1036    
1037      public void clearParameters() throws SQLException
1038      {
1039        String methodCall = "clearParameters()";
1040    
1041        synchronized (argTrace)
1042        {
1043          argTrace.clear();
1044        }
1045    
1046        try
1047        {
1048          realPreparedStatement.clearParameters();
1049        }
1050        catch (SQLException s)
1051        {
1052          reportException(methodCall, s);
1053          throw s;
1054        }
1055        reportReturn(methodCall);
1056      }
1057    
1058      public ResultSetMetaData getMetaData() throws SQLException
1059      {
1060        String methodCall = "getMetaData()";
1061        try
1062        {
1063          return (ResultSetMetaData) reportReturn(methodCall, realPreparedStatement.getMetaData());
1064        }
1065        catch (SQLException s)
1066        {
1067          reportException(methodCall, s);
1068          throw s;
1069        }
1070      }
1071    
1072      public void addBatch() throws SQLException
1073      {
1074        String methodCall = "addBatch()";
1075        currentBatch.add(dumpedSql());
1076        try
1077        {
1078          realPreparedStatement.addBatch();
1079        }
1080        catch (SQLException s)
1081        {
1082          reportException(methodCall, s);
1083          throw s;
1084        }
1085        reportReturn(methodCall);
1086      }
1087    
1088      public <T> T unwrap(Class<T> iface) throws SQLException {
1089        String methodCall = "unwrap(" + (iface==null?"null":iface.getName()) + ")";
1090        try
1091        {
1092          //todo: double check this logic
1093          //NOTE: could call super.isWrapperFor to simplify this logic, but it would result in extra log output
1094          //because the super classes would be invoked, thus executing their logging methods too...
1095          return (T)reportReturn(methodCall,
1096            (iface != null && (iface==PreparedStatement.class||iface==Statement.class||iface==Spy.class))?
1097              (T)this:
1098              realPreparedStatement.unwrap(iface));
1099        }
1100        catch (SQLException s)
1101        {
1102          reportException(methodCall,s);
1103          throw s;
1104        }
1105      }
1106    
1107      public boolean isWrapperFor(Class<?> iface) throws SQLException
1108      {
1109        String methodCall = "isWrapperFor(" + (iface==null?"null":iface.getName()) + ")";
1110        try
1111        {
1112          //NOTE: could call super.isWrapperFor to simplify this logic, but it would result in extra log output
1113          //when the super classes would be invoked..
1114          return reportReturn(methodCall,
1115            (iface != null && (iface==PreparedStatement.class||iface==Statement.class||iface==Spy.class)) ||
1116            realPreparedStatement.isWrapperFor(iface));
1117        }
1118        catch (SQLException s)
1119        {
1120          reportException(methodCall,s);
1121          throw s;
1122        }
1123      }
1124    
1125    }