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((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}