    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.ParameterMetaData;
    027    import java.sql.PreparedStatement;
    028    import java.sql.Ref;
    029    import java.sql.ResultSet;
    030    import java.sql.ResultSetMetaData;
    031    import java.sql.SQLException;
    032    import java.sql.Time;
    033    import java.sql.Timestamp;
    034    import java.util.ArrayList;
    035    import java.util.Calendar;
    036    import java.util.StringTokenizer;
    037    import java.util.List;
    038
    039    /**
    040     * Wraps a PreparedStatement and reports method calls, returns and exceptions.
    041     *
    042     * @author Arthur Blake
    043     */
    044    public class PreparedStatementSpy extends StatementSpy implements PreparedStatement
    045    {
    046
    047      private final SpyLogDelegator log;
    048
    049      /**
    050       * holds list of bind variables for tracing
    051       */
    052      protected final List argTrace = new ArrayList();
    053
    054      // a way to turn on and off type help...
    055      // todo:  make this a configurable parameter
    056      // todo, debug arrays and streams in a more useful manner.... if possible
    057      private static final boolean showTypeHelp = false;
    058
    059      /**
    060       * Store an argument (bind variable) into the argTrace list (above) for later dumping.
    061       *
    062       * @param i          index of argument being set.
    063       * @param typeHelper optional additional info about the type that is being set in the arg
    064       * @param arg        argument being bound.
    065       */
    066      protected void argTraceSet(int i, String typeHelper, Object arg)
    067      {
    068        i--;  // make the index 0 based
    069        synchronized (argTrace)
    070        {
    071          // if an object is being inserted out of sequence, fill up missing values with null...
    072          while (i >= argTrace.size())
    073          {
    074            argTrace.add(argTrace.size(), null);
    075          }
    076          if (!showTypeHelp || typeHelper == null)
    077          {
    078            typeHelper = "";
    079          }
    080          if (arg != null)
    081          {
    082            argTrace.set(i, typeHelper + arg.toString());
    083          }
    084          else
    085          {
    086            argTrace.set(i, typeHelper + "null");
    087          }
    088        }
    089      }
    090
    091      private String sql;
    092
    093      protected String dumpedSql()
    094      {
    095        StringBuffer dumpSql = new StringBuffer();
    096        int lastPos = 0;
    097        int Qpos = sql.indexOf('?', lastPos);  // find position of first question mark
    098        int argIdx = 0;
    099        String arg;
    100
    101        while (Qpos != -1)
    102        {
    103          // get stored argument
    104          synchronized (argTrace)
    105          {
    106            try
    107            {
    108              arg = (String) argTrace.get(argIdx);
    109            }
    110            catch (IndexOutOfBoundsException e)
    111            {
    112              arg = "?";
    113            }
    114          }
    115          if (arg == null)
    116          {
    117            arg = "?";
    118          }
    119
    120          argIdx++;
    121
    122          dumpSql.append(sql.substring(lastPos, Qpos));  // dump segment of sql up to question mark.
    123          lastPos = Qpos + 1;
    124          Qpos = sql.indexOf('?', lastPos);
    125          dumpSql.append(arg);
    126        }
    127        if (lastPos < sql.length())
    128        {
    129          dumpSql.append(sql.substring(lastPos, sql.length()));  // dump last segment
    130        }
    131
    132        // insert line breaks into sql to make it more readable
    133        StringBuffer output = new StringBuffer();
    134        StringTokenizer st = new StringTokenizer(dumpSql.toString());
    135
    136        String token;
    137        int linelength = 0;
    138
    139        while (st.hasMoreElements())
    140        {
    141          token = (String) st.nextElement();
    142
    143          output.append(token);
    144          linelength += token.length();
    145          output.append(" ");
    146          linelength++;
    147          if (linelength > 90)
    148          {
    149            output.append("\n");
    150            linelength = 0;
    151          }
    152        }
    153
    154        return output.toString();
    155      }
    156
    157      protected void reportAllReturns(String methodCall, String msg)
    158      {
    159        log.methodReturned(this, methodCall, msg);
    160      }
    161
    162      /**
    163       * The real PreparedStatement that this PreparedStatementSpy wraps.
    164       */
    165      protected PreparedStatement realPreparedStatement;
    166
    167      /**
    168       * RdbmsSpecifics for formatting SQL for the given RDBMS.
    169       */
    170      protected RdbmsSpecifics rdbmsSpecifics;
    171
    172      /**
    173       * Create a prepared statement spy for logging activity of another PreparedStatement.
    174       *
    175       * @param sql                   SQL for the prepared statement that is being spied upon.
    176       * @param connectionSpy         ConnectionSpy that was called to produce this PreparedStatement.
    177       * @param realPreparedStatement The actual PreparedStatement that is being spied upon.
    178       */
    179      public PreparedStatementSpy(String sql, ConnectionSpy connectionSpy, PreparedStatement realPreparedStatement)
    180      {
    181        super(connectionSpy, realPreparedStatement);  // does null check for us
    182        this.sql = sql;
    183        this.realPreparedStatement = realPreparedStatement;
    184        log = SpyLogFactory.getSpyLogDelegator();
    185        rdbmsSpecifics = connectionSpy.getRdbmsSpecifics();
    186      }
    187
    188      public String getClassType()
    189      {
    190        return "PreparedStatement";
    191      }
    192
    193      // forwarding methods
    194
    195      public void setTime(int parameterIndex, Time x) throws SQLException
    196      {
    197        String methodCall = "setTime(" + parameterIndex + ", " + x + ")";
    198        argTraceSet(parameterIndex, "(Time)", x);
    199        try
    200        {
    201          realPreparedStatement.setTime(parameterIndex, x);
    202        }
    203        catch (SQLException s)
    204        {
    205          reportException(methodCall, s);
    206          throw s;
    207        }
    208        reportReturn(methodCall);
    209      }
    210
    211      public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException
    212      {
    213        String methodCall = "setTime(" + parameterIndex + ", " + x + ", " + cal + ")";
    214        argTraceSet(parameterIndex, "(Time)", x);
    215        try
    216        {
    217          realPreparedStatement.setTime(parameterIndex, x, cal);
    218        }
    219        catch (SQLException s)
    220        {
    221          reportException(methodCall, s);
    222          throw s;
    223        }
    224        reportReturn(methodCall);
    225      }
    226
    227      public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException
    228      {
    229        String methodCall = "setCharacterStream(" + parameterIndex + ", " + reader + ", " + length + ")";
    230        argTraceSet(parameterIndex, "(Reader)", "<Reader of length " + length + ">");
    231        try
    232        {
    233          realPreparedStatement.setCharacterStream(parameterIndex, reader, length);
    234        }
    235        catch (SQLException s)
    236        {
    237          reportException(methodCall, s);
    238          throw s;
    239        }
    240        reportReturn(methodCall);
    241      }
    242
    243      public void setNull(int parameterIndex, int sqlType) throws SQLException
    244      {
    245        String methodCall = "setNull(" + parameterIndex + ", " + sqlType + ")";
    246        argTraceSet(parameterIndex, null, "null");
    247        try
    248        {
    249          realPreparedStatement.setNull(parameterIndex, sqlType);
    250        }
    251        catch (SQLException s)
    252        {
    253          reportException(methodCall, s);
    254          throw s;
    255        }
    256        reportReturn(methodCall);
    257      }
    258
    259      public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException
    260      {
    261        String methodCall = "setNull(" + paramIndex + ", " + sqlType + ", " + typeName + ")";
    262        argTraceSet(paramIndex, null, "null");
    263        try
    264        {
    265          realPreparedStatement.setNull(paramIndex, sqlType, typeName);
    266        }
    267        catch (SQLException s)
    268        {
    269          reportException(methodCall, s);
    270          throw s;
    271        }
    272        reportReturn(methodCall);
    273      }
    274
    275      public void setRef(int i, Ref x) throws SQLException
    276      {
    277        String methodCall = "setRef(" + i + ", " + x + ")";
    278        argTraceSet(i, "(Ref)", x);
    279        try
    280        {
    281          realPreparedStatement.setRef(i, x);
    282        }
    283        catch (SQLException s)
    284        {
    285          reportException(methodCall, s);
    286          throw s;
    287        }
    288        reportReturn(methodCall);
    289      }
    290
    291      public void setBoolean(int parameterIndex, boolean x) throws SQLException
    292      {
    293        String methodCall = "setBoolean(" + parameterIndex + ", " + x + ")";
    294        argTraceSet(parameterIndex, "(boolean)", Boolean.toString(x));
    295        try
    296        {
    297          realPreparedStatement.setBoolean(parameterIndex, x);
    298        }
    299        catch (SQLException s)
    300        {
    301          reportException(methodCall, s);
    302          throw s;
    303        }
    304        reportReturn(methodCall);
    305      }
    306
    307      public void setBlob(int i, Blob x) throws SQLException
    308      {
    309        String methodCall = "setBlob(" + i + ", " + x + ")";
    310        argTraceSet(i, "(Blob)", "<Blob of size " + x.length() + ">");
    311        try
    312        {
    313          realPreparedStatement.setBlob(i, x);
    314        }
    315        catch (SQLException s)
    316        {
    317          reportException(methodCall, s);
    318          throw s;
    319        }
    320        reportReturn(methodCall);
    321      }
    322
    323      public void setClob(int i, Clob x) throws SQLException
    324      {
    325        String methodCall = "setClob(" + i + ", " + x + ")";
    326        argTraceSet(i, "(Clob)", "<Clob of size " + x.length() + ">");
    327        try
    328        {
    329          realPreparedStatement.setClob(i, x);
    330        }
    331        catch (SQLException s)
    332        {
    333          reportException(methodCall, s);
    334          throw s;
    335        }
    336        reportReturn(methodCall);
    337      }
    338
    339      public void setArray(int i, Array x) throws SQLException
    340      {
    341        String methodCall = "setArray(" + i + ", " + x + ")";
    342        argTraceSet(i, "(Array)", "<Array>");
    343        try
    344        {
    345          realPreparedStatement.setArray(i, x);
    346        }
    347        catch (SQLException s)
    348        {
    349          reportException(methodCall, s);
    350          throw s;
    351        }
    352        reportReturn(methodCall);
    353      }
    354
    355      public void setByte(int parameterIndex, byte x) throws SQLException
    356      {
    357        String methodCall = "setByte(" + parameterIndex + ", " + x + ")";
    358        argTraceSet(parameterIndex, "(byte)", Byte.toString(x));
    359        try
    360        {
    361          realPreparedStatement.setByte(parameterIndex, x);
    362        }
    363        catch (SQLException s)
    364        {
    365          reportException(methodCall, s);
    366          throw s;
    367        }
    368        reportReturn(methodCall);
    369      }
    370
    371      /**
    372       * @deprecated
    373       */
    374      public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
    375      {
    376        String methodCall = "setUnicodeStream(" + parameterIndex + ", " + x + ", " + length + ")";
    377        argTraceSet(parameterIndex, "(Unicode InputStream)", "<Unicode InputStream of length " + length + ">");
    378        try
    379        {
    380          realPreparedStatement.setUnicodeStream(parameterIndex, x, length);
    381        }
    382        catch (SQLException s)
    383        {
    384          reportException(methodCall, s);
    385          throw s;
    386        }
    387        reportReturn(methodCall);
    388      }
    389
    390      public void setShort(int parameterIndex, short x) throws SQLException
    391      {
    392        String methodCall = "setShort(" + parameterIndex + ", " + x + ")";
    393        argTraceSet(parameterIndex, "(short)", Short.toString(x));
    394        try
    395        {
    396          realPreparedStatement.setShort(parameterIndex, x);
    397        }
    398        catch (SQLException s)
    399        {
    400          reportException(methodCall, s);
    401          throw s;
    402        }
    403        reportReturn(methodCall);
    404      }
    405
    406      public boolean execute() throws SQLException
    407      {
    408        String methodCall = "execute()";
    409        String dumpedSql = dumpedSql();
    410        reportSql(dumpedSql, methodCall);
    411        long tstart = System.currentTimeMillis();
    412        try
    413        {
    414          boolean result = realPreparedStatement.execute();
    415          reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
    416          return reportReturn(methodCall, result);
    417        }
    418        catch (SQLException s)
    419        {
    420          reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
    421          throw s;
    422        }
    423      }
    424
    425      public void setInt(int parameterIndex, int x) throws SQLException
    426      {
    427        String methodCall = "setInt(" + parameterIndex + ", " + x + ")";
    428        argTraceSet(parameterIndex, "(int)", Integer.toString(x));
    429        try
    430        {
    431          realPreparedStatement.setInt(parameterIndex, x);
    432        }
    433        catch (SQLException s)
    434        {
    435          reportException(methodCall, s);
    436          throw s;
    437        }
    438        reportReturn(methodCall);
    439      }
    440
    441      public void setLong(int parameterIndex, long x) throws SQLException
    442      {
    443        String methodCall = "setLong(" + parameterIndex + ", " + x + ")";
    444        argTraceSet(parameterIndex, "(long)", Long.toString(x));
    445        try
    446        {
    447          realPreparedStatement.setLong(parameterIndex, x);
    448        }
    449        catch (SQLException s)
    450        {
    451          reportException(methodCall, s);
    452          throw s;
    453        }
    454        reportReturn(methodCall);
    455      }
    456
    457      public void setFloat(int parameterIndex, float x) throws SQLException
    458      {
    459        String methodCall = "setFloat(" + parameterIndex + ", " + x + ")";
    460        argTraceSet(parameterIndex, "(float)", Float.toString(x));
    461        try
    462        {
    463          realPreparedStatement.setFloat(parameterIndex, x);
    464        }
    465        catch (SQLException s)
    466        {
    467          reportException(methodCall, s);
    468          throw s;
    469        }
    470        reportReturn(methodCall);
    471      }
    472
    473      public void setDouble(int parameterIndex, double x) throws SQLException
    474      {
    475        String methodCall = "setDouble(" + parameterIndex + ", " + x + ")";
    476        argTraceSet(parameterIndex, "(double)", Double.toString(x));
    477        try
    478        {
    479          realPreparedStatement.setDouble(parameterIndex, x);
    480        }
    481        catch (SQLException s)
    482        {
    483          reportException(methodCall, s);
    484          throw s;
    485        }
    486        reportReturn(methodCall);
    487      }
    488
    489      public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
    490      {
    491        String methodCall = "setBigDecimal(" + parameterIndex + ", " + x + ")";
    492        argTraceSet(parameterIndex, "(BigDecimal)", x);
    493        try
    494        {
    495          realPreparedStatement.setBigDecimal(parameterIndex, x);
    496        }
    497        catch (SQLException s)
    498        {
    499          reportException(methodCall, s);
    500          throw s;
    501        }
    502        reportReturn(methodCall);
    503      }
    504
    505      public void setURL(int parameterIndex, URL x) throws SQLException
    506      {
    507        String methodCall = "setURL(" + parameterIndex + ", " + x + ")";
    508
    509        argTraceSet(parameterIndex, "(URL)", x);
    510
    511        try
    512        {
    513          realPreparedStatement.setURL(parameterIndex, x);
    514        }
    515        catch (SQLException s)
    516        {
    517          reportException(methodCall, s);
    518          throw s;
    519        }
    520        reportReturn(methodCall);
    521      }
    522
    523      public void setString(int parameterIndex, String x) throws SQLException
    524      {
    525        String methodCall = "setString(" + parameterIndex + ", \"" + x + "\")";
    526
    527        argTraceSet(parameterIndex, "(String)", rdbmsSpecifics.formatParameterObject(x));
    528
    529        try
    530        {
    531          realPreparedStatement.setString(parameterIndex, x);
    532        }
    533        catch (SQLException s)
    534        {
    535          reportException(methodCall, s);
    536          throw s;
    537        }
    538        reportReturn(methodCall);
    539      }
    540
    541      public void setBytes(int parameterIndex, byte[] x) throws SQLException
    542      {
    543        String methodCall = "setBytes(" + parameterIndex + ", " + x + ")";
    544        argTraceSet(parameterIndex, "(byte[])", "<byte[]>");
    545        try
    546        {
    547          realPreparedStatement.setBytes(parameterIndex, x);
    548        }
    549        catch (SQLException s)
    550        {
    551          reportException(methodCall, s);
    552          throw s;
    553        }
    554        reportReturn(methodCall);
    555      }
    556
    557      public void setDate(int parameterIndex, Date x) throws SQLException
    558      {
    559        String methodCall = "setDate(" + parameterIndex + ", " + x + ")";
    560        argTraceSet(parameterIndex, "(Date)", rdbmsSpecifics.formatParameterObject(x));
    561        try
    562        {
    563          realPreparedStatement.setDate(parameterIndex, x);
    564        }
    565        catch (SQLException s)
    566        {
    567          reportException(methodCall, s);
    568          throw s;
    569        }
    570        reportReturn(methodCall);
    571      }
    572
    573      public ParameterMetaData getParameterMetaData() throws SQLException
    574      {
    575        String methodCall = "getParameterMetaData()";
    576        try
    577        {
    578          return (ParameterMetaData) reportReturn(methodCall, realPreparedStatement.getParameterMetaData());
    579        }
    580        catch (SQLException s)
    581        {
    582          reportException(methodCall, s);
    583          throw s;
    584        }
    585      }
    586
    587      public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException
    588      {
    589        String methodCall = "setDate(" + parameterIndex + ", " + x + ", " + cal + ")";
    590        argTraceSet(parameterIndex, "(Date)", rdbmsSpecifics.formatParameterObject(x));
    591
    592        try
    593        {
    594          realPreparedStatement.setDate(parameterIndex, x, cal);
    595        }
    596        catch (SQLException s)
    597        {
    598          reportException(methodCall, s);
    599          throw s;
    600        }
    601        reportReturn(methodCall);
    602      }
    603
    604      public ResultSet executeQuery() throws SQLException
    605      {
    606        String methodCall = "executeQuery()";
    607        String dumpedSql = dumpedSql();
    608        reportSql(dumpedSql, methodCall);
    609        long tstart = System.currentTimeMillis();
    610        try
    611        {
    612          ResultSet r = realPreparedStatement.executeQuery();
    613          reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
    614          ResultSetSpy rsp = new ResultSetSpy(this, r);
    615          return (ResultSet) reportReturn(methodCall, rsp);
    616        }
    617        catch (SQLException s)
    618        {
    619          reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
    620          throw s;
    621        }
    622      }
    623
    624      private String getTypeHelp(Object x)
    625      {
    626        if (x==null)
    627        {
    628          return "(null)";
    629        }
    630        else
    631        {
    632          return "(" + x.getClass().getName() + ")";
    633        }
    634      }
    635
    636      public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
    637      {
    638        String methodCall = "setObject(" + parameterIndex + ", " + x + ", " + targetSqlType + ", " + scale + ")";
    639        argTraceSet(parameterIndex, getTypeHelp(x), rdbmsSpecifics.formatParameterObject(x));
    640
    641        try
    642        {
    643          realPreparedStatement.setObject(parameterIndex, x, targetSqlType, scale);
    644        }
    645        catch (SQLException s)
    646        {
    647          reportException(methodCall, s);
    648          throw s;
    649        }
    650        reportReturn(methodCall);
    651      }
    652
    653      public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
    654      {
    655        String methodCall = "setObject(" + parameterIndex + ", " + x + ", " + targetSqlType + ")";
    656        argTraceSet(parameterIndex, getTypeHelp(x), rdbmsSpecifics.formatParameterObject(x));
    657        try
    658        {
    659          realPreparedStatement.setObject(parameterIndex, x, targetSqlType);
    660        }
    661        catch (SQLException s)
    662        {
    663          reportException(methodCall, s);
    664          throw s;
    665        }
    666        reportReturn(methodCall);
    667      }
    668
    669      public void setObject(int parameterIndex, Object x) throws SQLException
    670      {
    671        String methodCall = "setObject(" + parameterIndex + ", " + x + ")";
    672        argTraceSet(parameterIndex, getTypeHelp(x), rdbmsSpecifics.formatParameterObject(x));
    673        try
    674        {
    675          realPreparedStatement.setObject(parameterIndex, x);
    676        }
    677        catch (SQLException s)
    678        {
    679          reportException(methodCall, s);
    680          throw s;
    681        }
    682        reportReturn(methodCall);
    683      }
    684
    685      public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
    686      {
    687        String methodCall = "setTimestamp(" + parameterIndex + ", " + x + ")";
    688        argTraceSet(parameterIndex, "(Date)", rdbmsSpecifics.formatParameterObject(x));
    689        try
    690        {
    691          realPreparedStatement.setTimestamp(parameterIndex, x);
    692        }
    693        catch (SQLException s)
    694        {
    695          reportException(methodCall, s);
    696          throw s;
    697        }
    698        reportReturn(methodCall);
    699      }
    700
    701      public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
    702      {
    703        String methodCall = "setTimestamp(" + parameterIndex + ", " + x + ", " + cal + ")";
    704        argTraceSet(parameterIndex, "(Timestamp)", rdbmsSpecifics.formatParameterObject(x));
    705        try
    706        {
    707          realPreparedStatement.setTimestamp(parameterIndex, x, cal);
    708        }
    709        catch (SQLException s)
    710        {
    711          reportException(methodCall, s);
    712          throw s;
    713        }
    714        reportReturn(methodCall);
    715      }
    716
    717      public int executeUpdate() throws SQLException
    718      {
    719        String methodCall = "executeUpdate()";
    720        String dumpedSql = dumpedSql();
    721        reportSql(dumpedSql, methodCall);
    722        long tstart = System.currentTimeMillis();
    723        try
    724        {
    725          int result = realPreparedStatement.executeUpdate();
    726          reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
    727          return reportReturn(methodCall, result);
    728        }
    729        catch (SQLException s)
    730        {
    731          reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
    732          throw s;
    733        }
    734      }
    735
    736      public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
    737      {
    738        String methodCall = "setAsciiStream(" + parameterIndex + ", " + x + ", " + length + ")";
    739        argTraceSet(parameterIndex, "(Ascii InputStream)", "<Ascii InputStream of length " + x + ">");
    740        try
    741        {
    742          realPreparedStatement.setAsciiStream(parameterIndex, x, length);
    743        }
    744        catch (SQLException s)
    745        {
    746          reportException(methodCall, s);
    747          throw s;
    748        }
    749        reportReturn(methodCall);
    750      }
    751
    752      public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
    753      {
    754        String methodCall = "setBinaryStream(" + parameterIndex + ", " + x + ", " + length + ")";
    755        argTraceSet(parameterIndex, "(Binary InputStream)", "<Binary InputStream of length " + length + ">");
    756        try
    757        {
    758          realPreparedStatement.setBinaryStream(parameterIndex, x, length);
    759        }
    760        catch (SQLException s)
    761        {
    762          reportException(methodCall, s);
    763          throw s;
    764        }
    765        reportReturn(methodCall);
    766      }
    767
    768      public void clearParameters() throws SQLException
    769      {
    770        String methodCall = "clearParameters()";
    771
    772        synchronized (argTrace)
    773        {
    774          argTrace.clear();
    775        }
    776
    777        try
    778        {
    779          realPreparedStatement.clearParameters();
    780        }
    781        catch (SQLException s)
    782        {
    783          reportException(methodCall, s);
    784          throw s;
    785        }
    786        reportReturn(methodCall);
    787      }
    788
    789      public ResultSetMetaData getMetaData() throws SQLException
    790      {
    791        String methodCall = "getMetaData()";
    792        try
    793        {
    794          return (ResultSetMetaData) reportReturn(methodCall, realPreparedStatement.getMetaData());
    795        }
    796        catch (SQLException s)
    797        {
    798          reportException(methodCall, s);
    799          throw s;
    800        }
    801      }
    802
    803      public void addBatch() throws SQLException
    804      {
    805        String methodCall = "addBatch()";
    806        currentBatch.add(dumpedSql());
    807        try
    808        {
    809          realPreparedStatement.addBatch();
    810        }
    811        catch (SQLException s)
    812        {
    813          reportException(methodCall, s);
    814          throw s;
    815        }
    816        reportReturn(methodCall);
    817      }
    818    }