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((String) null, maxRows,
1348                                sendPacket, resultSetConcurrency,
1349                                createStreamingResultSet(), true,
1350                                this.currentCatalog);
1351                    } else {
1352                        if (maxRows <= 0) {
1353                            this.connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
1354                                -1, this.currentCatalog);
1355                        } else {
1356                            this.connection.execSQL(
1357                                "SET OPTION SQL_SELECT_LIMIT=" + maxRows, -1,
1358                                this.currentCatalog);
1359                        }
1360                    }
1361                } else {
1362                    this.connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
1363                        -1, this.currentCatalog);
1364                }
1365
1366                // Finally, execute the query
1367                rs = this.connection.execSQL(null, -1, sendPacket,
1368                        resultSetConcurrency, createStreamingResultSet(),
1369                        (firstCharOfStmt == 'S'), this.currentCatalog);
1370            } else {
1371                rs = this.connection.execSQL(null, -1, sendPacket,
1372                        resultSetConcurrency, createStreamingResultSet(),
1373                        (firstCharOfStmt == 'S'), this.currentCatalog);
1374            }
1375
1376            if (this.retrieveGeneratedKeys) {
1377                this.connection.setReadInfoMsgEnabled(oldInfoMsgState);
1378            }
1379
1380            if (oldCatalog != null) {
1381                this.connection.setCatalog(oldCatalog);
1382            }
1383
1384            lastInsertId = rs.getUpdateID();
1385
1386            if (rs != null) {
1387                rs.setFirstCharOfQuery(this.firstCharOfStmt);
1388                rs.setConnection(connection);
1389                rs.setResultSetType(resultSetType);
1390                rs.setResultSetConcurrency(resultSetConcurrency);
1391                rs.setStatement(this);
1392
1393                this.results = rs;
1394            }
1395
1396            return ((rs != null) && rs.reallyResult());
1397        }
1398    }
1399
1400    /**
1401     * JDBC 2.0 Submit a batch of commands to the database for execution. This
1402     * method is optional.
1403     *
1404     * @return an array of update counts containing one element for each
1405     *         command in the batch.  The array is ordered according to the
1406     *         order in which commands were inserted into the batch
1407     *
1408     * @throws SQLException if a database-access error occurs, or the driver
1409     *         does not support batch statements
1410     * @throws java.sql.BatchUpdateException DOCUMENT ME!
1411     */
1412    public int[] executeBatch() throws SQLException {
1413        if (connection.isReadOnly()) {
1414            throw new SQLException("Connection is read-only. "
1415                + "Queries leading to data modification are not allowed",
1416                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1417        }
1418
1419        try {
1420            int[] updateCounts = null;
1421
1422            if (batchedArgs != null) {
1423                int nbrCommands = batchedArgs.size();
1424                updateCounts = new int[nbrCommands];
1425
1426                for (int i = 0; i < nbrCommands; i++) {
1427                    updateCounts[i] = -3;
1428                }
1429
1430                SQLException sqlEx = null;
1431
1432                int commandIndex = 0;
1433
1434                if (this.retrieveGeneratedKeys) {
1435                    this.batchedGeneratedKeys = new ArrayList(nbrCommands);
1436                }
1437
1438                for (commandIndex = 0; commandIndex < nbrCommands;
1439                        commandIndex++) {
1440                    Object arg = batchedArgs.get(commandIndex);
1441
1442                    if (arg instanceof String) {
1443                        updateCounts[commandIndex] = executeUpdate((String) arg);
1444                    } else {
1445                        BatchParams paramArg = (BatchParams) arg;
1446
1447                        try {
1448                            updateCounts[commandIndex] = executeUpdate(paramArg.parameterStrings,
1449                                    paramArg.parameterStreams,
1450                                    paramArg.isStream, paramArg.streamLengths,
1451                                    paramArg.isNull);
1452
1453                            if (this.retrieveGeneratedKeys) {
1454                                java.sql.ResultSet rs = null;
1455
1456                                try {
1457                                  // we don't want to use our version,
1458                                    // because we've altered the behavior of ours to support batch updates
1459                                    // (catch-22)
1460
1461                                    rs = super.getGeneratedKeys(); 
1462                                    
1463                                    while (rs.next()) {
1464                                        this.batchedGeneratedKeys.add(new byte[][] {rs
1465                                            .getBytes(1)});
1466                                    }
1467                                } finally {
1468                                    if (rs != null) {
1469                                        rs.close();
1470                                    }
1471                                }
1472                            }
1473                        } catch (SQLException ex) {
1474                            updateCounts[commandIndex] = EXECUTE_FAILED;
1475
1476                            if (this.connection.continueBatchOnError()) {
1477                                sqlEx = ex;
1478                            } else {
1479                                int[] newUpdateCounts = new int[commandIndex];
1480                                System.arraycopy(updateCounts, 0,
1481                                    newUpdateCounts, 0, commandIndex);
1482
1483                                throw new java.sql.BatchUpdateException(ex
1484                                    .getMessage(), ex.getSQLState(),
1485                                    ex.getErrorCode(), newUpdateCounts);
1486                            }
1487                        }
1488                    }
1489                }
1490
1491                if (sqlEx != null) {
1492                    throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
1493                        sqlEx.getSQLState(), sqlEx.getErrorCode(), updateCounts);
1494                }
1495            }
1496
1497            return (updateCounts != null) ? updateCounts : new int[0];
1498        } finally {
1499            clearBatch();
1500        }
1501    }
1502
1503    /**
1504     * A Prepared SQL query is executed and its ResultSet is returned
1505     *
1506     * @return a ResultSet that contains the data produced by the query - never
1507     *         null
1508     *
1509     * @throws SQLException if a database error occurs.
1510     */
1511    public synchronized java.sql.ResultSet executeQuery()
1512        throws SQLException {
1513        checkClosed();
1514
1515        if ((this.firstCharOfStmt == 'I') || (this.firstCharOfStmt == 'U')
1516                || (this.firstCharOfStmt == 'D')
1517                || (this.firstCharOfStmt == 'A')
1518                || (this.firstCharOfStmt == 'C')) {
1519            if (StringUtils.startsWithIgnoreCaseAndWs(this.originalSql, "INSERT")
1520                    || StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
1521                        "UPDATE")
1522                    || StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
1523                        "DELETE")
1524                    || StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
1525                        "DROP")
1526                    || StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
1527                        "CREATE")
1528                    || StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
1529                        "ALTER")) {
1530                throw new SQLException("Can not issue data manipulation statements with executeQuery()",
1531                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1532            }
1533        }
1534
1535        // We need to execute this all together
1536        // So synchronize on the Connection's mutex (because
1537        // even queries going through there synchronize
1538        // on the same mutex.
1539        synchronized (connection.getMutex()) {
1540            this.batchedGeneratedKeys = null;
1541
1542            Buffer sendPacket = fillSendPacket();
1543
1544            if (this.results != null) {
1545                this.results.close();
1546            }
1547
1548            String oldCatalog = null;
1549
1550            if (!this.connection.getCatalog().equals(currentCatalog)) {
1551                oldCatalog = this.connection.getCatalog();
1552                this.connection.setCatalog(currentCatalog);
1553            }
1554
1555            if (this.connection.useMaxRows()) {
1556                // If there isn't a limit clause in the SQL
1557                // then limit the number of rows to return in
1558                // an efficient manner. Only do this if
1559                // setMaxRows() hasn't been used on any Statements
1560                // generated from the current Connection (saves
1561                // a query, and network traffic).
1562                if (hasLimitClause) {
1563                    results = this.connection.execSQL((String) null, maxRows,
1564                            sendPacket, resultSetConcurrency,
1565                            createStreamingResultSet(), true,
1566                            this.currentCatalog);
1567                } else {
1568                    if (maxRows <= 0) {
1569                        this.connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
1570                            -1, this.currentCatalog);
1571                    } else {
1572                        this.connection.execSQL("SET OPTION SQL_SELECT_LIMIT="
1573                            + maxRows, -1, this.currentCatalog);
1574                    }
1575
1576                    this.results = this.connection.execSQL(null, -1,
1577                            sendPacket, resultSetConcurrency,
1578                            createStreamingResultSet(), true,
1579                            this.currentCatalog);
1580
1581                    if (oldCatalog != null) {
1582                        this.connection.setCatalog(oldCatalog);
1583                    }
1584                }
1585            } else {
1586                this.results = this.connection.execSQL(null, -1, sendPacket,
1587                        resultSetConcurrency, createStreamingResultSet(), true,
1588                        this.currentCatalog);
1589            }
1590
1591            if (oldCatalog != null) {
1592                this.connection.setCatalog(oldCatalog);
1593            }
1594        }
1595
1596        lastInsertId = this.results.getUpdateID();
1597        nextResults = this.results;
1598        this.results.setConnection(connection);
1599        this.results.setResultSetType(resultSetType);
1600        this.results.setResultSetConcurrency(resultSetConcurrency);
1601        this.results.setStatement(this);
1602
1603        return (java.sql.ResultSet) this.results;
1604    }
1605
1606    /**
1607     * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition, SQL
1608     * statements that return nothing such as SQL DDL statements can be
1609     * executed.
1610     *
1611     * @return either the row count for INSERT, UPDATE or DELETE; or 0 for SQL
1612     *         statements that return nothing.
1613     *
1614     * @throws SQLException if a database access error occurs
1615     */
1616    public synchronized int executeUpdate() throws SQLException {
1617        this.batchedGeneratedKeys = null;
1618
1619        return executeUpdate(parameterValues, parameterStreams, isStream,
1620            streamLengths, isNull);
1621    }
1622
1623    /**
1624     * Returns this PreparedStatement represented as a string.
1625     *
1626     * @return this PreparedStatement represented as a string.
1627     *
1628     * @throws RuntimeException if an error occurs
1629     */
1630    public String toString() {
1631        StringBuffer buf = new StringBuffer();
1632        buf.append(super.toString());
1633        buf.append(": ");
1634
1635        try {
1636            for (int i = 0; i < parameterValues.length; ++i) {
1637                if (this.charEncoding != null) {
1638                    buf.append(new String(staticSqlStrings[i], this.charEncoding));
1639                } else {
1640                    buf.append(new String(staticSqlStrings[i]));
1641                }
1642
1643                if ((parameterValues[i] == null) && !isStream[i]) {
1644                    buf.append("** NOT SPECIFIED **");
1645                } else if (isStream[i]) {
1646                    buf.append("** STREAM DATA **");
1647                } else {
1648                    if (this.charConverter != null) {
1649                        buf.append(this.charConverter.toString(
1650                                parameterValues[i]));
1651                    } else {
1652                        if (this.charEncoding != null) {
1653                            buf.append(new String(parameterValues[i],
1654                                    this.charEncoding));
1655                        } else {
1656                            buf.append(StringUtils.toAsciiString(
1657                                    parameterValues[i]));
1658                        }
1659                    }
1660                }
1661            }
1662
1663            if (this.charEncoding != null) {
1664                buf.append(new String(
1665                        staticSqlStrings[parameterValues.length],
1666                        this.charEncoding));
1667            } else {
1668                buf.append(staticSqlStrings[parameterValues.length]);
1669            }
1670        } catch (UnsupportedEncodingException uue) {
1671            throw new RuntimeException("Unsupported character encoding '"
1672                + this.charEncoding + "'");
1673        }
1674
1675        return buf.toString();
1676    }
1677
1678    /**
1679     * Used by updatable result sets for refreshRow() because the parameter has
1680     * already been escaped for updater or inserter prepared statements.
1681     *
1682     * @param parameterIndex the parameter to set.
1683     * @param parameterAsBytes the parameter as a string.
1684     *
1685     * @throws SQLException if an error occurs
1686     */
1687    protected void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes)
1688        throws SQLException {
1689        byte[] parameterWithQuotes = new byte[parameterAsBytes.length + 2];
1690        parameterWithQuotes[0] = '\'';
1691        System.arraycopy(parameterAsBytes, 0, parameterWithQuotes, 1,
1692            parameterAsBytes.length);
1693        parameterWithQuotes[parameterAsBytes.length + 1] = '\'';
1694
1695        setInternal(parameterIndex, parameterWithQuotes);
1696    }
1697
1698    /**
1699     * Sets wheather or not this statement should retreive generated keys.
1700     *
1701     * @param retrieveGeneratedKeys
1702     */
1703    protected void setRetrieveGeneratedKeys(boolean retrieveGeneratedKeys) {
1704        this.retrieveGeneratedKeys = retrieveGeneratedKeys;
1705    }
1706
1707    /**
1708     * Added to allow batch-updates
1709     *
1710     * @param batchedParameterStrings string values used in single statement
1711     * @param batchedParameterStreams stream values used in single statement
1712     * @param batchedIsStream flags for streams used in single statement
1713     * @param batchedStreamLengths lengths of streams to be read.
1714     * @param batchedIsNull flags for parameters that are null
1715     *
1716     * @return the update count
1717     *
1718     * @throws SQLException if a database error occurs
1719     * @throws java.sql.SQLException DOCUMENT ME!
1720     */
1721    protected synchronized int executeUpdate(byte[][] batchedParameterStrings,
1722        InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
1723        int[] batchedStreamLengths, boolean[] batchedIsNull)
1724        throws SQLException {
1725        if (connection.isReadOnly()) {
1726            throw new SQLException("Connection is read-only. "
1727                + "Queries leading to data modification are not allowed",
1728                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1729        }
1730
1731        checkClosed();
1732
1733        if ((this.firstCharOfStmt == 'S')
1734                && StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
1735                    "SELECT")) {
1736            throw new java.sql.SQLException("Can not issue executeUpdate() for SELECTs",
1737                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
1738        }
1739
1740        ResultSet rs = null;
1741
1742        // The checking and changing of catalogs
1743        // must happen in sequence, so synchronize
1744        // on the same mutex that _conn is using
1745        synchronized (connection.getMutex()) {
1746            Buffer sendPacket = fillSendPacket(batchedParameterStrings,
1747                    batchedParameterStreams, batchedIsStream,
1748                    batchedStreamLengths);
1749
1750            String oldCatalog = null;
1751
1752            if (!this.connection.getCatalog().equals(currentCatalog)) {
1753                oldCatalog = this.connection.getCatalog();
1754                this.connection.setCatalog(currentCatalog);
1755            }
1756
1757            //
1758            // Only apply max_rows to selects
1759            //
1760            if (this.connection.useMaxRows()) {
1761                this.connection.execSQL("SET OPTION SQL_SELECT_LIMIT=DEFAULT",
1762                    -1, this.currentCatalog);
1763            }
1764
1765            boolean oldInfoMsgState = false;
1766
1767            if (this.retrieveGeneratedKeys) {
1768                oldInfoMsgState = this.connection.isReadInfoMsgEnabled();
1769                this.connection.setReadInfoMsgEnabled(true);
1770            }
1771
1772            rs = this.connection.execSQL(null, -1, sendPacket,
1773                    resultSetConcurrency, false, false, this.currentCatalog);
1774
1775            if (this.retrieveGeneratedKeys) {
1776                this.connection.setReadInfoMsgEnabled(oldInfoMsgState);
1777            }
1778
1779            if (oldCatalog != null) {
1780                this.connection.setCatalog(oldCatalog);
1781            }
1782        }
1783
1784        this.results = rs;
1785
1786        rs.setFirstCharOfQuery(this.firstCharOfStmt);
1787
1788        updateCount = rs.getUpdateCount();
1789
1790        int truncatedUpdateCount = 0;
1791
1792        if (updateCount > Integer.MAX_VALUE) {
1793            truncatedUpdateCount = Integer.MAX_VALUE;
1794        } else {
1795            truncatedUpdateCount = (int) updateCount;
1796        }
1797
1798        lastInsertId = rs.getUpdateID();
1799        this.results = rs;
1800
1801        return truncatedUpdateCount;
1802    }
1803
1804    byte[] getBytes(int parameterIndex) throws SQLException {
1805        if (isStream[parameterIndex]) {
1806            return streamToBytes(parameterStreams[parameterIndex], false,
1807                streamLengths[parameterIndex],
1808                this.connection.useStreamLengthsInPrepStmts());
1809        } else {
1810            byte[] parameterVal = parameterValues[parameterIndex];
1811
1812            if (parameterVal == null) {
1813                return null;
1814            }
1815
1816            if ((parameterVal[0] == '\'')
1817                    && (parameterVal[parameterVal.length - 1] == '\'')) {
1818                byte[] valNoQuotes = new byte[parameterVal.length - 2];
1819                System.arraycopy(parameterVal, 1, valNoQuotes, 0,
1820                    parameterVal.length - 2);
1821
1822                return valNoQuotes;
1823            } else {
1824                return parameterVal;
1825            }
1826        }
1827    }
1828
1829    boolean isNull(int paramIndex) {
1830        return isNull[paramIndex];
1831    }
1832
1833    ParseInfo getParseInfo() {
1834        return this.parseInfo;
1835    }
1836
1837    /**
1838     * Sets the concurrency for result sets generated by this statement
1839     *
1840     * @param concurrencyFlag the result set concurrency flag from the
1841     *        ResultSet interface.
1842     */
1843    void setResultSetConcurrency(int concurrencyFlag) {
1844        resultSetConcurrency = concurrencyFlag;
1845    }
1846
1847    /**
1848     * Sets the result set type for result sets generated by this statement
1849     *
1850     * @param typeFlag the result set type from the ResultSet interface
1851     */
1852    void setResultSetType(int typeFlag) {
1853        resultSetType = typeFlag;
1854    }
1855
1856    private final String getDateTimePattern(String dt, boolean toTime)
1857        throws Exception {
1858        //
1859        // Special case
1860        //
1861        int dtLength = (dt != null) ? dt.length() : 0;
1862
1863        if ((dtLength >= 8) && (dtLength <= 10)) {
1864            int dashCount = 0;
1865            boolean isDateOnly = true;
1866
1867            for (int i = 0; i < dtLength; i++) {
1868                char c = dt.charAt(i);
1869
1870                if (!Character.isDigit(c) && (c != '-')) {
1871                    isDateOnly = false;
1872
1873                    break;
1874                }
1875
1876                if (c == '-') {
1877                    dashCount++;
1878                }
1879            }
1880
1881            if (isDateOnly && (dashCount == 2)) {
1882                return "yyyy-MM-dd";
1883            }
1884        }
1885
1886        //
1887        // Special case - time-only
1888        //
1889        boolean colonsOnly = true;
1890
1891        for (int i = 0; i < dtLength; i++) {
1892            char c = dt.charAt(i);
1893
1894            if (!Character.isDigit(c) && (c != ':')) {
1895                colonsOnly = false;
1896
1897                break;
1898            }
1899        }
1900
1901        if (colonsOnly) {
1902            return "HH:mm:ss";
1903        }
1904
1905        int n;
1906        int z;
1907        int count;
1908        int maxvecs;
1909        char c;
1910        char separator;
1911        StringReader reader = new StringReader(dt + " ");
1912        ArrayList vec = new ArrayList();
1913        ArrayList vecRemovelist = new ArrayList();
1914        Object[] nv = new Object[3];
1915        Object[] v;
1916        nv[0] = new Character('y');
1917        nv[1] = new StringBuffer();
1918        nv[2] = new Integer(0);
1919        vec.add(nv);
1920
1921        if (toTime) {
1922            nv = new Object[3];
1923            nv[0] = new Character('h');
1924            nv[1] = new StringBuffer();
1925            nv[2] = new Integer(0);
1926            vec.add(nv);
1927        }
1928
1929        while ((z = reader.read()) != -1) {
1930            separator = (char) z;
1931            maxvecs = vec.size();
1932
1933            for (count = 0; count < maxvecs; count++) {
1934                v = (Object[]) vec.get(count);
1935                n = ((Integer) v[2]).intValue();
1936                c = getSuccessor(((Character) v[0]).charValue(), n);
1937
1938                if (!Character.isLetterOrDigit(separator)) {
1939                    if ((c == ((Character) v[0]).charValue()) && (c != 'S')) {
1940                        vecRemovelist.add(v);
1941                    } else {
1942                        ((StringBuffer) v[1]).append(separator);
1943
1944                        if ((c == 'X') || (c == 'Y')) {
1945                            v[2] = new Integer(4);
1946                        }
1947                    }
1948                } else {
1949                    if (c == 'X') {
1950                        c = 'y';
1951                        nv = new Object[3];
1952                        nv[1] = (new StringBuffer(((StringBuffer) v[1])
1953                                .toString())).append('M');
1954                        nv[0] = new Character('M');
1955                        nv[2] = new Integer(1);
1956                        vec.add(nv);
1957                    } else if (c == 'Y') {
1958                        c = 'M';
1959                        nv = new Object[3];
1960                        nv[1] = (new StringBuffer(((StringBuffer) v[1])
1961                                .toString())).append('d');
1962                        nv[0] = new Character('d');
1963                        nv[2] = new Integer(1);
1964                        vec.add(nv);
1965                    }
1966
1967                    ((StringBuffer) v[1]).append(c);
1968
1969                    if (c == ((Character) v[0]).charValue()) {
1970                        v[2] = new Integer(n + 1);
1971                    } else {
1972                        v[0] = new Character(c);
1973                        v[2] = new Integer(1);
1974                    }
1975                }
1976            }
1977
1978            int size = vecRemovelist.size();
1979
1980            for (int i = 0; i < size; i++) {
1981                v = (Object[]) vecRemovelist.get(i);
1982                vec.remove(v);
1983            }
1984
1985            vecRemovelist.clear();
1986        }
1987
1988        int size = vec.size();
1989
1990        for (int i = 0; i < size; i++) {
1991            v = (Object[]) vec.get(i);
1992            c = ((Character) v[0]).charValue();
1993            n = ((Integer) v[2]).intValue();
1994
1995            boolean bk = getSuccessor(c, n) != c;
1996            boolean atEnd = (((c == 's') || (c == 'm')
1997                || ((c == 'h') && toTime)) && bk);
1998            boolean finishesAtDate = (bk && (c == 'd') && !toTime);
1999            boolean containsEnd = (((StringBuffer) v[1]).toString().indexOf('W') != -1);
2000
2001            if ((!atEnd && !finishesAtDate) || (containsEnd)) {
2002                vecRemovelist.add(v);
2003            }
2004        }
2005
2006        size = vecRemovelist.size();
2007
2008        for (int i = 0; i < size; i++) {
2009            vec.remove(vecRemovelist.get(i));
2010        }
2011
2012        vecRemovelist.clear();
2013        v = (Object[]) vec.get(0); //might throw exception
2014
2015        StringBuffer format = (StringBuffer) v[1];
2016        format.setLength(format.length() - 1);
2017
2018        return format.toString();
2019    }
2020
2021    private final void setInternal(int paramIndex, byte[] val)
2022        throws SQLException {
2023        if (this.isClosed) {
2024            throw new SQLException("PreparedStatement has been closed. No further operations allowed.",
2025                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2026        }
2027
2028        if ((paramIndex < 1)) {
2029            throw new SQLException("Parameter index out of range ("
2030                + paramIndex + " < 1 ).", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2031        } else if (paramIndex >= staticSqlStrings.length) {
2032            throw new SQLException("Parameter index out of range ("
2033                + paramIndex + " > number of parameters, which is " + (staticSqlStrings.length - 1) + ").",
2034                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2035        }
2036
2037        isStream[paramIndex - 1] = false;
2038        isNull[paramIndex - 1] = false;
2039        parameterStreams[paramIndex - 1] = null;
2040        parameterValues[paramIndex - 1] = val;
2041    }
2042
2043    private final void setInternal(int paramIndex, String val)
2044        throws SQLException {
2045        byte[] parameterAsBytes = null;
2046
2047        if (this.charConverter != null) {
2048            parameterAsBytes = this.charConverter.toBytes(val);
2049        } else {
2050            try {
2051                parameterAsBytes = StringUtils.getBytes(val,
2052                        this.charConverter, this.charEncoding,
2053                        this.connection.getServerCharacterEncoding(),
2054            this.connection.parserKnowsUnicode());
2055            } catch (UnsupportedEncodingException uEE) {
2056                throw new SQLException("Unsupported encoding '"
2057                    + this.charEncoding + "'",
2058                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2059            }
2060        }
2061
2062        setInternal(paramIndex, parameterAsBytes);
2063    }
2064
2065    /**
2066     * Sets the value for the placeholder as a serialized Java object (used by
2067     * various forms of setObject()
2068     *
2069     * @param parameterIndex DOCUMENT ME!
2070     * @param parameterObj DOCUMENT ME!
2071     *
2072     * @throws SQLException if a database error occurs.
2073     * @throws java.sql.SQLException DOCUMENT ME!
2074     */
2075    private final void setSerializableObject(int parameterIndex,
2076        Object parameterObj) throws SQLException {
2077        try {
2078            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
2079            ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut);
2080            objectOut.writeObject(parameterObj);
2081            objectOut.flush();
2082            objectOut.close();
2083            bytesOut.flush();
2084            bytesOut.close();
2085
2086            byte[] buf = bytesOut.toByteArray();
2087            ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf);
2088            setBinaryStream(parameterIndex, bytesIn, buf.length);
2089        } catch (Exception ex) {
2090            throw new java.sql.SQLException("Invalid argument value: "
2091                + ex.getClass().getName(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2092        }
2093    }
2094
2095    private final char getSuccessor(char c, int n) {
2096        return ((c == 'y') && (n == 2)) ? 'X'
2097                                        : (((c == 'y') && (n < 4)) ? 'y'
2098                                                                   : ((c == 'y')
2099        ? 'M'
2100        : (((c == 'M') && (n == 2)) ? 'Y'
2101                                    : (((c == 'M') && (n < 3)) ? 'M'
2102                                                               : ((c == 'M')
2103        ? 'd'
2104        : (((c == 'd') && (n < 2)) ? 'd'
2105                                   : ((c == 'd') ? 'H'
2106                                                 : (((c == 'H') && (n < 2))
2107        ? 'H'
2108        : ((c == 'H') ? 'm'
2109                      : (((c == 'm') && (n < 2)) ? 'm'
2110                                                 : ((c == 'm') ? 's'
2111                                                               : (((c == 's')
2112        && (n < 2)) ? 's' : 'W'))))))))))));
2113    }
2114
2115    /**
2116     * Set a parameter to a java.sql.Time value.  The driver converts this to a
2117     * SQL TIME value when it sends it to the database, using the given
2118     * timezone.
2119     *
2120     * @param parameterIndex the first parameter is 1...));
2121     * @param x the parameter value
2122     * @param tz the timezone to use
2123     *
2124     * @throws SQLException if a database access error occurs
2125     */
2126    private void setTimeInternal(int parameterIndex, Time x, TimeZone tz)
2127        throws SQLException {
2128        if (x == null) {
2129            setNull(parameterIndex, java.sql.Types.TIME);
2130        } else {
2131            x = TimeUtil.changeTimezone(this.connection, x, tz,
2132                    this.connection.getServerTimezone());
2133            setInternal(parameterIndex, "'" + x.toString() + "'");
2134        }
2135    }
2136
2137    /**
2138     * Set a parameter to a java.sql.Timestamp value.  The driver converts this
2139     * to a SQL TIMESTAMP value when it sends it to the database.
2140     *
2141     * @param parameterIndex the first parameter is 1, the second is 2, ...
2142     * @param x the parameter value
2143     * @param tz the timezone to use
2144     *
2145     * @throws SQLException if a database-access error occurs.
2146     */
2147    private synchronized void setTimestampInternal(int parameterIndex,
2148        Timestamp x, TimeZone tz) throws SQLException {
2149        if (x == null) {
2150            setNull(parameterIndex, java.sql.Types.TIMESTAMP);
2151        } else {
2152            String timestampString = null;
2153            x = TimeUtil.changeTimezone(this.connection, x, tz,
2154                    this.connection.getServerTimezone());
2155
2156            if (this.tsdf == null) {
2157                this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''");
2158            }
2159
2160            timestampString = this.tsdf.format(x);
2161
2162            setInternal(parameterIndex, timestampString); // SimpleDateFormat is not thread-safe
2163        }
2164    }
2165
2166    private final void escapeblockFast(byte[] buf,
2167        ByteArrayOutputStream bytesOut, int size) {
2168        int lastwritten = 0;
2169
2170        for (int i = 0; i < size; i++) {
2171            byte b = buf[i];
2172
2173            if (b == '\0') {
2174                //write stuff not yet written
2175                if (i > lastwritten) {
2176                    bytesOut.write(buf, lastwritten, i - lastwritten);
2177                }
2178
2179                //write escape
2180                bytesOut.write('\\');
2181                bytesOut.write('0');
2182                lastwritten = i + 1;
2183            } else {
2184                if ((b == '\\') || (b == '\'') || (b == '"')) {
2185                    //write stuff not yet written
2186                    if (i > lastwritten) {
2187                        bytesOut.write(buf, lastwritten, i - lastwritten);
2188                    }
2189
2190                    //write escape
2191                    bytesOut.write('\\');
2192                    lastwritten = i; //not i+1 as b wasn't written.
2193                }
2194            }
2195        }
2196
2197        //write out remaining stuff from buffer
2198        if (lastwritten < size) {
2199            bytesOut.write(buf, lastwritten, size - lastwritten);
2200        }
2201    }
2202
2203    private final void escapeblockFast(byte[] buf, Buffer packet, int size)
2204        throws SQLException {
2205        int lastwritten = 0;
2206
2207        for (int i = 0; i < size; i++) {
2208            byte b = buf[i];
2209
2210            if (b == '\0') {
2211                //write stuff not yet written
2212                if (i > lastwritten) {
2213                    packet.writeBytesNoNull(buf, lastwritten, i - lastwritten);
2214                }
2215
2216                //write escape
2217                packet.writeByte((byte) '\\');
2218                packet.writeByte((byte) '0');
2219                lastwritten = i + 1;
2220            } else {
2221                if ((b == '\\') || (b == '\'') || (b == '"')) {
2222                    //write stuff not yet written
2223                    if (i > lastwritten) {
2224                        packet.writeBytesNoNull(buf, lastwritten,
2225                            i - lastwritten);
2226                    }
2227
2228                    //write escape
2229                    packet.writeByte((byte) '\\');
2230                    lastwritten = i; //not i+1 as b wasn't written.
2231                }
2232            }
2233        }
2234
2235        //write out remaining stuff from buffer
2236        if (lastwritten < size) {
2237            packet.writeBytesNoNull(buf, lastwritten, size - lastwritten);
2238        }
2239    }
2240
2241    /**
2242     * Creates the packet that contains the query to be sent to the server.
2243     *
2244     * @return the packet to send to the server to issue the query.
2245     *
2246     * @throws SQLException if a database error occurs.
2247     */
2248    private Buffer fillSendPacket() throws SQLException {
2249        return fillSendPacket(this.parameterValues, this.parameterStreams,
2250            this.isStream, this.streamLengths);
2251    }
2252
2253    /**
2254     * Creates the packet that contains the query to be sent to the server.
2255     *
2256     * @param batchedParameterStrings DOCUMENT ME!
2257     * @param batchedParameterStreams DOCUMENT ME!
2258     * @param batchedIsStream DOCUMENT ME!
2259     * @param batchedStreamLengths DOCUMENT ME!
2260     *
2261     * @return DOCUMENT ME!
2262     *
2263     * @throws SQLException if a database error occurs.
2264     * @throws java.sql.SQLException DOCUMENT ME!
2265     */
2266    private Buffer fillSendPacket(byte[][] batchedParameterStrings,
2267        InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
2268        int[] batchedStreamLengths) throws SQLException {
2269        Buffer sendPacket = this.connection.getIO().getSharedSendPacket();
2270
2271        sendPacket.clear();
2272
2273        sendPacket.writeByte((byte) MysqlDefs.QUERY);
2274
2275        boolean useStreamLengths = this.connection.useStreamLengthsInPrepStmts();
2276
2277        //
2278        // Try and get this allocation as close as possible
2279        // for BLOBs
2280        //
2281        int ensurePacketSize = 0;
2282
2283        for (int i = 0; i < batchedParameterStrings.length; i++) {
2284            if (batchedIsStream[i] && useStreamLengths) {
2285                ensurePacketSize += batchedStreamLengths[i];
2286            }
2287        }
2288
2289        if (ensurePacketSize != 0) {
2290            sendPacket.ensureCapacity(ensurePacketSize);
2291        }
2292
2293        for (int i = 0; i < batchedParameterStrings.length; i++) {
2294            if ((batchedParameterStrings[i] == null)
2295                    && (batchedParameterStreams[i] == null)) {
2296                throw new java.sql.SQLException(
2297                    "No value specified for parameter " + (i + 1),
2298                    SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS);
2299            }
2300
2301            sendPacket.writeBytesNoNull(staticSqlStrings[i]);
2302
2303            if (batchedIsStream[i]) {
2304                streamToBytes(sendPacket, batchedParameterStreams[i], true,
2305                    batchedStreamLengths[i], useStreamLengths);
2306            } else {
2307                sendPacket.writeBytesNoNull(batchedParameterStrings[i]);
2308            }
2309        }
2310
2311        sendPacket.writeBytesNoNull(staticSqlStrings[batchedParameterStrings.length]);
2312
2313        return sendPacket;
2314    }
2315
2316    //
2317    // Adds '+' to decimal numbers that are positive (MySQL doesn't
2318    // understand them otherwise
2319    //
2320    private static final String fixDecimalExponent(String dString) {
2321        int ePos = dString.indexOf("E");
2322
2323        if (ePos == -1) {
2324            ePos = dString.indexOf("e");
2325        }
2326
2327        if (ePos != -1) {
2328            if (dString.length() > (ePos + 1)) {
2329                char maybeMinusChar = dString.charAt(ePos + 1);
2330
2331                if (maybeMinusChar != '-') {
2332                    StringBuffer buf = new StringBuffer(dString.length() + 1);
2333                    buf.append(dString.substring(0, ePos + 1));
2334                    buf.append('+');
2335                    buf.append(dString.substring(ePos + 1, dString.length()));
2336                    dString = buf.toString();
2337                }
2338            }
2339        }
2340
2341        return dString;
2342    }
2343
2344    private void initializeFromParseInfo() throws SQLException {
2345        this.staticSqlStrings = this.parseInfo.staticSql;
2346        this.hasLimitClause = this.parseInfo.foundLimitClause;
2347        this.isLoadDataQuery = this.parseInfo.foundLoadData;
2348        this.firstCharOfStmt = this.parseInfo.firstStmtChar;
2349
2350        int numberOfParameters = staticSqlStrings.length - 1;
2351
2352        parameterValues = new byte[numberOfParameters][];
2353        parameterStreams = new InputStream[numberOfParameters];
2354        isStream = new boolean[numberOfParameters];
2355        streamLengths = new int[numberOfParameters];
2356        isNull = new boolean[numberOfParameters];
2357
2358        clearParameters();
2359
2360        for (int j = 0; j < numberOfParameters; j++) {
2361            isStream[j] = false;
2362        }
2363    }
2364
2365    private final int readblock(InputStream i, byte[] b, int length)
2366        throws SQLException {
2367        try {
2368            int lengthToRead = length;
2369
2370            if (lengthToRead > b.length) {
2371                lengthToRead = b.length;
2372            }
2373
2374            return i.read(b, 0, lengthToRead);
2375        } catch (Throwable E) {
2376            throw new java.sql.SQLException("Error reading from InputStream "
2377                + E.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR);
2378        }
2379    }
2380
2381    private final int readblock(InputStream i, byte[] b)
2382        throws SQLException {
2383        try {
2384            return i.read(b);
2385        } catch (Throwable E) {
2386            throw new java.sql.SQLException("Error reading from InputStream "
2387                + E.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR);
2388        }
2389    }
2390
2391    /**
2392     * For the setXXXStream() methods. Basically converts an InputStream into a
2393     * String. Not very efficient, but it works.
2394     *
2395     * @param in DOCUMENT ME!
2396     * @param streamLength DOCUMENT ME!
2397     * @param useLength DOCUMENT ME!
2398     *
2399     * @return DOCUMENT ME!
2400     *
2401     * @throws SQLException DOCUMENT ME!
2402     */
2403    private final byte[] streamToBytes(InputStream in, int streamLength,
2404        boolean useLength) throws SQLException {
2405        return streamToBytes(in, true, streamLength, useLength);
2406    }
2407
2408    private final byte[] streamToBytes(InputStream in, boolean escape,
2409        int streamLength, boolean useLength) throws SQLException {
2410        try {
2411            if (streamLength == -1) {
2412                useLength = false;
2413            }
2414
2415            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
2416
2417            int bc = -1;
2418
2419            if (useLength) {
2420                bc = readblock(in, streamConvertBuf, streamLength);
2421            } else {
2422                bc = readblock(in, streamConvertBuf);
2423            }
2424
2425            int lengthLeftToRead = streamLength - bc;
2426
2427            if (this.connection.getIO().versionMeetsMinimum(4, 1, 0)) {
2428              bytesOut.write('_');
2429              bytesOut.write('b');
2430              bytesOut.write('i');
2431              bytesOut.write('n');
2432              bytesOut.write('a');
2433              bytesOut.write('r');
2434              bytesOut.write('y');
2435            }
2436            
2437            if (escape) {
2438                bytesOut.write('\'');
2439            }
2440
2441            while (bc > 0) {
2442                if (escape) {
2443                    escapeblockFast(streamConvertBuf, bytesOut, bc);
2444                } else {
2445                    bytesOut.write(streamConvertBuf, 0, bc);
2446                }
2447
2448                if (useLength) {
2449                    bc = readblock(in, streamConvertBuf, lengthLeftToRead);
2450
2451                    if (bc > 0) {
2452                        lengthLeftToRead -= bc;
2453                    }
2454                } else {
2455                    bc = readblock(in, streamConvertBuf);
2456                }
2457            }
2458
2459            if (escape) {
2460                bytesOut.write('\'');
2461            }
2462
2463            return bytesOut.toByteArray();
2464        } finally {
2465            try {
2466                in.close();
2467            } catch (IOException ioEx) {
2468                ;
2469            }
2470
2471            in = null;
2472        }
2473    }
2474
2475    private final void streamToBytes(Buffer packet, InputStream in,
2476        boolean escape, int streamLength, boolean useLength)
2477        throws SQLException {
2478        try {
2479            if (streamLength == -1) {
2480                useLength = false;
2481            }
2482
2483            int bc = -1;
2484
2485            if (useLength) {
2486                bc = readblock(in, streamConvertBuf, streamLength);
2487            } else {
2488                bc = readblock(in, streamConvertBuf);
2489            }
2490
2491            int lengthLeftToRead = streamLength - bc;
2492
2493            if (this.connection.getIO().versionMeetsMinimum(4, 1, 0)) {
2494              packet.writeStringNoNull("_binary");
2495            }
2496            
2497            if (escape) {
2498                packet.writeByte((byte) '\'');
2499            }
2500
2501            while (bc > 0) {
2502                if (escape) {
2503                    escapeblockFast(streamConvertBuf, packet, bc);
2504                } else {
2505                    packet.writeBytesNoNull(streamConvertBuf, 0, bc);
2506                }
2507
2508                if (useLength) {
2509                    bc = readblock(in, streamConvertBuf, lengthLeftToRead);
2510
2511                    if (bc > 0) {
2512                        lengthLeftToRead -= bc;
2513                    }
2514                } else {
2515                    bc = readblock(in, streamConvertBuf);
2516                }
2517            }
2518
2519            if (escape) {
2520                packet.writeByte((byte) '\'');
2521            }
2522        } finally {
2523            try {
2524                in.close();
2525            } catch (IOException ioEx) {
2526                ;
2527            }
2528
2529            in = null;
2530        }
2531    }
2532
2533    /**
2534     * Reads length bytes from reader into buf. Blocks until enough input is
2535     * available
2536     *
2537     * @param reader DOCUMENT ME!
2538     * @param buf DOCUMENT ME!
2539     * @param length DOCUMENT ME!
2540     *
2541     * @return DOCUMENT ME!
2542     *
2543     * @throws IOException DOCUMENT ME!
2544     */
2545    private static int readFully(Reader reader, char[] buf, int length)
2546        throws IOException {
2547        int numCharsRead = 0;
2548
2549        while (numCharsRead < length) {
2550            int count = reader.read(buf, numCharsRead, length - numCharsRead);
2551
2552            if (count < 0) {
2553                break;
2554            }
2555
2556            numCharsRead += count;
2557        }
2558
2559        return numCharsRead;
2560    }
2561
2562    class BatchParams {
2563        boolean[] isNull = null;
2564        boolean[] isStream = null;
2565        InputStream[] parameterStreams = null;
2566        byte[][] parameterStrings = null;
2567        int[] streamLengths = null;
2568
2569        BatchParams(byte[][] strings, InputStream[] streams,
2570            boolean[] isStreamFlags, int[] lengths, boolean[] isNullFlags) {
2571            //
2572            // Make copies
2573            //
2574            parameterStrings = new byte[strings.length][];
2575            parameterStreams = new InputStream[streams.length];
2576            isStream = new boolean[isStreamFlags.length];
2577            streamLengths = new int[lengths.length];
2578            isNull = new boolean[isNullFlags.length];
2579            System.arraycopy(strings, 0, parameterStrings, 0, strings.length);
2580            System.arraycopy(streams, 0, parameterStreams, 0, streams.length);
2581            System.arraycopy(isStreamFlags, 0, isStream, 0, isStreamFlags.length);
2582            System.arraycopy(lengths, 0, streamLengths, 0, lengths.length);
2583            System.arraycopy(isNullFlags, 0, isNull, 0, isNullFlags.length);
2584        }
2585    }
2586
2587    class ParseInfo {
2588        byte[][] staticSql = null;
2589        boolean foundLimitClause = false;
2590        boolean foundLoadData = false;
2591        char firstStmtChar = 0;
2592        int statementLength = 0;
2593        long lastUsed = 0;
2594
2595        /**
2596                               *
2597                               */
2598        public ParseInfo(String sql, Connection conn,
2599            java.sql.DatabaseMetaData dbmd, String encoding,
2600            SingleByteCharsetConverter converter) throws SQLException {
2601            if (sql == null) {
2602                throw new SQLException("SQL String can not be NULL",
2603                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
2604            }
2605
2606            lastUsed = System.currentTimeMillis();
2607
2608            String quotedIdentifierString = dbmd.getIdentifierQuoteString();
2609
2610            char quotedIdentifierChar = 0;
2611
2612            if ((quotedIdentifierString != null)
2613                    && !quotedIdentifierString.equals(" ")
2614                    && (quotedIdentifierString.length() > 0)) {
2615                quotedIdentifierChar = quotedIdentifierString.charAt(0);
2616            }
2617
2618            statementLength = sql.length();
2619
2620            ArrayList endpointList = new ArrayList();
2621            boolean inQuotes = false;
2622            boolean inQuotedId = false;
2623            int lastParmEnd = 0;
2624            int i;
2625
2626            int pre1 = 0;
2627            int pre2 = 0;
2628
2629            int lastAlphaCharPos = 0;
2630            int stopLookingForLimitClause = statementLength - 5;
2631
2632            foundLimitClause = false;
2633
2634            for (i = 0; i < statementLength; ++i) {
2635                char c = sql.charAt(i);
2636
2637                if ((firstStmtChar == 0) && !Character.isWhitespace(c)) {
2638                    // Determine what kind of statement we're doing (_S_elect, _I_nsert, etc.)
2639                    firstStmtChar = Character.toUpperCase(c);
2640                }
2641
2642                if (Character.isLetter(c)) {
2643                    lastAlphaCharPos = i;
2644                }
2645
2646                // are we in a quoted identifier?
2647                // (only valid when the id is not inside a 'string')
2648                if (!inQuotes && (quotedIdentifierChar != 0)
2649                        && (c == quotedIdentifierChar)) {
2650                    inQuotedId = !inQuotedId;
2651                }
2652
2653                // only respect quotes when not in a quoted identifier
2654                if (!inQuotedId) {
2655                    if ((c == '\'') && (pre1 == '\\') && (pre2 == '\\')) {
2656                        inQuotes = !inQuotes;
2657                    } else if ((c == '\'') && (pre1 != '\\')) {
2658                        inQuotes = !inQuotes;
2659                    }
2660                }
2661
2662                if ((c == '?') && !inQuotes) {
2663                    endpointList.add(new int[] { lastParmEnd, i });
2664                    lastParmEnd = i + 1;
2665                }
2666
2667                if (!inQuotes && (i < stopLookingForLimitClause)) {
2668                    if ((c == 'L') || (c == 'l')) {
2669                        char posI1 = sql.charAt(i + 1);
2670
2671                        if ((posI1 == 'I') || (posI1 == 'i')) {
2672                            char posM = sql.charAt(i + 2);
2673
2674                            if ((posM == 'M') || (posM == 'm')) {
2675                                char posI2 = sql.charAt(i + 3);
2676
2677                                if ((posI2 == 'I') || (posI2 == 'i')) {
2678                                    char posT = sql.charAt(i + 4);
2679
2680                                    if ((posT == 'T') || (posT == 't')) {
2681                                        foundLimitClause = true;
2682                                    }
2683                                }
2684                            }
2685                        }
2686                    }
2687                }
2688
2689                pre2 = pre1;
2690                pre1 = c;
2691            }
2692
2693            if (firstStmtChar == 'L') {
2694                if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) {
2695                    foundLoadData = true;
2696                } else {
2697                    foundLoadData = false;
2698                }
2699            } else {
2700                foundLoadData = false;
2701            }
2702
2703            endpointList.add(new int[] { lastParmEnd, statementLength });
2704            staticSql = new byte[endpointList.size()][];
2705
2706            for (i = 0; i < staticSql.length; i++) {
2707                int[] ep = (int[]) endpointList.get(i);
2708                int end = ep[1];
2709                int begin = ep[0];
2710                int len = end - begin;
2711
2712                if (foundLoadData) {
2713                    String temp = new String(sql.toCharArray(), begin, len);
2714                    staticSql[i] = temp.getBytes();
2715                } else if (encoding == null) {
2716                    byte[] buf = new byte[len];
2717
2718                    for (int j = 0; j < len; j++) {
2719                        buf[j] = (byte) sql.charAt(begin + j);
2720                    }
2721
2722                    staticSql[i] = buf;
2723                } else {
2724                    try {
2725                        if (converter != null) {
2726                            staticSql[i] = StringUtils.getBytes(sql, converter,
2727                                    encoding, connection.getServerCharacterEncoding(),
2728                  begin, len,
2729                                    connection.parserKnowsUnicode());
2730                        } else {
2731                            String temp = new String(sql.toCharArray(), begin,
2732                                    len);
2733                            staticSql[i] = StringUtils.getBytes(temp, null,
2734                                    encoding, connection.parserKnowsUnicode());
2735                        }
2736                    } catch (java.io.UnsupportedEncodingException ue) {
2737                        throw new SQLException(ue.toString());
2738                    }
2739                }
2740            }
2741
2742            int numberOfParameters = staticSql.length - 1;
2743        }
2744    }
2745}