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