Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/mysql/jdbc/PreparedStatement.java


1   /*
2    Copyright (C) 2002-2004 MySQL AB
3   
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of version 2 of the GNU General Public License as
6    published by the Free Software Foundation.
7    
8   
9    There are special exceptions to the terms and conditions of the GPL 
10   as it is applied to this software. View the full text of the 
11   exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
12   software distribution.
13  
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18  
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  
23   */
24  package com.mysql.jdbc;
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.ByteArrayOutputStream;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.ObjectOutputStream;
31  import java.io.Reader;
32  import java.io.StringReader;
33  import java.io.UnsupportedEncodingException;
34  
35  import java.math.BigDecimal;
36  
37  import java.net.URL;
38  
39  import java.sql.Array;
40  import java.sql.Clob;
41  import java.sql.ParameterMetaData;
42  import java.sql.Ref;
43  import java.sql.SQLException;
44  import java.sql.Time;
45  import java.sql.Timestamp;
46  import java.sql.Types;
47  
48  import java.text.ParsePosition;
49  import java.text.SimpleDateFormat;
50  
51  import java.util.ArrayList;
52  import java.util.Calendar;
53  import java.util.TimeZone;
54  
55  
56  /**
57   * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
58   * This object can then be used to efficiently execute this statement multiple
59   * times.
60   * 
61   * <p>
62   * <B>Note:</B> The setXXX methods for setting IN parameter values must specify
63   * types that are compatible with the defined SQL type of the input parameter.
64   * For instance, if the IN parameter has SQL type Integer, then setInt should
65   * be used.
66   * </p>
67   * 
68   * <p>
69   * If arbitrary parameter type conversions are required, then the setObject
70   * method should be used with a target SQL type.
71   * </p>
72   *
73   * @author Mark Matthews
74   * @version $Id: PreparedStatement.java,v 1.27.2.45 2004/08/27 21:50:12 mmatthew Exp $
75   *
76   * @see java.sql.ResultSet
77   * @see java.sql.PreparedStatement
78   */
79  public class PreparedStatement extends com.mysql.jdbc.Statement
80      implements java.sql.PreparedStatement {
81      private ArrayList batchedGeneratedKeys = null;
82      private java.sql.DatabaseMetaData dbmd = null;
83      private ParseInfo parseInfo;
84      private java.sql.ResultSetMetaData pstmtResultMetaData;
85      private SimpleDateFormat tsdf = null;
86      private String originalSql = null;
87      private boolean[] isNull = null;
88      private boolean[] isStream = null;
89      private InputStream[] parameterStreams = null;
90      private byte[][] parameterValues = null;
91      private byte[][] staticSqlStrings = null;
92      private byte[] streamConvertBuf = new byte[4096];
93      private int[] streamLengths = null;
94      private boolean hasLimitClause = false;
95      private boolean isLoadDataQuery = false;
96      private boolean retrieveGeneratedKeys = false;
97      private boolean useTrueBoolean = false;
98      private char firstCharOfStmt = 0;
99  
100     /**
101      * Constructor for the PreparedStatement class.
102      *
103      * @param conn the connection creating this statement
104      * @param sql the SQL for this statement
105      * @param catalog the catalog/database this statement should be issued
106      *        against
107      *
108      * @throws SQLException if a database error occurs.
109      */
110     public PreparedStatement(Connection conn, String sql, String catalog)
111         throws SQLException {
112         super(conn, catalog);
113 
114         if (sql == null) {
115             throw new SQLException("SQL String can not be NULL",
116                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
117         }
118 
119         originalSql = sql;
120 
121         this.dbmd = this.connection.getMetaData();
122 
123         useTrueBoolean = connection.getIO().versionMeetsMinimum(3, 21, 23);
124 
125         this.parseInfo = new ParseInfo(sql, this.connection, this.dbmd,
126                 this.charEncoding, this.charConverter);
127 
128         initializeFromParseInfo();
129     }
130 
131     /**
132      * Creates a new PreparedStatement object.
133      *
134      * @param conn the connection creating this statement
135      * @param sql the SQL for this statement
136      * @param catalog the catalog/database this statement should be issued
137      *        against
138      * @param cachedParseInfo already created parseInfo.
139      *
140      * @throws SQLException DOCUMENT ME!
141      */
142     public PreparedStatement(Connection conn, String sql, String catalog,
143         ParseInfo cachedParseInfo) throws SQLException {
144         super(conn, catalog);
145 
146         if (sql == null) {
147             throw new SQLException("SQL String can not be NULL",
148                 SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
149         }
150 
151         originalSql = sql;
152 
153         this.dbmd = this.connection.getMetaData();
154 
155         useTrueBoolean = connection.getIO().versionMeetsMinimum(3, 21, 23);
156 
157         this.parseInfo = cachedParseInfo;
158 
159         initializeFromParseInfo();
160     }
161 
162     /**
163      * JDBC 2.0 Set an Array parameter.
164      *
165      * @param i the first parameter is 1, the second is 2, ...
166      * @param x an object representing an SQL array
167      *
168      * @throws SQLException because this method is not implemented.
169      * @throws NotImplemented DOCUMENT ME!
170      */
171     public void setArray(int i, Array x) throws SQLException {
172         throw new NotImplemented();
173     }
174 
175     /**
176      * When a very large ASCII value is input to a LONGVARCHAR parameter, it
177      * may be more practical to send it via a java.io.InputStream. JDBC will
178      * read the data from the stream as needed, until it reaches end-of-file.
179      * The JDBC driver will do any necessary conversion from ASCII to the
180      * database char format.
181      * 
182      * <P>
183      * <B>Note:</B> This stream object can either be a standard Java stream
184      * object or your own subclass that implements the standard interface.
185      * </p>
186      *
187      * @param parameterIndex the first parameter is 1...
188      * @param x the parameter value
189      * @param length the number of bytes in the stream
190      *
191      * @exception SQLException if a database access error occurs
192      */
193     public synchronized void setAsciiStream(int parameterIndex, InputStream x,
194         int length) throws SQLException {
195         if (x == null) {
196             setNull(parameterIndex, java.sql.Types.VARCHAR);
197         } else {
198             setBinaryStream(parameterIndex, x, length);
199         }
200     }
201 
202     /**
203      * Set a parameter to a java.math.BigDecimal value.  The driver converts
204      * this to a SQL NUMERIC value when it sends it to the database.
205      *
206      * @param parameterIndex the first parameter is 1...
207      * @param x the parameter value
208      *
209      * @exception SQLException if a database access error occurs
210      */
211     public void setBigDecimal(int parameterIndex, BigDecimal x)
212         throws SQLException {
213         if (x == null) {
214             setNull(parameterIndex, java.sql.Types.DECIMAL);
215         } else {
216             setInternal(parameterIndex, fixDecimalExponent(x.toString()));
217         }
218     }
219 
220     /**
221      * When a very large binary value is input to a LONGVARBINARY parameter, it
222      * may be more practical to send it via a java.io.InputStream. JDBC will
223      * read the data from the stream as needed, until it reaches end-of-file.
224      * 
225      * <P>
226      * <B>Note:</B> This stream object can either be a standard Java stream
227      * object or your own subclass that implements the standard interface.
228      * </p>
229      *
230      * @param parameterIndex the first parameter is 1...
231      * @param x the parameter value
232      * @param length the number of bytes to read from the stream (ignored)
233      *
234      * @throws SQLException if a database access error occurs
235      * @throws java.sql.SQLException DOCUMENT ME!
236      */
237     public void setBinaryStream(int parameterIndex, InputStream x, int length)
238         throws SQLException {
239         if (x == null) {
240             setNull(parameterIndex, java.sql.Types.BINARY);
241         } else {
242             if ((parameterIndex < 1)
243                     || (parameterIndex > staticSqlStrings.length)) {
244                 throw new java.sql.SQLException(
245                     "Parameter index out of range (" + parameterIndex + " > "
246                     + staticSqlStrings.length + ")",
247                     SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
248             }
249 
250             parameterStreams[parameterIndex - 1] = x;
251             isStream[parameterIndex - 1] = true;
252             streamLengths[parameterIndex - 1] = length;
253             isNull[parameterIndex - 1] = false;
254         }
255     }
256 
257     /**
258      * JDBC 2.0 Set a BLOB parameter.
259      *
260      * @param i the first parameter is 1, the second is 2, ...
261      * @param x an object representing a BLOB
262      *
263      * @throws SQLException if a database error occurs
264      */
265     public void setBlob(int i, java.sql.Blob x) throws SQLException {
266         setBinaryStream(i, x.getBinaryStream(), (int) x.length());
267     }
268 
269     /**
270      * Set a parameter to a Java boolean value.  The driver converts this to a
271      * SQL BIT value when it sends it to the database.
272      *
273      * @param parameterIndex the first parameter is 1...
274      * @param x the parameter value
275      *
276      * @throws SQLException if a database access error occurs
277      */
278     public void setBoolean(int parameterIndex, boolean x)
279         throws SQLException {
280         if (useTrueBoolean) {
281             setInternal(parameterIndex, x ? "'1'" : "'0'");
282         } else {
283             setInternal(parameterIndex, x ? "'t'" : "'f'");
284         }
285     }
286 
287     /**
288      * Set a parameter to a Java byte value.  The driver converts this to a SQL
289      * TINYINT value when it sends it to the database.
290      *
291      * @param parameterIndex the first parameter is 1...
292      * @param x the parameter value
293      *
294      * @exception SQLException if a database access error occurs
295      */
296     public void setByte(int parameterIndex, byte x) throws SQLException {
297         setInternal(parameterIndex, String.valueOf(x));
298     }
299 
300     /**
301      * Set a parameter to a Java array of bytes.  The driver converts this to a
302      * SQL VARBINARY or LONGVARBINARY (depending on the argument's size
303      * relative to the driver's limits on VARBINARYs) when it sends it to the
304      * database.
305      *
306      * @param parameterIndex the first parameter is 1...
307      * @param x the parameter value
308      *
309      * @exception SQLException if a database access error occurs
310      */
311     public void setBytes(int parameterIndex, byte[] x)
312       throws SQLException {
313       setBytes(parameterIndex, x, true);
314     }
315     
316     protected void setBytes(int parameterIndex, byte[] x, boolean checkForIntroducer)
317         throws SQLException {
318         if (x == null) {
319             setNull(parameterIndex, java.sql.Types.BINARY);
320         } else {
321             // escape them
322             int numBytes = x.length;
323             
324             int pad = 2;
325 
326             boolean needsIntroducer = checkForIntroducer && this.connection.getIO().versionMeetsMinimum(4, 1, 0);
327             
328             if (needsIntroducer) {
329               pad += 7;
330             }
331         
332             ByteArrayOutputStream bOut = new ByteArrayOutputStream(numBytes + pad);
333 
334             if (needsIntroducer) {
335               bOut.write('_');
336               bOut.write('b');
337               bOut.write('i');
338               bOut.write('n');
339               bOut.write('a');
340               bOut.write('r');
341               bOut.write('y');
342             }
343             
344             bOut.write('\'');
345 
346             for (int i = 0; i < numBytes; ++i) {
347                 byte b = x[i];
348 
349                 switch (b) {
350                 case 0: /* Must be escaped for 'mysql' */
351                     bOut.write('\\');
352                     bOut.write('0');
353 
354                     break;
355 
356                 case '\n': /* Must be escaped for logs */
357                     bOut.write('\\');
358                     bOut.write('n');
359 
360                     break;
361 
362                 case '\r':
363                     bOut.write('\\');
364                     bOut.write('r');
365 
366                     break;
367 
368                 case '\\':
369                     bOut.write('\\');
370                     bOut.write('\\');
371 
372                     break;
373 
374                 case '\'':
375                     bOut.write('\\');
376                     bOut.write('\'');
377 
378                     break;
379 
380                 case '"': /* Better safe than sorry */
381                     bOut.write('\\');
382                     bOut.write('"');
383 
384                     break;
385 
386                 case '\032': /* This gives problems on Win32 */
387                     bOut.write('\\');
388                     bOut.write('Z');
389 
390                     break;
391 
392                 default:
393                     bOut.write(b);
394                 }
395             }
396 
397             bOut.write('\'');
398 
399             setInternal(parameterIndex, bOut.toByteArray());
400         }
401     }
402 
403     /**
404      * JDBC 2.0 When a very large UNICODE value is input to a LONGVARCHAR
405      * parameter, it may be more practical to send it via a java.io.Reader.
406      * JDBC will read the data from the stream as needed, until it reaches
407      * end-of-file.  The JDBC driver will do any necessary conversion from
408      * UNICODE to the database char format.
409      * 
410      * <P>
411      * <B>Note:</B> This stream object can either be a standard Java stream
412      * object or your own subclass that implements the standard interface.
413      * </p>
414      *
415      * @param parameterIndex the first parameter is 1, the second is 2, ...
416      * @param reader the java reader which contains the UNICODE data
417      * @param length the number of characters in the stream
418      *
419      * @throws SQLException if a database-access error occurs.
420      */
421     public void setCharacterStream(int parameterIndex, java.io.Reader reader,
422         int length) throws SQLException {
423         try {
424             if (reader == null) {
425                 setNull(parameterIndex, Types.LONGVARCHAR);
426             } else {
427                 char[] c = null;
428                 int len = 0;
429 
430                 boolean useLength = this.connection.useStreamLengthsInPrepStmts();
431 
432                 if (useLength && (length != -1)) {
433                     c = new char[length];
434 
435                     int numCharsRead = readFully(reader, c, length); // blocks until all read
436 
437                     setString(parameterIndex, new String(c, 0, numCharsRead));
438                 } else {
439                     c = new char[4096];
440 
441                     StringBuffer buf = new StringBuffer();
442 
443                     while ((len = reader.read(c)) != -1) {
444                         buf.append(c, 0, len);
445                     }
446 
447                     setString(parameterIndex, buf.toString());
448                 }
449             }
450         } catch (java.io.IOException ioEx) {
451             throw new SQLException(ioEx.toString(),
452                 SQLError.SQL_STATE_GENERAL_ERROR);
453         }
454     }
455 
456     /**
457      * JDBC 2.0 Set a CLOB parameter.
458      *
459      * @param i the first parameter is 1, the second is 2, ...
460      * @param x an object representing a CLOB
461      *
462      * @throws SQLException if a database error occurs
463      */
464     public void setClob(int i, Clob x) throws SQLException {
465         setString(i, x.getSubString(1L, (int) x.length()));
466     }
467 
468     /**
469      * Set a parameter to a java.sql.Date value.  The driver converts this to a
470      * SQL DATE value when it sends it to the database.
471      *
472      * @param parameterIndex the first parameter is 1...
473      * @param x the parameter value
474      *
475      * @exception SQLException if a database access error occurs
476      */
477     public void setDate(int parameterIndex, java.sql.Date x)
478         throws SQLException {
479         if (x == null) {
480             setNull(parameterIndex, java.sql.Types.DATE);
481         } else {
482             // FIXME: Have instance version of this, problem as it's
483             //        not thread-safe :(
484             SimpleDateFormat dateFormatter = new SimpleDateFormat(
485                     "''yyyy-MM-dd''");
486             setInternal(parameterIndex, dateFormatter.format(x));
487         }
488     }
489 
490     /**
491      * Set a parameter to a java.sql.Date value.  The driver converts this to a
492      * SQL DATE value when it sends it to the database.
493      *
494      * @param parameterIndex the first parameter is 1, the second is 2, ...
495      * @param x the parameter value
496      * @param cal the calendar to interpret the date with
497      *
498      * @throws SQLException if a database-access error occurs.
499      */
500     public void setDate(int parameterIndex, java.sql.Date x, Calendar cal)
501         throws SQLException {
502         setDate(parameterIndex, x);
503     }
504 
505     /**
506      * Set a parameter to a Java double value.  The driver converts this to a
507      * SQL DOUBLE value when it sends it to the database
508      *
509      * @param parameterIndex the first parameter is 1...
510      * @param x the parameter value
511      *
512      * @throws SQLException if a database access error occurs
513      */
514     public void setDouble(int parameterIndex, double x)
515         throws SQLException {
516         setInternal(parameterIndex, fixDecimalExponent(String.valueOf(x)));
517     }
518 
519     /**
520      * Set a parameter to a Java float value.  The driver converts this to a
521      * SQL FLOAT value when it sends it to the database.
522      *
523      * @param parameterIndex the first parameter is 1...
524      * @param x the parameter value
525      *
526      * @throws SQLException if a database access error occurs
527      */
528     public void setFloat(int parameterIndex, float x) throws SQLException {
529         setInternal(parameterIndex, fixDecimalExponent(String.valueOf(x)));
530     }
531 
532     /* (non-Javadoc)
533      * @see java.sql.Statement#getGeneratedKeys()
534      */
535     public synchronized java.sql.ResultSet getGeneratedKeys()
536         throws SQLException {
537         if (this.batchedGeneratedKeys == null) {
538             return super.getGeneratedKeys();
539         } else {
540             Field[] fields = new Field[1];
541             fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17);
542 
543             return new com.mysql.jdbc.ResultSet(currentCatalog, fields,
544                 new RowDataStatic(this.batchedGeneratedKeys), connection);
545         }
546     }
547 
548     /**
549      * Set a parameter to a Java int value.  The driver converts this to a SQL
550      * INTEGER value when it sends it to the database.
551      *
552      * @param parameterIndex the first parameter is 1...
553      * @param x the parameter value
554      *
555      * @throws SQLException if a database access error occurs
556      */
557     public void setInt(int parameterIndex, int x) throws SQLException {
558         setInternal(parameterIndex, String.valueOf(x));
559     }
560 
561     /**
562      * Set a parameter to a Java long value.  The driver converts this to a SQL
563      * BIGINT value when it sends it to the database.
564      *
565      * @param parameterIndex the first parameter is 1...
566      * @param x the parameter value
567      *
568      * @throws SQLException if a database access error occurs
569      */
570     public void setLong(int parameterIndex, long x) throws SQLException {
571         setInternal(parameterIndex, String.valueOf(x));
572     }
573 
574     /**
575      * The number, types and properties of a ResultSet's columns are provided
576      * by the getMetaData method.
577      *
578      * @return the description of a ResultSet's columns
579      *
580      * @throws SQLException if a database-access error occurs.
581      */
582     public synchronized java.sql.ResultSetMetaData getMetaData()
583         throws SQLException {
584         PreparedStatement mdStmt = null;
585         java.sql.ResultSet mdRs = null;
586 
587         if (this.pstmtResultMetaData == null) {
588             try {
589                 mdStmt = new PreparedStatement(this.connection,
590                         this.originalSql, this.currentCatalog, this.parseInfo);
591 
592                 mdStmt.setMaxRows(0);
593 
594                 int paramCount = this.parameterValues.length;
595 
596                 for (int i = 1; i <= paramCount; i++) {
597                     mdStmt.setString(i, "");
598                 }
599 
600                 boolean hadResults = mdStmt.execute();
601 
602                 if (hadResults) {
603                     mdRs = mdStmt.getResultSet();
604 
605                     this.pstmtResultMetaData = mdRs.getMetaData();
606                 } else {
607                     this.pstmtResultMetaData = new ResultSetMetaData(new Field[0]);
608                 }
609             } finally {
610                 SQLException sqlExRethrow = null;
611 
612                 if (mdRs != null) {
613                     try {
614                         mdRs.close();
615                     } catch (SQLException sqlEx) {
616                         sqlExRethrow = sqlEx;
617                     }
618 
619                     mdRs = null;
620                 }
621 
622                 if (mdStmt != null) {
623                     try {
624                         mdStmt.close();
625                     } catch (SQLException sqlEx) {
626                         sqlExRethrow = sqlEx;
627                     }
628 
629                     mdStmt = null;
630                 }
631 
632                 if (sqlExRethrow != null) {
633                     throw sqlExRethrow;
634                 }
635             }
636         }
637 
638         return this.pstmtResultMetaData;
639     }
640 
641     /**
642      * Set a parameter to SQL NULL
643      * 
644      * <p>
645      * <B>Note:</B> You must specify the parameters SQL type (although MySQL
646      * ignores it)
647      * </p>
648      *
649      * @param parameterIndex the first parameter is 1, etc...
650      * @param sqlType the SQL type code defined in java.sql.Types
651      *
652      * @throws SQLException if a database access error occurs
653      */
654     public void setNull(int parameterIndex, int sqlType)
655         throws SQLException {
656         setInternal(parameterIndex, "null");
657         isNull[parameterIndex - 1] = true;
658     }
659 
660     //--------------------------JDBC 2.0-----------------------------
661 
662     /**
663      * Set a parameter to SQL NULL.
664      * 
665      * <P>
666      * <B>Note:</B> You must specify the parameter's SQL type.
667      * </p>
668      *
669      * @param parameterIndex the first parameter is 1, the second is 2, ...
670      * @param sqlType SQL type code defined by java.sql.Types
671      * @param arg argument parameters for null
672      *
673      * @throws SQLException if a database-access error occurs.
674      */
675     public void setNull(int parameterIndex, int sqlType, String arg)
676         throws SQLException {
677         setNull(parameterIndex, sqlType);
678     }
679 
680     /**
681      * Set the value of a parameter using an object; use the java.lang
682      * equivalent objects for integral values.
683      * 
684      * <P>
685      * The given Java object will be converted to the targetSqlType before
686      * being sent to the database.
687      * </p>
688      * 
689      * <P>
690      * note that this method may be used to pass database-specific abstract
691      * data types.  This is done by using a Driver-specific Java type and
692      * using a targetSqlType of java.sql.Types.OTHER
693      * </p>
694      *
695      * @param parameterIndex the first parameter is 1...
696      * @param parameterObj the object containing the input parameter value
697      * @param targetSqlType The SQL type to be send to the database
698      * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
699      *        this is the number of digits after the decimal.  For all other
700      *        types this value will be ignored.
701      *
702      * @throws SQLException if a database access error occurs
703      * @throws java.sql.SQLException DOCUMENT ME!
704      */
705     public void setObject(int parameterIndex, Object parameterObj,
706         int targetSqlType, int scale) throws SQLException {
707         if (parameterObj == null) {
708             setNull(parameterIndex, java.sql.Types.OTHER);
709         } else {
710             try {
711                 switch (targetSqlType) {
712                 case Types.BIT:
713                 case Types.TINYINT:
714                 case Types.SMALLINT:
715                 case Types.INTEGER:
716                 case Types.BIGINT:
717                 case Types.REAL:
718                 case Types.FLOAT:
719                 case Types.DOUBLE:
720                 case Types.DECIMAL:
721                 case Types.NUMERIC:
722 
723                     Number parameterAsNum;
724 
725                     if (parameterObj instanceof Boolean) {
726                         parameterAsNum = ((Boolean) parameterObj).booleanValue()
727                             ? new Integer(1) : new Integer(0);
728                     } else if (parameterObj instanceof String) {
729                         switch (targetSqlType) {
730                         case Types.BIT:
731                             parameterAsNum = (Boolean.getBoolean((String) parameterObj)
732                                 ? new Integer("1") : new Integer("0"));
733 
734                             break;
735 
736                         case Types.TINYINT:
737                         case Types.SMALLINT:
738                         case Types.INTEGER:
739                             parameterAsNum = Integer.valueOf((String) parameterObj);
740 
741                             break;
742 
743                         case Types.BIGINT:
744                             parameterAsNum = Long.valueOf((String) parameterObj);
745 
746                             break;
747 
748                         case Types.REAL:
749                             parameterAsNum = Float.valueOf((String) parameterObj);
750 
751                             break;
752 
753                         case Types.FLOAT:
754                         case Types.DOUBLE:
755                             parameterAsNum = Double.valueOf((String) parameterObj);
756 
757                             break;
758 
759                         case Types.DECIMAL:
760                         case Types.NUMERIC:default:
761                             parameterAsNum = new java.math.BigDecimal((String) parameterObj);
762                         }
763                     } else {
764                         parameterAsNum = (Number) parameterObj;
765                     }
766 
767                     switch (targetSqlType) {
768                     case Types.BIT:
769                     case Types.TINYINT:
770                     case Types.SMALLINT:
771                     case Types.INTEGER:
772                         setInt(parameterIndex, parameterAsNum.intValue());
773 
774                         break;
775 
776                     case Types.BIGINT:
777                         setLong(parameterIndex, parameterAsNum.longValue());
778 
779                         break;
780 
781                     case Types.REAL:
782                         setFloat(parameterIndex, parameterAsNum.floatValue());
783 
784                         break;
785 
786                     case Types.FLOAT:
787                     case Types.DOUBLE:
788                         setDouble(parameterIndex, parameterAsNum.doubleValue());
789 
790                         break;
791 
792                     case Types.DECIMAL:
793                     case Types.NUMERIC:default:
794 
795                         if (parameterAsNum instanceof java.math.BigDecimal) {
796                             setBigDecimal(parameterIndex,
797                                 (java.math.BigDecimal) parameterAsNum);
798                         } else if (parameterAsNum instanceof java.math.BigInteger) {
799                             setBigDecimal(parameterIndex,
800                                 new java.math.BigDecimal(
801                                     (java.math.BigInteger) parameterAsNum, scale));
802                         } else {
803                             setBigDecimal(parameterIndex,
804                                 new java.math.BigDecimal(
805                                     parameterAsNum.doubleValue()));
806                         }
807 
808                         break;
809                     }
810 
811                     break;
812 
813                 case Types.CHAR:
814                 case Types.VARCHAR:
815                 case Types.LONGVARCHAR:
816                     setString(parameterIndex, parameterObj.toString());
817 
818                     break;
819 
820                 case Types.CLOB:
821 
822                     if (parameterObj instanceof java.sql.Clob) {
823                         setClob(parameterIndex, (java.sql.Clob) parameterObj);
824                     } else {
825                         setString(parameterIndex, parameterObj.toString());
826                     }
827 
828                     break;
829 
830                 case Types.BINARY:
831                 case Types.VARBINARY:
832                 case Types.LONGVARBINARY:
833                 case Types.BLOB:
834 
835                     if (parameterObj instanceof byte[]) {
836                         setBytes(parameterIndex, (byte[]) parameterObj);
837                     } else if (parameterObj instanceof java.sql.Blob) {
838                         setBlob(parameterIndex, (java.sql.Blob) parameterObj);
839                     } else {
840                         setBytes(parameterIndex,
841                             StringUtils.getBytes(parameterObj.toString(),
842                                 this.charConverter, this.charEncoding,
843                                 this.connection.getServerCharacterEncoding(), 
844                 this.connection.parserKnowsUnicode()));
845                     }
846 
847                     break;
848 
849                 case Types.DATE:
850                 case Types.TIMESTAMP:
851 
852                     java.util.Date parameterAsDate;
853 
854                     if (parameterObj instanceof String) {
855                         ParsePosition pp = new ParsePosition(0);
856                         java.text.DateFormat sdf = new java.text.SimpleDateFormat(getDateTimePattern(
857                                     (String) parameterObj, false));
858                         parameterAsDate = sdf.parse((String) parameterObj, pp);
859                     } else {
860                         parameterAsDate = (java.util.Date) parameterObj;
861                     }
862 
863                     switch (targetSqlType) {
864                     case Types.DATE:
865 
866                         if (parameterAsDate instanceof java.sql.Date) {
867                             setDate(parameterIndex,
868                                 (java.sql.Date) parameterAsDate);
869                         } else {
870                             setDate(parameterIndex,
871                                 new java.sql.Date(parameterAsDate.getTime()));
872                         }
873 
874                         break;
875 
876                     case Types.TIMESTAMP:
877 
878                         if (parameterAsDate instanceof java.sql.Timestamp) {
879                             setTimestamp(parameterIndex,
880                                 (java.sql.Timestamp) parameterAsDate);
881                         } else {
882                             setTimestamp(parameterIndex,
883                                 new java.sql.Timestamp(
884                                     parameterAsDate.getTime()));
885                         }
886 
887                         break;
888                     }
889 
890                     break;
891 
892                 case Types.TIME:
893 
894                     if (parameterObj instanceof String) {
895                         java.text.DateFormat sdf = new java.text.SimpleDateFormat(getDateTimePattern(
896                                     (String) parameterObj, true));
897                         setTime(parameterIndex,
898                             new java.sql.Time(sdf.parse((String) parameterObj)
899                                                  .getTime()));
900                     } else if (parameterObj instanceof Timestamp) {
901                         Timestamp xT = (Timestamp) parameterObj;
902                         setTime(parameterIndex, new java.sql.Time(xT.getTime()));
903                     } else {
904                         setTime(parameterIndex, (java.sql.Time) parameterObj);
905                     }
906 
907                     break;
908 
909                 case Types.OTHER:
910                     setSerializableObject(parameterIndex, parameterObj);
911 
912                     break;
913 
914                 default:
915                     throw new java.sql.SQLException("Unknown Types value",
916                         SQLError.SQL_STATE_GENERAL_ERROR);
917                 }
918             } catch (Exception ex) {
919                 if (ex instanceof java.sql.SQLException) {
920                     throw (java.sql.SQLException) ex;
921                 } else {
922                     throw new java.sql.SQLException("Cannot convert "
923                         + parameterObj.getClass().toString()
924                         + " to SQL type requested due to "
925                         + ex.getClass().getName() + " - " + ex.getMessage(),
926                         SQLError.SQL_STATE_GENERAL_ERROR);
927                 }
928             }
929         }
930     }
931 
932     /**
933      * Set the value of a parameter using an object; use the java.lang
934      * equivalent objects for integral values.
935      *
936      * @param parameterIndex the first parameter is 1...
937      * @param parameterObj the object containing the input parameter value
938      * @param targetSqlType The SQL type to be send to the database
939      *
940      * @throws SQLException if an error occurs
941      */
942     public void setObject(int parameterIndex, Object parameterObj,
943         int targetSqlType) throws SQLException {
944         setObject(parameterIndex, parameterObj, targetSqlType, 0);
945     }
946 
947     /**
948      * Sets the given parameter to the given object.
949      *
950      * @param parameterIndex the parameter to set.
951      * @param parameterObj the object to use as a value for the parameter.
952      *
953      * @throws SQLException if an error occurs.
954      */
955     public void setObject(int parameterIndex, Object parameterObj)
956         throws SQLException {
957         if (parameterObj == null) {
958             setNull(parameterIndex, java.sql.Types.OTHER);
959         } else {
960             if (parameterObj instanceof Byte) {
961                 setInt(parameterIndex, ((Byte) parameterObj).intValue());
962             } else if (parameterObj instanceof String) {
963                 setString(parameterIndex, (String) parameterObj);
964             } else if (parameterObj instanceof BigDecimal) {
965                 setBigDecimal(parameterIndex, (BigDecimal) parameterObj);
966             } else if (parameterObj instanceof Short) {
967                 setShort(parameterIndex, ((Short) parameterObj).shortValue());
968             } else if (parameterObj instanceof Integer) {
969                 setInt(parameterIndex, ((Integer) parameterObj).intValue());
970             } else if (parameterObj instanceof Long) {
971                 setLong(parameterIndex, ((Long) parameterObj).longValue());
972             } else if (parameterObj instanceof Float) {
973                 setFloat(parameterIndex, ((Float) parameterObj).floatValue());
974             } else if (parameterObj instanceof Double) {
975                 setDouble(parameterIndex, ((Double) parameterObj).doubleValue());
976             } else if (parameterObj instanceof byte[]) {
977                 setBytes(parameterIndex, (byte[]) parameterObj);
978             } else if (parameterObj instanceof java.sql.Date) {
979                 setDate(parameterIndex, (java.sql.Date) parameterObj);
980             } else if (parameterObj instanceof Time) {
981                 setTime(parameterIndex, (Time) parameterObj);
982             } else if (parameterObj instanceof Timestamp) {
983                 setTimestamp(parameterIndex, (Timestamp) parameterObj);
984             } else if (parameterObj instanceof Boolean) {
985                 setBoolean(parameterIndex,
986                     ((Boolean) parameterObj).booleanValue());
987             } else if (parameterObj instanceof InputStream) {
988                 setBinaryStream(parameterIndex, (InputStream) parameterObj, -1);
989             } else if (parameterObj instanceof java.sql.Blob) {
990                 setBlob(parameterIndex, (java.sql.Blob) parameterObj);
991             } else if (parameterObj instanceof java.sql.Clob) {
992                 setClob(parameterIndex, (java.sql.Clob) parameterObj);
993             } else if (parameterObj instanceof java.util.Date) {
994                 setTimestamp(parameterIndex,
995                     new Timestamp(((java.util.Date) parameterObj).getTime()));
996             } else {
997                 setSerializableObject(parameterIndex, parameterObj);
998             }
999         }
1000    }
1001
1002    /**
1003     * @see PreparedStatement#getParameterMetaData()
1004     */
1005    public ParameterMetaData getParameterMetaData() throws SQLException {
1006        throw new NotImplemented();
1007    }
1008
1009    /**
1010     * JDBC 2.0 Set a REF(&lt;structured-type&gt;) parameter.
1011     *
1012     * @param i the first parameter is 1, the second is 2, ...
1013     * @param x an object representing data of an SQL REF Type
1014     *
1015     * @throws SQLException if a database error occurs
1016     * @throws NotImplemented DOCUMENT ME!
1017     */
1018    public void setRef(int i, Ref x) throws SQLException {
1019        throw new NotImplemented();
1020    }
1021
1022    /**
1023     * Set a parameter to a Java short value.  The driver converts this to a
1024     * SQL SMALLINT value when it sends it to the database.
1025     *
1026     * @param parameterIndex the first parameter is 1...
1027     * @param x the parameter value
1028     *
1029     * @throws SQLException if a database access error occurs
1030     */
1031    public void setShort(int parameterIndex, short x) throws SQLException {
1032        setInternal(parameterIndex, String.valueOf(x));
1033    }
1034
1035    /**
1036     * Set a parameter to a Java String value.  The driver converts this to a
1037     * SQL VARCHAR or LONGVARCHAR value (depending on the arguments size
1038     * relative to the driver's limits on VARCHARs) when it sends it to the
1039     * database.
1040     *
1041     * @param parameterIndex the first parameter is 1...
1042     * @param x the parameter value
1043     *
1044     * @throws SQLException if a database error occurs.
1045     */
1046    public void setString(int parameterIndex, String x)
1047        throws SQLException {
1048        // if the passed string is null, then set this column to null
1049        if (x == null) {
1050            try {
1051                setInternal(parameterIndex,
1052                    StringUtils.getBytes("null", this.charConverter,
1053                        this.charEncoding, this.connection.getServerCharacterEncoding(),
1054            this.connection.parserKnowsUnicode()));
1055            } catch (UnsupportedEncodingException uue) {
1056                throw new SQLException("Unsupported character encoding '"
1057                    + this.charEncoding + "'",
1058                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1059            }
1060        } else {
1061            StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
1062            buf.append('\'');
1063
1064            int stringLength = x.length();
1065
1066            for (int i = 0; i < stringLength; ++i) {
1067                char c = x.charAt(i);
1068
1069                switch (c) {
1070                case 0: /* Must be escaped for 'mysql' */
1071                    buf.append('\\');
1072                    buf.append('0');
1073
1074                    break;
1075
1076                case '\n': /* Must be escaped for logs */
1077                    buf.append('\\');
1078                    buf.append('n');
1079
1080                    break;
1081
1082                case '\r':
1083                    buf.append('\\');
1084                    buf.append('r');
1085
1086                    break;
1087
1088                case '\\':
1089                    buf.append('\\');
1090                    buf.append('\\');
1091
1092                    break;
1093
1094                case '\'':
1095                    buf.append('\\');
1096                    buf.append('\'');
1097
1098                    break;
1099
1100                case '"': /* Better safe than sorry */
1101                    buf.append('\\');
1102                    buf.append('"');
1103
1104                    break;
1105
1106                case '\032': /* This gives problems on Win32 */
1107                    buf.append('\\');
1108                    buf.append('Z');
1109
1110                    break;
1111
1112                default:
1113                    buf.append(c);
1114                }
1115            }
1116
1117            buf.append('\'');
1118
1119            String parameterAsString = buf.toString();
1120
1121            try {
1122                byte[] parameterAsBytes = null;
1123
1124                if (!this.isLoadDataQuery) {
1125                    parameterAsBytes = StringUtils.getBytes(parameterAsString,
1126                            this.charConverter, this.charEncoding,
1127                            this.connection.getServerCharacterEncoding(), 
1128              this.connection.parserKnowsUnicode());
1129                } else {
1130                    // Send with platform character encoding
1131                    parameterAsBytes = parameterAsString.getBytes();
1132                }
1133
1134                setInternal(parameterIndex, parameterAsBytes);
1135            } catch (UnsupportedEncodingException uue) {
1136                throw new SQLException("Unsupported character encoding '"
1137                    + this.charEncoding + "'",
1138                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1139            }
1140        }
1141    }
1142
1143    /**
1144     * Set a parameter to a java.sql.Time value.  The driver converts this to a
1145     * SQL TIME value when it sends it to the database.
1146     *
1147     * @param parameterIndex the first parameter is 1...));
1148     * @param x the parameter value
1149     *
1150     * @throws SQLException if a database access error occurs
1151     */
1152    public void setTime(int parameterIndex, Time x) throws SQLException {
1153        setTimeInternal(parameterIndex, x, this.connection.getDefaultTimeZone());
1154    }
1155
1156    /**
1157     * Set a parameter to a java.sql.Time value.  The driver converts this to a
1158     * SQL TIME value when it sends it to the database.
1159     *
1160     * @param parameterIndex the first parameter is 1, the second is 2, ...
1161     * @param x the parameter value
1162     * @param cal the cal specifying the timezone
1163     *
1164     * @throws SQLException if a database-access error occurs.
1165     */
1166    public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
1167        throws SQLException {
1168        setTimeInternal(parameterIndex, x, cal.getTimeZone());
1169    }
1170
1171    /**
1172     * Set a parameter to a java.sql.Timestamp value.  The driver converts this
1173     * to a SQL TIMESTAMP value when it sends it to the database.
1174     *
1175     * @param parameterIndex the first parameter is 1...
1176     * @param x the parameter value
1177     *
1178     * @throws SQLException if a database access error occurs
1179     */
1180    public void setTimestamp(int parameterIndex, Timestamp x)
1181        throws SQLException {
1182        setTimestampInternal(parameterIndex, x,
1183            this.connection.getDefaultTimeZone());
1184    }
1185
1186    /**
1187     * Set a parameter to a java.sql.Timestamp value.  The driver converts this
1188     * to a SQL TIMESTAMP value when it sends it to the database.
1189     *
1190     * @param parameterIndex the first parameter is 1, the second is 2, ...
1191     * @param x the parameter value
1192     * @param cal the calendar specifying the timezone to use
1193     *
1194     * @throws SQLException if a database-access error occurs.
1195     */
1196    public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
1197        Calendar cal) throws SQLException {
1198        setTimestampInternal(parameterIndex, x, cal.getTimeZone());
1199    }
1200
1201    /**
1202     * @see PreparedStatement#setURL(int, URL)
1203     */
1204    public void setURL(int parameterIndex, URL arg) throws SQLException {
1205        if (arg != null) {
1206            setString(parameterIndex, arg.toString());
1207        } else {
1208            setNull(parameterIndex, Types.CHAR);
1209        }
1210    }
1211
1212    /**
1213     * When a very large Unicode value is input to a LONGVARCHAR parameter, it
1214     * may be more practical to send it via a java.io.InputStream. JDBC will
1215     * read the data from the stream as needed, until it reaches end-of-file.
1216     * The JDBC driver will do any necessary conversion from UNICODE to the
1217     * database char format.
1218     * 
1219     * <P>
1220     * <B>Note:</B> This stream object can either be a standard Java stream
1221     * object or your own subclass that implements the standard interface.
1222     * </p>
1223     *
1224     * @param parameterIndex the first parameter is 1...
1225     * @param x the parameter value
1226     * @param length the number of bytes to read from the stream
1227     *
1228     * @throws SQLException if a database access error occurs
1229     *
1230     * @deprecated
1231     */
1232    public void setUnicodeStream(int parameterIndex, InputStream x, int length)
1233        throws SQLException {
1234        if (x == null) {
1235            setNull(parameterIndex, java.sql.Types.VARCHAR);
1236        } else {
1237            setBinaryStream(parameterIndex, x, length);
1238        }
1239    }
1240
1241    /**
1242     * JDBC 2.0 Add a set of parameters to the batch.
1243     *
1244     * @throws SQLException if a database-access error occurs.
1245     *
1246     * @see Statement#addBatch
1247     */
1248    public void addBatch() throws SQLException {
1249        if (batchedArgs == null) {
1250            batchedArgs = new ArrayList();
1251        }
1252
1253        batchedArgs.add(new BatchParams(parameterValues, parameterStreams,
1254                isStream, streamLengths, isNull));
1255    }
1256
1257    /**
1258     * In general, parameter values remain in force for repeated used of a
1259     * Statement.  Setting a parameter value automatically clears its previous
1260     * value.  However, in some cases, it is useful to immediately release the
1261     * resources used by the current parameter values; this can be done by
1262     * calling clearParameters
1263     *
1264     * @throws SQLException if a database access error occurs
1265     */
1266    public void clearParameters() throws SQLException {
1267        for (int i = 0; i < parameterValues.length; i++) {
1268            parameterValues[i] = null;
1269            parameterStreams[i] = null;
1270            isStream[i] = false;
1271            isNull[i] = false;
1272        }
1273    }
1274
1275    /**
1276     * Closes this prepared statement and releases all resources.
1277     *
1278     * @throws SQLException if database error occurs.
1279     */
1280    public void close() throws SQLException {
1281        super.close();
1282
1283        this.parseInfo = null;
1284        this.dbmd = null;
1285        this.originalSql = null;
1286        this.staticSqlStrings = null;
1287        this.parameterValues = null;
1288        this.parameterStreams = null;
1289        this.isStream = null;
1290        this.streamLengths = null;
1291        this.isNull = null;
1292        this.streamConvertBuf = null;
1293    }
1294
1295    /**
1296     * Some prepared statements return multiple results; the execute method
1297     * handles these complex statements as well as the simpler form of
1298     * statements handled by executeQuery and executeUpdate
1299     *
1300     * @return true if the next result is a ResultSet; false if it is an update
1301     *         count or there are no more results
1302     *
1303     * @throws SQLException if a database error occurs.
1304     */
1305    public boolean execute() throws SQLException {
1306        if (connection.isReadOnly() && (firstCharOfStmt != 'S')) {
1307            throw new SQLException("Connection is read-only. "
1308                + "Queries leading to data modification are not allowed",
1309                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1310        }
1311
1312        checkClosed();
1313
1314        ResultSet rs = null;
1315
1316        synchronized (connection.getMutex()) {
1317            this.batchedGeneratedKeys = null;
1318
1319            Buffer sendPacket = fillSendPacket();
1320
1321            String oldCatalog = null;
1322
1323            if (!this.connection.getCatalog().equals(currentCatalog)) {
1324                oldCatalog = this.connection.getCatalog();
1325                this.connection.setCatalog(currentCatalog);
1326            }
1327
1328            boolean oldInfoMsgState = false;
1329
1330            if (this.retrieveGeneratedKeys) {
1331                oldInfoMsgState = this.connection.isReadInfoMsgEnabled();
1332                this.connection.setReadInfoMsgEnabled(true);
1333            }
1334
1335            // If there isn't a limit clause in the SQL
1336            // then limit the number of rows to return in
1337            // an efficient manner. Only do this if
1338            // setMaxRows() hasn't been used on any Statements
1339            // generated from the current Connection (saves
1340            // a query, and network traffic).
1341            //
1342            // Only apply max_rows to selects
1343            //
1344            if (this.connection.useMaxRows()) {
1345                if (firstCharOfStmt == 'S') {
1346                    if (hasLimitClause) {
1347                        rs = this.connection.execSQL((