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(<structured-type>) 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((