1 /*
2 Copyright 2002-2007 MySQL AB, 2008 Sun Microsystems
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 There are special exceptions to the terms and conditions of the GPL
9 as it is applied to this software. View the full text of the
10 exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
11 software distribution.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22
23
24 */
25 package com.mysql.jdbc;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.Reader;
30 import java.io.UnsupportedEncodingException;
31 import java.lang.reflect.Constructor;
32 import java.lang.reflect.InvocationTargetException;
33 import java.math.BigDecimal;
34 import java.net.URL;
35 import java.sql.Array;
36 import java.sql.Blob;
37 import java.sql.Clob;
38 import java.sql.Date;
39 import java.sql.ParameterMetaData;
40 import java.sql.Ref;
41 import java.sql.ResultSet;
42 import java.sql.SQLException;
43 import java.sql.Time;
44 import java.sql.Timestamp;
45 import java.sql.Types;
46 import java.util.ArrayList;
47 import java.util.Calendar;
48 import java.util.GregorianCalendar;
49 import java.util.TimeZone;
50
51 import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
52 import com.mysql.jdbc.exceptions.MySQLTimeoutException;
53 import com.mysql.jdbc.profiler.ProfilerEvent;
54 import com.mysql.jdbc.profiler.ProfilerEventHandlerFactory;
55
56 /**
57 * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements.
58 *
59 * @author Mark Matthews
60 * @version $Id: ServerPreparedStatement.java,v 1.1.2.2 2005/05/17 14:58:56
61 * mmatthews Exp $
62 */
63 public class ServerPreparedStatement extends PreparedStatement {
64 private static final Constructor JDBC_4_SPS_CTOR;
65
66 static {
67 if (Util.isJdbc4()) {
68 try {
69 JDBC_4_SPS_CTOR = Class.forName("com.mysql.jdbc.JDBC4ServerPreparedStatement")
70 .getConstructor(
71 new Class[] { ConnectionImpl.class, String.class, String.class,
72 Integer.TYPE, Integer.TYPE});
73 } catch (SecurityException e) {
74 throw new RuntimeException(e);
75 } catch (NoSuchMethodException e) {
76 throw new RuntimeException(e);
77 } catch (ClassNotFoundException e) {
78 throw new RuntimeException(e);
79 }
80 } else {
81 JDBC_4_SPS_CTOR = null;
82 }
83 }
84
85 protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192;
86
87 static class BatchedBindValues {
88 BindValue[] batchedParameterValues;
89
90 BatchedBindValues(BindValue[] paramVals) {
91 int numParams = paramVals.length;
92
93 this.batchedParameterValues = new BindValue[numParams];
94
95 for (int i = 0; i < numParams; i++) {
96 this.batchedParameterValues[i] = new BindValue(paramVals[i]);
97 }
98 }
99 }
100
101 public static class BindValue {
102
103 long boundBeforeExecutionNum = 0;
104
105 public long bindLength; /* Default length of data */
106
107 int bufferType; /* buffer type */
108
109 byte byteBinding;
110
111 double doubleBinding;
112
113 float floatBinding;
114
115 int intBinding;
116
117 public boolean isLongData; /* long data indicator */
118
119 public boolean isNull; /* NULL indicator */
120
121 boolean isSet = false; /* has this parameter been set? */
122
123 long longBinding;
124
125 short shortBinding;
126
127 public Object value; /* The value to store */
128
129 BindValue() {
130 }
131
132 BindValue(BindValue copyMe) {
133 this.value = copyMe.value;
134 this.isSet = copyMe.isSet;
135 this.isLongData = copyMe.isLongData;
136 this.isNull = copyMe.isNull;
137 this.bufferType = copyMe.bufferType;
138 this.bindLength = copyMe.bindLength;
139 this.byteBinding = copyMe.byteBinding;
140 this.shortBinding = copyMe.shortBinding;
141 this.intBinding = copyMe.intBinding;
142 this.longBinding = copyMe.longBinding;
143 this.floatBinding = copyMe.floatBinding;
144 this.doubleBinding = copyMe.doubleBinding;
145 }
146
147 void reset() {
148 this.isSet = false;
149 this.value = null;
150 this.isLongData = false;
151
152 this.byteBinding = 0;
153 this.shortBinding = 0;
154 this.intBinding = 0;
155 this.longBinding = 0L;
156 this.floatBinding = 0;
157 this.doubleBinding = 0D;
158 }
159
160 public String toString() {
161 return toString(false);
162 }
163
164 public String toString(boolean quoteIfNeeded) {
165 if (this.isLongData) {
166 return "' STREAM DATA '";
167 }
168
169 switch (this.bufferType) {
170 case MysqlDefs.FIELD_TYPE_TINY:
171 return String.valueOf(byteBinding);
172 case MysqlDefs.FIELD_TYPE_SHORT:
173 return String.valueOf(shortBinding);
174 case MysqlDefs.FIELD_TYPE_LONG:
175 return String.valueOf(intBinding);
176 case MysqlDefs.FIELD_TYPE_LONGLONG:
177 return String.valueOf(longBinding);
178 case MysqlDefs.FIELD_TYPE_FLOAT:
179 return String.valueOf(floatBinding);
180 case MysqlDefs.FIELD_TYPE_DOUBLE:
181 return String.valueOf(doubleBinding);
182 case MysqlDefs.FIELD_TYPE_TIME:
183 case MysqlDefs.FIELD_TYPE_DATE:
184 case MysqlDefs.FIELD_TYPE_DATETIME:
185 case MysqlDefs.FIELD_TYPE_TIMESTAMP:
186 case MysqlDefs.FIELD_TYPE_VAR_STRING:
187 case MysqlDefs.FIELD_TYPE_STRING:
188 case MysqlDefs.FIELD_TYPE_VARCHAR:
189 if (quoteIfNeeded) {
190 return "'" + String.valueOf(value) + "'";
191 } else {
192 return String.valueOf(value);
193 }
194 default:
195 if (value instanceof byte[]) {
196 return "byte data";
197
198 } else {
199 if (quoteIfNeeded) {
200 return "'" + String.valueOf(value) + "'";
201 } else {
202 return String.valueOf(value);
203 }
204 }
205 }
206 }
207
208 long getBoundLength() {
209 if (isNull) {
210 return 0;
211 }
212
213 if (isLongData) {
214 return bindLength;
215 }
216
217 switch (bufferType) {
218
219 case MysqlDefs.FIELD_TYPE_TINY:
220 return 1;
221 case MysqlDefs.FIELD_TYPE_SHORT:
222 return 2;
223 case MysqlDefs.FIELD_TYPE_LONG:
224 return 4;
225 case MysqlDefs.FIELD_TYPE_LONGLONG:
226 return 8;
227 case MysqlDefs.FIELD_TYPE_FLOAT:
228 return 4;
229 case MysqlDefs.FIELD_TYPE_DOUBLE:
230 return 8;
231 case MysqlDefs.FIELD_TYPE_TIME:
232 return 9;
233 case MysqlDefs.FIELD_TYPE_DATE:
234 return 7;
235 case MysqlDefs.FIELD_TYPE_DATETIME:
236 case MysqlDefs.FIELD_TYPE_TIMESTAMP:
237 return 11;
238 case MysqlDefs.FIELD_TYPE_VAR_STRING:
239 case MysqlDefs.FIELD_TYPE_STRING:
240 case MysqlDefs.FIELD_TYPE_VARCHAR:
241 case MysqlDefs.FIELD_TYPE_DECIMAL:
242 case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
243 if (value instanceof byte[]) {
244 return ((byte[]) value).length;
245 } else {
246 return ((String) value).length();
247 }
248 default:
249 return 0;
250 }
251 }
252 }
253
254 /* 1 (length) + 2 (year) + 1 (month) + 1 (day) */
255 private static final byte MAX_DATE_REP_LENGTH = (byte) 5;
256
257 /*
258 * 1 (length) + 2 (year) + 1 (month) + 1 (day) + 1 (hour) + 1 (minute) + 1
259 * (second) + 4 (microseconds)
260 */
261 private static final byte MAX_DATETIME_REP_LENGTH = 12;
262
263 /*
264 * 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) + 1 (minute) + 1
265 * (seconds) + 4 (microseconds)
266 */
267 private static final byte MAX_TIME_REP_LENGTH = 13;
268
269 private boolean hasOnDuplicateKeyUpdate = false;
270
271 private void storeTime(Buffer intoBuf, Time tm) throws SQLException {
272
273 intoBuf.ensureCapacity(9);
274 intoBuf.writeByte((byte) 8); // length
275 intoBuf.writeByte((byte) 0); // neg flag
276 intoBuf.writeLong(0); // tm->day, not used
277
278 Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
279
280 synchronized (sessionCalendar) {
281 java.util.Date oldTime = sessionCalendar.getTime();
282 try {
283 sessionCalendar.setTime(tm);
284 intoBuf.writeByte((byte) sessionCalendar.get(Calendar.HOUR_OF_DAY));
285 intoBuf.writeByte((byte) sessionCalendar.get(Calendar.MINUTE));
286 intoBuf.writeByte((byte) sessionCalendar.get(Calendar.SECOND));
287
288 // intoBuf.writeLongInt(0); // tm-second_part
289 } finally {
290 sessionCalendar.setTime(oldTime);
291 }
292 }
293 }
294
295 /**
296 * Flag indicating whether or not the long parameters have been 'switched'
297 * back to normal parameters. We can not execute() if clearParameters()
298 * hasn't been called in this case.
299 */
300 private boolean detectedLongParameterSwitch = false;
301
302 /**
303 * The number of fields in the result set (if any) for this
304 * PreparedStatement.
305 */
306 private int fieldCount;
307
308 /** Has this prepared statement been marked invalid? */
309 private boolean invalid = false;
310
311 /** If this statement has been marked invalid, what was the reason? */
312 private SQLException invalidationException;
313
314 /** Does this query modify data? */
315 private boolean isSelectQuery;
316
317 private Buffer outByteBuffer;
318
319 /** Bind values for individual fields */
320 private BindValue[] parameterBindings;
321
322 /** Field-level metadata for parameters */
323 private Field[] parameterFields;
324
325 /** Field-level metadata for result sets. */
326 private Field[] resultFields;
327
328 /** Do we need to send/resend types to the server? */
329 private boolean sendTypesToServer = false;
330
331 /** The ID that the server uses to identify this PreparedStatement */
332 private long serverStatementId;
333
334 /** The type used for string bindings, changes from version-to-version */
335 private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
336
337 private boolean serverNeedsResetBeforeEachExecution;
338
339 /**
340 * Creates a prepared statement instance -- We need to provide factory-style
341 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
342 * otherwise the class verifier complains when it tries to load JDBC4-only
343 * interface classes that are present in JDBC4 method signatures.
344 */
345
346 protected static ServerPreparedStatement getInstance(ConnectionImpl conn,
347 String sql, String catalog, int resultSetType,
348 int resultSetConcurrency) throws SQLException {
349 if (!Util.isJdbc4()) {
350 return new ServerPreparedStatement(conn, sql, catalog,
351 resultSetType, resultSetConcurrency);
352 }
353
354 try {
355 return (ServerPreparedStatement) JDBC_4_SPS_CTOR.newInstance(new Object[] { conn,
356 sql, catalog, Constants.integerValueOf(resultSetType),
357 Constants.integerValueOf(resultSetConcurrency) });
358 } catch (IllegalArgumentException e) {
359 throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
360 } catch (InstantiationException e) {
361 throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
362 } catch (IllegalAccessException e) {
363 throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
364 } catch (InvocationTargetException e) {
365 Throwable target = e.getTargetException();
366
367 if (target instanceof SQLException) {
368 throw (SQLException)target;
369 }
370
371 throw new SQLException(target.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
372 }
373 }
374
375 /**
376 * Creates a new ServerPreparedStatement object.
377 *
378 * @param conn
379 * the connection creating us.
380 * @param sql
381 * the SQL containing the statement to prepare.
382 * @param catalog
383 * the catalog in use when we were created.
384 *
385 * @throws SQLException
386 * If an error occurs
387 */
388 protected ServerPreparedStatement(ConnectionImpl conn, String sql, String catalog,
389 int resultSetType, int resultSetConcurrency)
390 throws SQLException {
391 super(conn, catalog);
392
393 checkNullOrEmptyQuery(sql);
394
395 this.hasOnDuplicateKeyUpdate = containsOnDuplicateKeyInString(sql);
396
397 int startOfStatement = findStartOfStatement(sql);
398
399 this.firstCharOfStmt = StringUtils.firstAlphaCharUc(sql, startOfStatement);
400
401 this.isSelectQuery = 'S' == this.firstCharOfStmt;
402
403 if (this.connection.versionMeetsMinimum(5, 0, 0)) {
404 this.serverNeedsResetBeforeEachExecution =
405 !this.connection.versionMeetsMinimum(5, 0, 3);
406 } else {
407 this.serverNeedsResetBeforeEachExecution =
408 !this.connection.versionMeetsMinimum(4, 1, 10);
409 }
410
411 this.useAutoSlowLog = this.connection.getAutoSlowLog();
412 this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
413 this.hasLimitClause = (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1); //$NON-NLS-1$
414
415 String statementComment = this.connection.getStatementComment();
416
417 this.originalSql = (statementComment == null) ? sql : "/* "
418 + statementComment + " */ " + sql;
419
420 if (this.connection.versionMeetsMinimum(4, 1, 2)) {
421 this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING;
422 } else {
423 this.stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
424 }
425
426 try {
427 serverPrepare(sql);
428 } catch (SQLException sqlEx) {
429 realClose(false, true);
430 // don't wrap SQLExceptions
431 throw sqlEx;
432 } catch (Exception ex) {
433 realClose(false, true);
434
435 SQLException sqlEx = SQLError.createSQLException(ex.toString(),
436 SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
437 sqlEx.initCause(ex);
438
439 throw sqlEx;
440 }
441
442 setResultSetType(resultSetType);
443 setResultSetConcurrency(resultSetConcurrency);
444
445 this.parameterTypes = new int[this.parameterCount];
446 }
447
448 /**
449 * JDBC 2.0 Add a set of parameters to the batch.
450 *
451 * @exception SQLException
452 * if a database-access error occurs.
453 *
454 * @see StatementImpl#addBatch
455 */
456 public synchronized void addBatch() throws SQLException {
457 checkClosed();
458
459 if (this.batchedArgs == null) {
460 this.batchedArgs = new ArrayList();
461 }
462
463 this.batchedArgs.add(new BatchedBindValues(this.parameterBindings));
464 }
465
466 protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
467
468 if (this.isClosed) {
469 return "statement has been closed, no further internal information available";
470 }
471
472 PreparedStatement pStmtForSub = null;
473
474 try {
475 pStmtForSub = PreparedStatement.getInstance(this.connection,
476 this.originalSql, this.currentCatalog);
477
478 int numParameters = pStmtForSub.parameterCount;
479 int ourNumParameters = this.parameterCount;
480
481 for (int i = 0; (i < numParameters) && (i < ourNumParameters); i++) {
482 if (this.parameterBindings[i] != null) {
483 if (this.parameterBindings[i].isNull) {
484 pStmtForSub.setNull(i + 1, Types.NULL);
485 } else {
486 BindValue bindValue = this.parameterBindings[i];
487
488 //
489 // Handle primitives first
490 //
491 switch (bindValue.bufferType) {
492
493 case MysqlDefs.FIELD_TYPE_TINY:
494 pStmtForSub.setByte(i + 1, bindValue.byteBinding);
495 break;
496 case MysqlDefs.FIELD_TYPE_SHORT:
497 pStmtForSub.setShort(i + 1, bindValue.shortBinding);
498 break;
499 case MysqlDefs.FIELD_TYPE_LONG:
500 pStmtForSub.setInt(i + 1, bindValue.intBinding);
501 break;
502 case MysqlDefs.FIELD_TYPE_LONGLONG:
503 pStmtForSub.setLong(i + 1, bindValue.longBinding);
504 break;
505 case MysqlDefs.FIELD_TYPE_FLOAT:
506 pStmtForSub.setFloat(i + 1, bindValue.floatBinding);
507 break;
508 case MysqlDefs.FIELD_TYPE_DOUBLE:
509 pStmtForSub.setDouble(i + 1,
510 bindValue.doubleBinding);
511 break;
512 default:
513 pStmtForSub.setObject(i + 1,
514 this.parameterBindings[i].value);
515 break;
516 }
517 }
518 }
519 }
520
521 return pStmtForSub.asSql(quoteStreamsAndUnknowns);
522 } finally {
523 if (pStmtForSub != null) {
524 try {
525 pStmtForSub.close();
526 } catch (SQLException sqlEx) {
527 ; // ignore
528 }
529 }
530 }
531 }
532
533 /*
534 * (non-Javadoc)
535 *
536 * @see com.mysql.jdbc.Statement#checkClosed()
537 */
538 protected void checkClosed() throws SQLException {
539 if (this.invalid) {
540 throw this.invalidationException;
541 }
542
543 super.checkClosed();
544 }
545
546 /**
547 * @see java.sql.PreparedStatement#clearParameters()
548 */
549 public void clearParameters() throws SQLException {
550 checkClosed();
551 clearParametersInternal(true);
552 }
553
554 private void clearParametersInternal(boolean clearServerParameters)
555 throws SQLException {
556 boolean hadLongData = false;
557
558 if (this.parameterBindings != null) {
559 for (int i = 0; i < this.parameterCount; i++) {
560 if ((this.parameterBindings[i] != null)
561 && this.parameterBindings[i].isLongData) {
562 hadLongData = true;
563 }
564
565 this.parameterBindings[i].reset();
566 }
567 }
568
569 if (clearServerParameters && hadLongData) {
570 serverResetStatement();
571
572 this.detectedLongParameterSwitch = false;
573 }
574 }
575
576 protected boolean isCached = false;
577
578 private boolean useAutoSlowLog;
579
580 private Calendar serverTzCalendar;
581
582 private Calendar defaultTzCalendar;
583
584 protected void setClosed(boolean flag) {
585 this.isClosed = flag;
586 }
587
588 /**
589 * @see java.sql.Statement#close()
590 */
591 public synchronized void close() throws SQLException {
592 if (this.isCached && !this.isClosed) {
593 clearParameters();
594
595 this.isClosed = true;
596
597 this.connection.recachePreparedStatement(this);
598 return;
599 }
600
601 realClose(true, true);
602 }
603
604 private void dumpCloseForTestcase() {
605 StringBuffer buf = new StringBuffer();
606 this.connection.generateConnectionCommentBlock(buf);
607 buf.append("DEALLOCATE PREPARE debug_stmt_");
608 buf.append(this.statementId);
609 buf.append(";\n");
610
611 this.connection.dumpTestcaseQuery(buf.toString());
612 }
613
614 private void dumpExecuteForTestcase() throws SQLException {
615 StringBuffer buf = new StringBuffer();
616
617 for (int i = 0; i < this.parameterCount; i++) {
618 this.connection.generateConnectionCommentBlock(buf);
619
620 buf.append("SET @debug_stmt_param");
621 buf.append(this.statementId);
622 buf.append("_");
623 buf.append(i);
624 buf.append("=");
625
626 if (this.parameterBindings[i].isNull) {
627 buf.append("NULL");
628 } else {
629 buf.append(this.parameterBindings[i].toString(true));
630 }
631
632 buf.append(";\n");
633 }
634
635 this.connection.generateConnectionCommentBlock(buf);
636
637 buf.append("EXECUTE debug_stmt_");
638 buf.append(this.statementId);
639
640 if (this.parameterCount > 0) {
641 buf.append(" USING ");
642 for (int i = 0; i < this.parameterCount; i++) {
643 if (i > 0) {
644 buf.append(", ");
645 }
646
647 buf.append("@debug_stmt_param");
648 buf.append(this.statementId);
649 buf.append("_");
650 buf.append(i);
651
652 }
653 }
654
655 buf.append(";\n");
656
657 this.connection.dumpTestcaseQuery(buf.toString());
658 }
659
660 private void dumpPrepareForTestcase() throws SQLException {
661
662 StringBuffer buf = new StringBuffer(this.originalSql.length() + 64);
663
664 this.connection.generateConnectionCommentBlock(buf);
665
666 buf.append("PREPARE debug_stmt_");
667 buf.append(this.statementId);
668 buf.append(" FROM \"");
669 buf.append(this.originalSql);
670 buf.append("\";\n");
671
672 this.connection.dumpTestcaseQuery(buf.toString());
673 }
674
675 protected int[] executeBatchSerially(int batchTimeout) throws SQLException {
676 ConnectionImpl locallyScopedConn = this.connection;
677
678 if (locallyScopedConn == null) {
679 checkClosed();
680 }
681
682 if (locallyScopedConn.isReadOnly()) {
683 throw SQLError.createSQLException(Messages
684 .getString("ServerPreparedStatement.2") //$NON-NLS-1$
685 + Messages.getString("ServerPreparedStatement.3"), //$NON-NLS-1$
686 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
687 }
688
689 checkClosed();
690
691 synchronized (locallyScopedConn.getMutex()) {
692 clearWarnings();
693
694 // Store this for later, we're going to 'swap' them out
695 // as we execute each batched statement...
696 BindValue[] oldBindValues = this.parameterBindings;
697
698 try {
699 int[] updateCounts = null;
700
701 if (this.batchedArgs != null) {
702 int nbrCommands = this.batchedArgs.size();
703 updateCounts = new int[nbrCommands];
704
705 if (this.retrieveGeneratedKeys) {
706 this.batchedGeneratedKeys = new ArrayList(nbrCommands);
707 }
708
709 for (int i = 0; i < nbrCommands; i++) {
710 updateCounts[i] = -3;
711 }
712
713 SQLException sqlEx = null;
714
715 int commandIndex = 0;
716
717 BindValue[] previousBindValuesForBatch = null;
718
719 CancelTask timeoutTask = null;
720
721 try {
722 if (locallyScopedConn.getEnableQueryTimeouts() &&
723 batchTimeout != 0
724 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
725 timeoutTask = new CancelTask(this);
726 locallyScopedConn.getCancelTimer().schedule(timeoutTask,
727 batchTimeout);
728 }
729
730 for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
731 Object arg = this.batchedArgs.get(commandIndex);
732
733 if (arg instanceof String) {
734 updateCounts[commandIndex] = executeUpdate((String) arg);
735 } else {
736 this.parameterBindings = ((BatchedBindValues) arg).batchedParameterValues;
737
738 try {
739 // We need to check types each time, as
740 // the user might have bound different
741 // types in each addBatch()
742
743 if (previousBindValuesForBatch != null) {
744 for (int j = 0; j < this.parameterBindings.length; j++) {
745 if (this.parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) {
746 this.sendTypesToServer = true;
747
748 break;
749 }
750 }
751 }
752
753 try {
754 updateCounts[commandIndex] = executeUpdate(false, true);
755 } finally {
756 previousBindValuesForBatch = this.parameterBindings;
757 }
758
759 if (this.retrieveGeneratedKeys) {
760 java.sql.ResultSet rs = null;
761
762 try {
763 // we don't want to use our version,
764 // because we've altered the behavior of
765 // ours to support batch updates
766 // (catch-22)
767 // Ideally, what we need here is
768 // super.super.getGeneratedKeys()
769 // but that construct doesn't exist in
770 // Java, so that's why there's
771 // this kludge.
772 rs = getGeneratedKeysInternal();
773
774 while (rs.next()) {
775 this.batchedGeneratedKeys
776 .add(new ByteArrayRow(new byte[][] { rs
777 .getBytes(1) }, getExceptionInterceptor()));
778 }
779 } finally {
780 if (rs != null) {
781 rs.close();
782 }
783 }
784 }
785 } catch (SQLException ex) {
786 updateCounts[commandIndex] = EXECUTE_FAILED;
787
788 if (this.continueBatchOnError &&
789 !(ex instanceof MySQLTimeoutException) &&
790 !(ex instanceof MySQLStatementCancelledException)
791 && !hasDeadlockOrTimeoutRolledBackTx(ex)) {
792 sqlEx = ex;
793 } else {
794 int[] newUpdateCounts = new int[commandIndex];
795 System.arraycopy(updateCounts, 0,
796 newUpdateCounts, 0, commandIndex);
797
798 throw new java.sql.BatchUpdateException(ex
799 .getMessage(), ex.getSQLState(), ex
800 .getErrorCode(), newUpdateCounts);
801 }
802 }
803 }
804 }
805 } finally {
806 if (timeoutTask != null) {
807 timeoutTask.cancel();
808 }
809
810 resetCancelledState();
811 }
812
813 if (sqlEx != null) {
814 throw new java.sql.BatchUpdateException(sqlEx
815 .getMessage(), sqlEx.getSQLState(), sqlEx
816 .getErrorCode(), updateCounts);
817 }
818 }
819
820 return (updateCounts != null) ? updateCounts : new int[0];
821 } finally {
822 this.parameterBindings = oldBindValues;
823 this.sendTypesToServer = true;
824
825 clearBatch();
826 }
827 }
828 }
829
830 /**
831 * @see com.mysql.jdbc.PreparedStatement#executeInternal(int,
832 * com.mysql.jdbc.Buffer, boolean, boolean)
833 */
834 protected com.mysql.jdbc.ResultSetInternalMethods executeInternal(int maxRowsToRetrieve,
835 Buffer sendPacket, boolean createStreamingResultSet,
836 boolean queryIsSelectOnly, Field[] metadataFromCache,
837 boolean isBatch)
838 throws SQLException {
839 this.numberOfExecutions++;
840
841 // We defer to server-side execution
842 try {
843 return serverExecute(maxRowsToRetrieve, createStreamingResultSet,
844 metadataFromCache);
845 } catch (SQLException sqlEx) {
846 // don't wrap SQLExceptions
847 if (this.connection.getEnablePacketDebug()) {
848 this.connection.getIO().dumpPacketRingBuffer();
849 }
850
851 if (this.connection.getDumpQueriesOnException()) {
852 String extractedSql = toString();
853 StringBuffer messageBuf = new StringBuffer(extractedSql
854 .length() + 32);
855 messageBuf
856 .append("\n\nQuery being executed when exception was thrown:\n\n");
857 messageBuf.append(extractedSql);
858
859 sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf
860 .toString(), getExceptionInterceptor());
861 }
862
863 throw sqlEx;
864 } catch (Exception ex) {
865 if (this.connection.getEnablePacketDebug()) {
866 this.connection.getIO().dumpPacketRingBuffer();
867 }
868
869 SQLException sqlEx = SQLError.createSQLException(ex.toString(),
870 SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
871
872 if (this.connection.getDumpQueriesOnException()) {
873 String extractedSql = toString();
874 StringBuffer messageBuf = new StringBuffer(extractedSql
875 .length() + 32);
876 messageBuf
877 .append("\n\nQuery being executed when exception was thrown:\n\n");
878 messageBuf.append(extractedSql);
879
880 sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf
881 .toString(), getExceptionInterceptor());
882 }
883
884 sqlEx.initCause(ex);
885
886 throw sqlEx;
887 }
888 }
889
890 /**
891 * @see com.mysql.jdbc.PreparedStatement#fillSendPacket()
892 */
893 protected Buffer fillSendPacket() throws SQLException {
894 return null; // we don't use this type of packet
895 }
896
897 /**
898 * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte,
899 * java.io.InputStream, boolean, int)
900 */
901 protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
902 InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
903 int[] batchedStreamLengths) throws SQLException {
904 return null; // we don't use this type of packet
905 }
906
907 /**
908 * Returns the structure representing the value that (can be)/(is)
909 * bound at the given parameter index.
910 *
911 * @param parameterIndex 1-based
912 * @param forLongData is this for a stream?
913 * @return
914 * @throws SQLException
915 */
916 protected BindValue getBinding(int parameterIndex, boolean forLongData)
917 throws SQLException {
918 checkClosed();
919
920 if (this.parameterBindings.length == 0) {
921 throw SQLError.createSQLException(Messages
922 .getString("ServerPreparedStatement.8"), //$NON-NLS-1$
923 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
924 }
925
926 parameterIndex--;
927
928 if ((parameterIndex < 0)
929 || (parameterIndex >= this.parameterBindings.length)) {
930 throw SQLError.createSQLException(Messages
931 .getString("ServerPreparedStatement.9") //$NON-NLS-1$
932 + (parameterIndex + 1)
933 + Messages.getString("ServerPreparedStatement.10") //$NON-NLS-1$
934 + this.parameterBindings.length,
935 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
936 }
937
938 if (this.parameterBindings[parameterIndex] == null) {
939 this.parameterBindings[parameterIndex] = new BindValue();
940 } else {
941 if (this.parameterBindings[parameterIndex].isLongData
942 && !forLongData) {
943 this.detectedLongParameterSwitch = true;
944 }
945 }
946
947 this.parameterBindings[parameterIndex].isSet = true;
948 this.parameterBindings[parameterIndex].boundBeforeExecutionNum = this.numberOfExecutions;
949
950 return this.parameterBindings[parameterIndex];
951 }
952
953 /**
954 * @see com.mysql.jdbc.PreparedStatement#getBytes(int)
955 */
956 byte[] getBytes(int parameterIndex) throws SQLException {
957 BindValue bindValue = getBinding(parameterIndex, false);
958
959 if (bindValue.isNull) {
960 return null;
961 } else if (bindValue.isLongData) {
962 throw SQLError.notImplemented();
963 } else {
964 if (this.outByteBuffer == null) {
965 this.outByteBuffer = new Buffer(this.connection
966 .getNetBufferLength());
967 }
968
969 this.outByteBuffer.clear();
970
971 int originalPosition = this.outByteBuffer.getPosition();
972
973 storeBinding(this.outByteBuffer, bindValue, this.connection.getIO());
974
975 int newPosition = this.outByteBuffer.getPosition();
976
977 int length = newPosition - originalPosition;
978
979 byte[] valueAsBytes = new byte[length];
980
981 System.arraycopy(this.outByteBuffer.getByteBuffer(),
982 originalPosition, valueAsBytes, 0, length);
983
984 return valueAsBytes;
985 }
986 }
987
988 /**
989 * @see java.sql.PreparedStatement#getMetaData()
990 */
991 public java.sql.ResultSetMetaData getMetaData() throws SQLException {
992 checkClosed();
993
994 if (this.resultFields == null) {
995 return null;
996 }
997
998 return new ResultSetMetaData(this.resultFields,
999 this.connection.getUseOldAliasMetadataBehavior(), getExceptionInterceptor());
1000 }
1001
1002 /**
1003 * @see java.sql.PreparedStatement#getParameterMetaData()
1004 */
1005 public ParameterMetaData getParameterMetaData() throws SQLException {
1006 checkClosed();
1007
1008 if (this.parameterMetaData == null) {
1009 this.parameterMetaData = new MysqlParameterMetadata(
1010 this.parameterFields, this.parameterCount, getExceptionInterceptor());
1011 }
1012
1013 return this.parameterMetaData;
1014 }
1015
1016 /**
1017 * @see com.mysql.jdbc.PreparedStatement#isNull(int)
1018 */
1019 boolean isNull(int paramIndex) {
1020 throw new IllegalArgumentException(Messages
1021 .getString("ServerPreparedStatement.7")); //$NON-NLS-1$
1022 }
1023
1024 /**
1025 * Closes this connection and frees all resources.
1026 *
1027 * @param calledExplicitly
1028 * was this called from close()?
1029 *
1030 * @throws SQLException
1031 * if an error occurs
1032 */
1033 protected void realClose(boolean calledExplicitly,
1034 boolean closeOpenResults) throws SQLException {
1035 if (this.isClosed) {
1036 return;
1037 }
1038
1039 if (this.connection != null) {
1040 if (this.connection.getAutoGenerateTestcaseScript()) {
1041 dumpCloseForTestcase();
1042 }
1043
1044 //
1045 // Don't communicate with the server if we're being
1046 // called from the finalizer...
1047 //
1048 // This will leak server resources, but if we don't do this,
1049 // we'll deadlock (potentially, because there's no guarantee
1050 // when, what order, and what concurrency finalizers will be
1051 // called with). Well-behaved programs won't rely on finalizers
1052 // to clean up their statements.
1053 //
1054
1055 SQLException exceptionDuringClose = null;
1056
1057 if (calledExplicitly && !this.connection.isClosed()) {
1058 synchronized (this.connection.getMutex()) {
1059 try {
1060
1061 MysqlIO mysql = this.connection.getIO();
1062
1063 Buffer packet = mysql.getSharedSendPacket();
1064
1065 packet.writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT);
1066 packet.writeLong(this.serverStatementId);
1067
1068 mysql.sendCommand(MysqlDefs.COM_CLOSE_STATEMENT, null,
1069 packet, true, null, 0);
1070 } catch (SQLException sqlEx) {
1071 exceptionDuringClose = sqlEx;
1072 }
1073 }
1074 }
1075
1076 super.realClose(calledExplicitly, closeOpenResults);
1077
1078 clearParametersInternal(false);
1079 this.parameterBindings = null;
1080
1081 this.parameterFields = null;
1082 this.resultFields = null;
1083
1084 if (exceptionDuringClose != null) {
1085 throw exceptionDuringClose;
1086 }
1087 }
1088 }
1089
1090 /**
1091 * Used by Connection when auto-reconnecting to retrieve 'lost' prepared
1092 * statements.
1093 *
1094 * @throws SQLException
1095 * if an error occurs.
1096 */
1097 protected void rePrepare() throws SQLException {
1098 this.invalidationException = null;
1099
1100 try {
1101 serverPrepare(this.originalSql);
1102 } catch (SQLException sqlEx) {
1103 // don't wrap SQLExceptions
1104 this.invalidationException = sqlEx;
1105 } catch (Exception ex) {
1106 this.invalidationException = SQLError.createSQLException(ex.toString(),
1107 SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
1108 this.invalidationException.initCause(ex);
1109 }
1110
1111 if (this.invalidationException != null) {
1112 this.invalid = true;
1113
1114 this.parameterBindings = null;
1115
1116 this.parameterFields = null;
1117 this.resultFields = null;
1118
1119 if (this.results != null) {
1120 try {
1121 this.results.close();
1122 } catch (Exception ex) {
1123 ;
1124 }
1125 }
1126
1127 if (this.connection != null) {
1128 if (this.maxRowsChanged) {
1129 this.connection.unsetMaxRows(this);
1130 }
1131
1132 if (!this.connection.getDontTrackOpenResources()) {
1133 this.connection.unregisterStatement(this);
1134 }
1135 }
1136 }
1137 }
1138
1139 /**
1140 * Tells the server to execute this prepared statement with the current
1141 * parameter bindings.
1142 *
1143 * <pre>
1144 *
1145 *
1146 * - Server gets the command 'COM_EXECUTE' to execute the
1147 * previously prepared query. If there is any param markers;
1148 * then client will send the data in the following format:
1149 *
1150 * [COM_EXECUTE:1]
1151 * [STMT_ID:4]
1152 * [NULL_BITS:(param_count+7)/8)]
1153 * [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
1154 * [[length]data]
1155 * [[length]data] .. [[length]data].
1156 *
1157 * (Note: Except for string/binary types; all other types will not be
1158 * supplied with length field)
1159 *
1160 *
1161 * </pre>
1162 *
1163 * @param maxRowsToRetrieve
1164 * DOCUMENT ME!
1165 * @param createStreamingResultSet
1166 * DOCUMENT ME!
1167 *
1168 * @return DOCUMENT ME!
1169 *
1170 * @throws SQLException
1171 */
1172 private com.mysql.jdbc.ResultSetInternalMethods serverExecute(int maxRowsToRetrieve,
1173 boolean createStreamingResultSet,
1174 Field[] metadataFromCache) throws SQLException {
1175 synchronized (this.connection.getMutex()) {
1176 MysqlIO mysql = this.connection.getIO();
1177
1178 if (mysql.shouldIntercept()) {
1179 ResultSetInternalMethods interceptedResults =
1180 mysql.invokeStatementInterceptorsPre(this.originalSql, this, true);
1181
1182 if (interceptedResults != null) {
1183 return interceptedResults;
1184 }
1185 }
1186
1187 if (this.detectedLongParameterSwitch) {
1188 // Check when values were bound
1189 boolean firstFound = false;
1190 long boundTimeToCheck = 0;
1191
1192 for (int i = 0; i < this.parameterCount - 1; i++) {
1193 if (this.parameterBindings[i].isLongData) {
1194 if (firstFound && boundTimeToCheck !=
1195 this.parameterBindings[i].boundBeforeExecutionNum) {
1196 throw SQLError.createSQLException(Messages
1197 .getString("ServerPreparedStatement.11") //$NON-NLS-1$
1198 + Messages.getString("ServerPreparedStatement.12"), //$NON-NLS-1$
1199 SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
1200 } else {
1201 firstFound = true;
1202 boundTimeToCheck = this.parameterBindings[i].boundBeforeExecutionNum;
1203 }
1204 }
1205 }
1206
1207 // Okay, we've got all "newly"-bound streams, so reset
1208 // server-side state to clear out previous bindings
1209
1210 serverResetStatement();
1211 }
1212
1213
1214 // Check bindings
1215 for (int i = 0; i < this.parameterCount; i++) {
1216 if (!this.parameterBindings[i].isSet) {
1217 throw SQLError.createSQLException(Messages
1218 .getString("ServerPreparedStatement.13") + (i + 1) //$NON-NLS-1$
1219 + Messages.getString("ServerPreparedStatement.14"),
1220 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
1221 }
1222 }
1223
1224 //
1225 // Send all long data
1226 //
1227 for (int i = 0; i < this.parameterCount; i++) {
1228 if (this.parameterBindings[i].isLongData) {
1229 serverLongData(i, this.parameterBindings[i]);
1230 }
1231 }
1232
1233 if (this.connection.getAutoGenerateTestcaseScript()) {
1234 dumpExecuteForTestcase();
1235 }
1236
1237 //
1238 // store the parameter values
1239 //
1240
1241 Buffer packet = mysql.getSharedSendPacket();
1242
1243 packet.clear();
1244 packet.writeByte((byte) MysqlDefs.COM_EXECUTE);
1245 packet.writeLong(this.serverStatementId);
1246
1247 boolean usingCursor = false;
1248
1249 if (this.connection.versionMeetsMinimum(4, 1, 2)) {
1250 // we only create cursor-backed result sets if
1251 // a) The query is a SELECT
1252 // b) The server supports it
1253 // c) We know it is forward-only (note this doesn't
1254 // preclude updatable result sets)
1255 // d) The user has set a fetch size
1256 if (this.resultFields != null &&
1257 this.connection.isCursorFetchEnabled()
1258 && getResultSetType() == ResultSet.TYPE_FORWARD_ONLY
1259 && getResultSetConcurrency() == ResultSet.CONCUR_READ_ONLY
1260 && getFetchSize() > 0) {
1261 packet.writeByte(MysqlDefs.OPEN_CURSOR_FLAG);
1262 usingCursor = true;
1263 } else {
1264 packet.writeByte((byte) 0); // placeholder for flags
1265 }
1266
1267 packet.writeLong(1); // placeholder for parameter
1268 // iterations
1269 }
1270
1271 /* Reserve place for null-marker bytes */
1272 int nullCount = (this.parameterCount + 7) / 8;
1273
1274 // if (mysql.versionMeetsMinimum(4, 1, 2)) {
1275 // nullCount = (this.parameterCount + 9) / 8;
1276 // }
1277 int nullBitsPosition = packet.getPosition();
1278
1279 for (int i = 0; i < nullCount; i++) {
1280 packet.writeByte((byte) 0);
1281 }
1282
1283 byte[] nullBitsBuffer = new byte[nullCount];
1284
1285 /* In case if buffers (type) altered, indicate to server */
1286 packet.writeByte(this.sendTypesToServer ? (byte) 1 : (byte) 0);
1287
1288 if (this.sendTypesToServer) {
1289 /*
1290 * Store types of parameters in first in first package that is
1291 * sent to the server.
1292 */
1293 for (int i = 0; i < this.parameterCount; i++) {
1294 packet.writeInt(this.parameterBindings[i].bufferType);
1295 }
1296 }
1297
1298 //
1299 // store the parameter values
1300 //
1301 for (int i = 0; i < this.parameterCount; i++) {
1302 if (!this.parameterBindings[i].isLongData) {
1303 if (!this.parameterBindings[i].isNull) {
1304 storeBinding(packet, this.parameterBindings[i], mysql);
1305 } else {
1306 nullBitsBuffer[i / 8] |= (1 << (i & 7));
1307 }
1308 }
1309 }
1310
1311 //
1312 // Go back and write the NULL flags
1313 // to the beginning of the packet
1314 //
1315 int endPosition = packet.getPosition();
1316 packet.setPosition(nullBitsPosition);
1317 packet.writeBytesNoNull(nullBitsBuffer);
1318 packet.setPosition(endPosition);
1319
1320 long begin = 0;
1321
1322 boolean logSlowQueries = this.connection.getLogSlowQueries();
1323 boolean gatherPerformanceMetrics = this.connection
1324 .getGatherPerformanceMetrics();
1325
1326 if (this.profileSQL || logSlowQueries || gatherPerformanceMetrics) {
1327 begin = mysql.getCurrentTimeNanosOrMillis();
1328 }
1329
1330 resetCancelledState();
1331
1332 CancelTask timeoutTask = null;
1333
1334 try {
1335 if (this.connection.getEnableQueryTimeouts() &&
1336 this.timeoutInMillis != 0
1337 && this.connection.versionMeetsMinimum(5, 0, 0)) {
1338 timeoutTask = new CancelTask(this);
1339 this.connection.getCancelTimer().schedule(timeoutTask,
1340 this.timeoutInMillis);
1341 }
1342
1343 Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE,
1344 null, packet, false, null, 0);
1345
1346 long queryEndTime = 0L;
1347
1348 if (logSlowQueries || gatherPerformanceMetrics || this.profileSQL) {
1349 queryEndTime = mysql.getCurrentTimeNanosOrMillis();
1350 }
1351
1352 if (timeoutTask != null) {
1353 timeoutTask.cancel();
1354
1355 if (timeoutTask.caughtWhileCancelling != null) {
1356 throw timeoutTask.caughtWhileCancelling;
1357 }
1358
1359 timeoutTask = null;
1360 }
1361
1362 synchronized (this.cancelTimeoutMutex) {
1363 if (this.wasCancelled) {
1364 SQLException cause = null;
1365
1366 if (this.wasCancelledByTimeout) {
1367 cause = new MySQLTimeoutException();
1368 } else {
1369 cause = new MySQLStatementCancelledException();
1370 }
1371
1372 resetCancelledState();
1373
1374 throw cause;
1375 }
1376 }
1377
1378 boolean queryWasSlow = false;
1379
1380 if (logSlowQueries || gatherPerformanceMetrics) {
1381 long elapsedTime = queryEndTime - begin;
1382
1383 if (logSlowQueries) {
1384 if (this.useAutoSlowLog) {
1385 queryWasSlow = elapsedTime > this.connection.getSlowQueryThresholdMillis();
1386 } else {
1387 queryWasSlow = this.connection.isAbonormallyLongQuery(elapsedTime);
1388
1389 this.connection.reportQueryTime(elapsedTime);
1390 }
1391 }
1392
1393 if (queryWasSlow) {
1394
1395 StringBuffer mesgBuf = new StringBuffer(
1396 48 + this.originalSql.length());
1397 mesgBuf.append(Messages
1398 .getString("ServerPreparedStatement.15")); //$NON-NLS-1$
1399 mesgBuf.append(mysql.getSlowQueryThreshold());
1400 mesgBuf.append(Messages
1401 .getString("ServerPreparedStatement.15a")); //$NON-NLS-1$
1402 mesgBuf.append(elapsedTime);
1403 mesgBuf.append(Messages
1404 .getString("ServerPreparedStatement.16")); //$NON-NLS-1$
1405
1406 mesgBuf.append("as prepared: ");
1407 mesgBuf.append(this.originalSql);
1408 mesgBuf.append("\n\n with parameters bound:\n\n");
1409 mesgBuf.append(asSql(true));
1410
1411 this.eventSink
1412 .consumeEvent(new ProfilerEvent(
1413 ProfilerEvent.TYPE_SLOW_QUERY,
1414 "", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$
1415 getId(), 0, System.currentTimeMillis(),
1416 elapsedTime, mysql
1417 .getQueryTimingUnits(), null,
1418 new Throwable(), mesgBuf.toString()));
1419 }
1420
1421 if (gatherPerformanceMetrics) {
1422 this.connection.registerQueryExecutionTime(elapsedTime);
1423 }
1424 }
1425
1426 this.connection.incrementNumberOfPreparedExecutes();
1427
1428 if (this.profileSQL) {
1429 this.eventSink = ProfilerEventHandlerFactory
1430 .getInstance(this.connection);
1431
1432 this.eventSink.consumeEvent(new ProfilerEvent(
1433 ProfilerEvent.TYPE_EXECUTE,
1434 "", this.currentCatalog, //$NON-NLS-1$
1435 this.connectionId, this.statementId, -1, System
1436 .currentTimeMillis(), (int) (mysql
1437 .getCurrentTimeNanosOrMillis() - begin),
1438 mysql.getQueryTimingUnits(), null, new Throwable(),
1439 truncateQueryToLog(asSql(true))));
1440 }
1441
1442 com.mysql.jdbc.ResultSetInternalMethods rs = mysql.readAllResults(this,
1443 maxRowsToRetrieve, this.resultSetType,
1444 this.resultSetConcurrency, createStreamingResultSet,
1445 this.currentCatalog, resultPacket, true, this.fieldCount,
1446 metadataFromCache);
1447
1448 if (mysql.shouldIntercept()) {
1449 ResultSetInternalMethods interceptedResults =
1450 mysql.invokeStatementInterceptorsPost(this.originalSql, this, rs, true, null);
1451
1452 if (interceptedResults != null) {
1453 rs = interceptedResults;
1454 }
1455 }
1456
1457 if (this.profileSQL) {
1458 long fetchEndTime = mysql.getCurrentTimeNanosOrMillis();
1459
1460 this.eventSink.consumeEvent(new ProfilerEvent(
1461 ProfilerEvent.TYPE_FETCH,
1462 "", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$
1463 getId(), 0 /* FIXME rs.resultId */, System.currentTimeMillis(),
1464 (fetchEndTime - queryEndTime), mysql
1465 .getQueryTimingUnits(), null,
1466 new Throwable(), null));
1467 }
1468
1469 if (queryWasSlow && this.connection.getExplainSlowQueries()) {
1470 String queryAsString = asSql(true);
1471
1472 mysql.explainSlowQuery(queryAsString.getBytes(),
1473 queryAsString);
1474 }
1475
1476 if (!createStreamingResultSet &&
1477 this.serverNeedsResetBeforeEachExecution) {
1478 serverResetStatement(); // clear any long data...
1479 }
1480
1481
1482 this.sendTypesToServer = false;
1483 this.results = rs;
1484
1485 if (mysql.hadWarnings()) {
1486 mysql.scanForAndThrowDataTruncation();
1487 }
1488
1489 return rs;
1490 } catch (SQLException sqlEx) {
1491 if (mysql.shouldIntercept()) {
1492 mysql.invokeStatementInterceptorsPost(this.originalSql, this, null, true, sqlEx);
1493 }
1494
1495 throw sqlEx;
1496 } finally {
1497 if (timeoutTask != null) {
1498 timeoutTask.cancel();
1499 }
1500 }
1501 }
1502 }
1503
1504 /**
1505 * Sends stream-type data parameters to the server.
1506 *
1507 * <pre>
1508 *
1509 * Long data handling:
1510 *
1511 * - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
1512 * - The packet recieved will have the format as:
1513 * [COM_LONG_DATA: 1][STMT_ID:4][parameter_number:2][type:2][data]
1514 * - Checks if the type is specified by client, and if yes reads the type,
1515 * and stores the data in that format.
1516 * - It's up to the client to check for read data ended. The server doesn't
1517 * care; and also server doesn't notify to the client that it got the
1518 * data or not; if there is any error; then during execute; the error
1519 * will be returned
1520 *
1521 * </pre>
1522 *
1523 * @param parameterIndex
1524 * DOCUMENT ME!
1525 * @param longData
1526 * DOCUMENT ME!
1527 *
1528 * @throws SQLException
1529 * if an error occurs.
1530 */
1531 private void serverLongData(int parameterIndex, BindValue longData)
1532 throws SQLException {
1533 synchronized (this.connection.getMutex()) {
1534 MysqlIO mysql = this.connection.getIO();
1535
1536 Buffer packet = mysql.getSharedSendPacket();
1537
1538 Object value = longData.value;
1539
1540 if (value instanceof byte[]) {
1541 packet.clear();
1542 packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
1543 packet.writeLong(this.serverStatementId);
1544 packet.writeInt((parameterIndex));
1545
1546 packet.writeBytesNoNull((byte[]) longData.value);
1547
1548 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
1549 null, 0);
1550 } else if (value instanceof InputStream) {
1551 storeStream(mysql, parameterIndex, packet, (InputStream) value);
1552 } else if (value instanceof java.sql.Blob) {
1553 storeStream(mysql, parameterIndex, packet,
1554 ((java.sql.Blob) value).getBinaryStream());
1555 } else if (value instanceof Reader) {
1556 storeReader(mysql, parameterIndex, packet, (Reader) value);
1557 } else {
1558 throw SQLError.createSQLException(Messages
1559 .getString("ServerPreparedStatement.18") //$NON-NLS-1$
1560 + value.getClass().getName() + "'", //$NON-NLS-1$
1561 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
1562 }
1563 }
1564 }
1565
1566 private void serverPrepare(String sql) throws SQLException {
1567 synchronized (this.connection.getMutex()) {
1568 MysqlIO mysql = this.connection.getIO();
1569
1570 if (this.connection.getAutoGenerateTestcaseScript()) {
1571 dumpPrepareForTestcase();
1572 }
1573
1574 try {
1575 long begin = 0;
1576
1577 if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
1578 this.isLoadDataQuery = true;
1579 } else {
1580 this.isLoadDataQuery = false;
1581 }
1582
1583 if (this.connection.getProfileSql()) {
1584 begin = System.currentTimeMillis();
1585 }
1586
1587 String characterEncoding = null;
1588 String connectionEncoding = this.connection.getEncoding();
1589
1590 if (!this.isLoadDataQuery && this.connection.getUseUnicode()
1591 && (connectionEncoding != null)) {
1592 characterEncoding = connectionEncoding;
1593 }
1594
1595 Buffer prepareResultPacket = mysql.sendCommand(
1596 MysqlDefs.COM_PREPARE, sql, null, false,
1597 characterEncoding, 0);
1598
1599 if (this.connection.versionMeetsMinimum(4, 1, 1)) {
1600 // 4.1.1 and newer use the first byte
1601 // as an 'ok' or 'error' flag, so move
1602 // the buffer pointer past it to
1603 // start reading the statement id.
1604 prepareResultPacket.setPosition(1);
1605 } else {
1606 // 4.1.0 doesn't use the first byte as an
1607 // 'ok' or 'error' flag
1608 prepareResultPacket.setPosition(0);
1609 }
1610
1611 this.serverStatementId = prepareResultPacket.readLong();
1612 this.fieldCount = prepareResultPacket.readInt();
1613 this.parameterCount = prepareResultPacket.readInt();
1614 this.parameterBindings = new BindValue[this.parameterCount];
1615
1616 for (int i = 0; i < this.parameterCount; i++) {
1617 this.parameterBindings[i] = new BindValue();
1618 }
1619
1620 this.connection.incrementNumberOfPrepares();
1621
1622 if (this.profileSQL) {
1623 this.eventSink.consumeEvent(new ProfilerEvent(
1624 ProfilerEvent.TYPE_PREPARE,
1625 "", this.currentCatalog, //$NON-NLS-1$
1626 this.connectionId, this.statementId, -1,
1627 System.currentTimeMillis(),
1628 mysql.getCurrentTimeNanosOrMillis() - begin,
1629 mysql.getQueryTimingUnits(), null,
1630 new Throwable(), truncateQueryToLog(sql)));
1631 }
1632
1633 if (this.parameterCount > 0) {
1634 if (this.connection.versionMeetsMinimum(4, 1, 2)
1635 && !mysql.isVersion(5, 0, 0)) {
1636 this.parameterFields = new Field[this.parameterCount];
1637
1638 Buffer metaDataPacket = mysql.readPacket();
1639
1640 int i = 0;
1641
1642 while (!metaDataPacket.isLastDataPacket()
1643 && (i < this.parameterCount)) {
1644 this.parameterFields[i++] = mysql.unpackField(
1645 metaDataPacket, false);
1646 metaDataPacket = mysql.readPacket();
1647 }
1648 }
1649 }
1650
1651 if (this.fieldCount > 0) {
1652 this.resultFields = new Field[this.fieldCount];
1653
1654 Buffer fieldPacket = mysql.readPacket();
1655
1656 int i = 0;
1657
1658 // Read in the result set column information
1659 while (!fieldPacket.isLastDataPacket()
1660 && (i < this.fieldCount)) {
1661 this.resultFields[i++] = mysql.unpackField(fieldPacket,
1662 false);
1663 fieldPacket = mysql.readPacket();
1664 }
1665 }
1666 } catch (SQLException sqlEx) {
1667 if (this.connection.getDumpQueriesOnException()) {
1668 StringBuffer messageBuf = new StringBuffer(this.originalSql
1669 .length() + 32);
1670 messageBuf
1671 .append("\n\nQuery being prepared when exception was thrown:\n\n");
1672 messageBuf.append(this.originalSql);
1673
1674 sqlEx = ConnectionImpl.appendMessageToException(sqlEx,
1675 messageBuf.toString(), getExceptionInterceptor());
1676 }
1677
1678 throw sqlEx;
1679 } finally {
1680 // Leave the I/O channel in a known state...there might be
1681 // packets out there
1682 // that we're not interested in
1683 this.connection.getIO().clearInputStream();
1684 }
1685 }
1686 }
1687
1688 private String truncateQueryToLog(String sql) {
1689 String query = null;
1690
1691 if (sql.length() > this.connection.getMaxQuerySizeToLog()) {
1692 StringBuffer queryBuf = new StringBuffer(
1693 this.connection.getMaxQuerySizeToLog() + 12);
1694 queryBuf.append(sql.substring(0, this.connection.getMaxQuerySizeToLog()));
1695 queryBuf.append(Messages.getString("MysqlIO.25"));
1696
1697 query = queryBuf.toString();
1698 } else {
1699 query = sql;
1700 }
1701
1702 return query;
1703 }
1704
1705 private void serverResetStatement() throws SQLException {
1706 synchronized (this.connection.getMutex()) {
1707
1708 MysqlIO mysql = this.connection.getIO();
1709
1710 Buffer packet = mysql.getSharedSendPacket();
1711
1712 packet.clear();
1713 packet.writeByte((byte) MysqlDefs.COM_RESET_STMT);
1714 packet.writeLong(this.serverStatementId);
1715
1716 try {
1717 mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null, packet,
1718 !this.connection.versionMeetsMinimum(4, 1, 2), null, 0);
1719 } catch (SQLException sqlEx) {
1720 throw sqlEx;
1721 } catch (Exception ex) {
1722 SQLException sqlEx = SQLError.createSQLException(ex.toString(),
1723 SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
1724 sqlEx.initCause(ex);
1725
1726 throw sqlEx;
1727 } finally {
1728 mysql.clearInputStream();
1729 }
1730 }
1731 }
1732
1733 /**
1734 * @see java.sql.PreparedStatement#setArray(int, java.sql.Array)
1735 */
1736 public void setArray(int i, Array x) throws SQLException {
1737 throw SQLError.notImplemented();
1738 }
1739
1740 /**
1741 * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream,
1742 * int)
1743 */
1744 public void setAsciiStream(int parameterIndex, InputStream x, int length)
1745 throws SQLException {
1746 checkClosed();
1747
1748 if (x == null) {
1749 setNull(parameterIndex, java.sql.Types.BINARY);
1750 } else {
1751 BindValue binding = getBinding(parameterIndex, true);
1752 setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1753
1754 binding.value = x;
1755 binding.isNull = false;
1756 binding.isLongData = true;
1757
1758 if (this.connection.getUseStreamLengthsInPrepStmts()) {
1759 binding.bindLength = length;
1760 } else {
1761 binding.bindLength = -1;
1762 }
1763 }
1764 }
1765
1766 /**
1767 * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal)
1768 */
1769 public void setBigDecimal(int parameterIndex, BigDecimal x)
1770 throws SQLException {
1771 checkClosed();
1772
1773 if (x == null) {
1774 setNull(parameterIndex, java.sql.Types.DECIMAL);
1775 } else {
1776
1777 BindValue binding = getBinding(parameterIndex, false);
1778
1779 if (this.connection.versionMeetsMinimum(5, 0, 3)) {
1780 setType(binding, MysqlDefs.FIELD_TYPE_NEW_DECIMAL);
1781 } else {
1782 setType(binding, this.stringTypeCode);
1783 }
1784
1785 binding.value = StringUtils
1786 .fixDecimalExponent(StringUtils.consistentToString(x));
1787 binding.isNull = false;
1788 binding.isLongData = false;
1789 }
1790 }
1791
1792 /**
1793 * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream,
1794 * int)
1795 */
1796 public void setBinaryStream(int parameterIndex, InputStream x, int length)
1797 throws SQLException {
1798 checkClosed();
1799
1800 if (x == null) {
1801 setNull(parameterIndex, java.sql.Types.BINARY);
1802 } else {
1803 BindValue binding = getBinding(parameterIndex, true);
1804 setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1805
1806 binding.value = x;
1807 binding.isNull = false;
1808 binding.isLongData = true;
1809
1810 if (this.connection.getUseStreamLengthsInPrepStmts()) {
1811 binding.bindLength = length;
1812 } else {
1813 binding.bindLength = -1;
1814 }
1815 }
1816 }
1817
1818 /**
1819 * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob)
1820 */
1821 public void setBlob(int parameterIndex, Blob x) throws SQLException {
1822 checkClosed();
1823
1824 if (x == null) {
1825 setNull(parameterIndex, java.sql.Types.BINARY);
1826 } else {
1827 BindValue binding = getBinding(parameterIndex, true);
1828 setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1829
1830 binding.value = x;
1831 binding.isNull = false;
1832 binding.isLongData = true;
1833
1834 if (this.connection.getUseStreamLengthsInPrepStmts()) {
1835 binding.bindLength = x.length();
1836 } else {
1837 binding.bindLength = -1;
1838 }
1839 }
1840 }
1841
1842 /**
1843 * @see java.sql.PreparedStatement#setBoolean(int, boolean)
1844 */
1845 public void setBoolean(int parameterIndex, boolean x) throws SQLException {
1846 setByte(parameterIndex, (x ? (byte) 1 : (byte) 0));
1847 }
1848
1849 /**
1850 * @see java.sql.PreparedStatement#setByte(int, byte)
1851 */
1852 public void setByte(int parameterIndex, byte x) throws SQLException {
1853 checkClosed();
1854
1855 BindValue binding = getBinding(parameterIndex, false);
1856 setType(binding, MysqlDefs.FIELD_TYPE_TINY);
1857
1858 binding.value = null;
1859 binding.byteBinding = x;
1860 binding.isNull = false;
1861 binding.isLongData = false;
1862 }
1863
1864 /**
1865 * @see java.sql.PreparedStatement#setBytes(int, byte)
1866 */
1867 public void setBytes(int parameterIndex, byte[] x) throws SQLException {
1868 checkClosed();
1869
1870 if (x == null) {
1871 setNull(parameterIndex, java.sql.Types.BINARY);
1872 } else {
1873 BindValue binding = getBinding(parameterIndex, false);
1874 setType(binding, MysqlDefs.FIELD_TYPE_VAR_STRING);
1875
1876 binding.value = x;
1877 binding.isNull = false;
1878 binding.isLongData = false;
1879 }
1880 }
1881
1882 /**
1883 * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader,
1884 * int)
1885 */
1886 public void setCharacterStream(int parameterIndex, Reader reader, int length)
1887 throws SQLException {
1888 checkClosed();
1889
1890 if (reader == null) {
1891 setNull(parameterIndex, java.sql.Types.BINARY);
1892 } else {
1893 BindValue binding = getBinding(parameterIndex, true);
1894 setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1895
1896 binding.value = reader;
1897 binding.isNull = false;
1898 binding.isLongData = true;
1899
1900 if (this.connection.getUseStreamLengthsInPrepStmts()) {
1901 binding.bindLength = length;
1902 } else {
1903 binding.bindLength = -1;
1904 }
1905 }
1906 }
1907
1908 /**
1909 * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob)
1910 */
1911 public void setClob(int parameterIndex, Clob x) throws SQLException {
1912 checkClosed();
1913
1914 if (x == null) {
1915 setNull(parameterIndex, java.sql.Types.BINARY);
1916 } else {
1917 BindValue binding = getBinding(parameterIndex, true);
1918 setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
1919
1920 binding.value = x.getCharacterStream();
1921 binding.isNull = false;
1922 binding.isLongData = true;
1923
1924 if (this.connection.getUseStreamLengthsInPrepStmts()) {
1925 binding.bindLength = x.length();
1926 } else {
1927 binding.bindLength = -1;
1928 }
1929 }
1930 }
1931
1932 /**
1933 * Set a parameter to a java.sql.Date value. The driver converts this to a
1934 * SQL DATE value when it sends it to the database.
1935 *
1936 * @param parameterIndex
1937 * the first parameter is 1, the second is 2, ...
1938 * @param x
1939 * the parameter value
1940 *
1941 * @exception SQLException
1942 * if a database-access error occurs.
1943 */
1944 public void setDate(int parameterIndex, Date x) throws SQLException {
1945 setDate(parameterIndex, x, null);
1946 }
1947
1948 /**
1949 * Set a parameter to a java.sql.Date value. The driver converts this to a
1950 * SQL DATE value when it sends it to the database.
1951 *
1952 * @param parameterIndex
1953 * the first parameter is 1, the second is 2, ...
1954 * @param x
1955 * the parameter value
1956 * @param cal
1957 * the calendar to interpret the date with
1958 *
1959 * @exception SQLException
1960 * if a database-access error occurs.
1961 */
1962 public void setDate(int parameterIndex, Date x, Calendar cal)
1963 throws SQLException {
1964 if (x == null) {
1965 setNull(parameterIndex, java.sql.Types.DATE);
1966 } else {
1967 BindValue binding = getBinding(parameterIndex, false);
1968 setType(binding, MysqlDefs.FIELD_TYPE_DATE);
1969
1970 binding.value = x;
1971 binding.isNull = false;
1972 binding.isLongData = false;
1973 }
1974 }
1975
1976 /**
1977 * @see java.sql.PreparedStatement#setDouble(int, double)
1978 */
1979 public void setDouble(int parameterIndex, double x) throws SQLException {
1980 checkClosed();
1981
1982 if (!this.connection.getAllowNanAndInf()
1983 && (x == Double.POSITIVE_INFINITY
1984 || x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) {
1985 throw SQLError.createSQLException("'" + x
1986 + "' is not a valid numeric or approximate numeric value",
1987 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
1988
1989 }
1990
1991 BindValue binding = getBinding(parameterIndex, false);
1992 setType(binding, MysqlDefs.FIELD_TYPE_DOUBLE);
1993
1994 binding.value = null;
1995 binding.doubleBinding = x;
1996 binding.isNull = false;
1997 binding.isLongData = false;
1998 }
1999
2000 /**
2001 * @see java.sql.PreparedStatement#setFloat(int, float)
2002 */
2003 public void setFloat(int parameterIndex, float x) throws SQLException {
2004 checkClosed();
2005
2006 BindValue binding = getBinding(parameterIndex, false);
2007 setType(binding, MysqlDefs.FIELD_TYPE_FLOAT);
2008
2009 binding.value = null;
2010 binding.floatBinding = x;
2011 binding.isNull = false;
2012 binding.isLongData = false;
2013 }
2014
2015 /**
2016 * @see java.sql.PreparedStatement#setInt(int, int)
2017 */
2018 public void setInt(int parameterIndex, int x) throws SQLException {
2019 checkClosed();
2020
2021 BindValue binding = getBinding(parameterIndex, false);
2022 setType(binding, MysqlDefs.FIELD_TYPE_LONG);
2023
2024 binding.value = null;
2025 binding.intBinding = x;
2026 binding.isNull = false;
2027 binding.isLongData = false;
2028 }
2029
2030 /**
2031 * @see java.sql.PreparedStatement#setLong(int, long)
2032 */
2033 public void setLong(int parameterIndex, long x) throws SQLException {
2034 checkClosed();
2035
2036 BindValue binding = getBinding(parameterIndex, false);
2037 setType(binding, MysqlDefs.FIELD_TYPE_LONGLONG);
2038
2039 binding.value = null;
2040 binding.longBinding = x;
2041 binding.isNull = false;
2042 binding.isLongData = false;
2043 }
2044
2045 /**
2046 * @see java.sql.PreparedStatement#setNull(int, int)
2047 */
2048 public void setNull(int parameterIndex, int sqlType) throws SQLException {
2049 checkClosed();
2050
2051 BindValue binding = getBinding(parameterIndex, false);
2052
2053 //
2054 // Don't re-set types, but use something if this
2055 // parameter was never specified
2056 //
2057 if (binding.bufferType == 0) {
2058 setType(binding, MysqlDefs.FIELD_TYPE_NULL);
2059 }
2060
2061 binding.value = null;
2062 binding.isNull = true;
2063 binding.isLongData = false;
2064 }
2065
2066 /**
2067 * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String)
2068 */
2069 public void setNull(int parameterIndex, int sqlType, String typeName)
2070 throws SQLException {
2071 checkClosed();
2072
2073 BindValue binding = getBinding(parameterIndex, false);
2074
2075 //
2076 // Don't re-set types, but use something if this
2077 // parameter was never specified
2078 //
2079 if (binding.bufferType == 0) {
2080 setType(binding, MysqlDefs.FIELD_TYPE_NULL);
2081 }
2082
2083 binding.value = null;
2084 binding.isNull = true;
2085 binding.isLongData = false;
2086 }
2087
2088 /**
2089 * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref)
2090 */
2091 public void setRef(int i, Ref x) throws SQLException {
2092 throw SQLError.notImplemented();
2093 }
2094
2095 /**
2096 * @see java.sql.PreparedStatement#setShort(int, short)
2097 */
2098 public void setShort(int parameterIndex, short x) throws SQLException {
2099 checkClosed();
2100
2101 BindValue binding = getBinding(parameterIndex, false);
2102 setType(binding, MysqlDefs.FIELD_TYPE_SHORT);
2103
2104 binding.value = null;
2105 binding.shortBinding = x;
2106 binding.isNull = false;
2107 binding.isLongData = false;
2108 }
2109
2110 /**
2111 * @see java.sql.PreparedStatement#setString(int, java.lang.String)
2112 */
2113 public void setString(int parameterIndex, String x) throws SQLException {
2114 checkClosed();
2115
2116 if (x == null) {
2117 setNull(parameterIndex, java.sql.Types.CHAR);
2118 } else {
2119 BindValue binding = getBinding(parameterIndex, false);
2120
2121 setType(binding, this.stringTypeCode);
2122
2123 binding.value = x;
2124 binding.isNull = false;
2125 binding.isLongData = false;
2126 }
2127 }
2128
2129 /**
2130 * Set a parameter to a java.sql.Time value.
2131 *
2132 * @param parameterIndex
2133 * the first parameter is 1...));
2134 * @param x
2135 * the parameter value
2136 *
2137 * @throws SQLException
2138 * if a database access error occurs
2139 */
2140 public void setTime(int parameterIndex, java.sql.Time x)
2141 throws SQLException {
2142 setTimeInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false);
2143 }
2144
2145 /**
2146 * Set a parameter to a java.sql.Time value. The driver converts this to a
2147 * SQL TIME value when it sends it to the database, using the given
2148 * timezone.
2149 *
2150 * @param parameterIndex
2151 * the first parameter is 1...));
2152 * @param x
2153 * the parameter value
2154 * @param cal
2155 * the timezone to use
2156 *
2157 * @throws SQLException
2158 * if a database access error occurs
2159 */
2160 public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
2161 throws SQLException {
2162 setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
2163 }
2164
2165 /**
2166 * Set a parameter to a java.sql.Time value. The driver converts this to a
2167 * SQL TIME value when it sends it to the database, using the given
2168 * timezone.
2169 *
2170 * @param parameterIndex
2171 * the first parameter is 1...));
2172 * @param x
2173 * the parameter value
2174 * @param tz
2175 * the timezone to use
2176 *
2177 * @throws SQLException
2178 * if a database access error occurs
2179 */
2180 public void setTimeInternal(int parameterIndex, java.sql.Time x,
2181 Calendar targetCalendar,
2182 TimeZone tz, boolean rollForward) throws SQLException {
2183 if (x == null) {
2184 setNull(parameterIndex, java.sql.Types.TIME);
2185 } else {
2186 BindValue binding = getBinding(parameterIndex, false);
2187 setType(binding, MysqlDefs.FIELD_TYPE_TIME);
2188
2189 if (!this.useLegacyDatetimeCode) {
2190 binding.value = x;
2191 } else {
2192 Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
2193
2194 synchronized (sessionCalendar) {
2195 binding.value = TimeUtil.changeTimezone(this.connection,
2196 sessionCalendar,
2197 targetCalendar,
2198 x, tz,
2199 this.connection.getServerTimezoneTZ(),
2200 rollForward);
2201 }
2202 }
2203
2204 binding.isNull = false;
2205 binding.isLongData = false;
2206 }
2207 }
2208
2209 /**
2210 * Set a parameter to a java.sql.Timestamp value. The driver converts this
2211 * to a SQL TIMESTAMP value when it sends it to the database.
2212 *
2213 * @param parameterIndex
2214 * the first parameter is 1, the second is 2, ...
2215 * @param x
2216 * the parameter value
2217 *
2218 * @throws SQLException
2219 * if a database-access error occurs.
2220 */
2221 public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
2222 throws SQLException {
2223 setTimestampInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false);
2224 }
2225
2226 /**
2227 * Set a parameter to a java.sql.Timestamp value. The driver converts this
2228 * to a SQL TIMESTAMP value when it sends it to the database.
2229 *
2230 * @param parameterIndex
2231 * the first parameter is 1, the second is 2, ...
2232 * @param x
2233 * the parameter value
2234 * @param cal
2235 * the timezone to use
2236 *
2237 * @throws SQLException
2238 * if a database-access error occurs.
2239 */
2240 public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
2241 Calendar cal) throws SQLException {
2242 setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
2243 }
2244
2245 protected void setTimestampInternal(int parameterIndex,
2246 java.sql.Timestamp x, Calendar targetCalendar,
2247 TimeZone tz, boolean rollForward)
2248 throws SQLException {
2249 if (x == null) {
2250 setNull(parameterIndex, java.sql.Types.TIMESTAMP);
2251 } else {
2252 BindValue binding = getBinding(parameterIndex, false);
2253 setType(binding, MysqlDefs.FIELD_TYPE_DATETIME);
2254
2255 if (!this.useLegacyDatetimeCode) {
2256 binding.value = x;
2257 } else {
2258 Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
2259 this.connection.getUtcCalendar() :
2260 getCalendarInstanceForSessionOrNew();
2261
2262 synchronized (sessionCalendar) {
2263 binding.value = TimeUtil.changeTimezone(this.connection,
2264 sessionCalendar,
2265 targetCalendar,
2266 x, tz,
2267 this.connection.getServerTimezoneTZ(),
2268 rollForward);
2269 }
2270
2271 binding.isNull = false;
2272 binding.isLongData = false;
2273 }
2274 }
2275 }
2276
2277 protected void setType(BindValue oldValue, int bufferType) {
2278 if (oldValue.bufferType != bufferType) {
2279 this.sendTypesToServer = true;
2280 }
2281
2282 oldValue.bufferType = bufferType;
2283 }
2284
2285 /**
2286 * DOCUMENT ME!
2287 *
2288 * @param parameterIndex
2289 * DOCUMENT ME!
2290 * @param x
2291 * DOCUMENT ME!
2292 * @param length
2293 * DOCUMENT ME!
2294 *
2295 * @throws SQLException
2296 * DOCUMENT ME!
2297 * @throws NotImplemented
2298 * DOCUMENT ME!
2299 *
2300 * @see java.sql.PreparedStatement#setUnicodeStream(int,
2301 * java.io.InputStream, int)
2302 * @deprecated
2303 */
2304 public void setUnicodeStream(int parameterIndex, InputStream x, int length)
2305 throws SQLException {
2306 checkClosed();
2307
2308 throw SQLError.notImplemented();
2309 }
2310
2311 /**
2312 * @see java.sql.PreparedStatement#setURL(int, java.net.URL)
2313 */
2314 public void setURL(int parameterIndex, URL x) throws SQLException {
2315 checkClosed();
2316
2317 setString(parameterIndex, x.toString());
2318 }
2319
2320 /**
2321 * Method storeBinding.
2322 *
2323 * @param packet
2324 * @param bindValue
2325 * @param mysql
2326 * DOCUMENT ME!
2327 *
2328 * @throws SQLException
2329 * DOCUMENT ME!
2330 */
2331 private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql)
2332 throws SQLException {
2333 try {
2334 Object value = bindValue.value;
2335
2336 //
2337 // Handle primitives first
2338 //
2339 switch (bindValue.bufferType) {
2340
2341 case MysqlDefs.FIELD_TYPE_TINY:
2342 packet.writeByte(bindValue.byteBinding);
2343 return;
2344 case MysqlDefs.FIELD_TYPE_SHORT:
2345 packet.ensureCapacity(2);
2346 packet.writeInt(bindValue.shortBinding);
2347 return;
2348 case MysqlDefs.FIELD_TYPE_LONG:
2349 packet.ensureCapacity(4);
2350 packet.writeLong(bindValue.intBinding);
2351 return;
2352 case MysqlDefs.FIELD_TYPE_LONGLONG:
2353 packet.ensureCapacity(8);
2354 packet.writeLongLong(bindValue.longBinding);
2355 return;
2356 case MysqlDefs.FIELD_TYPE_FLOAT:
2357 packet.ensureCapacity(4);
2358 packet.writeFloat(bindValue.floatBinding);
2359 return;
2360 case MysqlDefs.FIELD_TYPE_DOUBLE:
2361 packet.ensureCapacity(8);
2362 packet.writeDouble(bindValue.doubleBinding);
2363 return;
2364 case MysqlDefs.FIELD_TYPE_TIME:
2365 storeTime(packet, (Time) value);
2366 return;
2367 case MysqlDefs.FIELD_TYPE_DATE:
2368 case MysqlDefs.FIELD_TYPE_DATETIME:
2369 case MysqlDefs.FIELD_TYPE_TIMESTAMP:
2370 storeDateTime(packet, (java.util.Date) value, mysql, bindValue.bufferType);
2371 return;
2372 case MysqlDefs.FIELD_TYPE_VAR_STRING:
2373 case MysqlDefs.FIELD_TYPE_STRING:
2374 case MysqlDefs.FIELD_TYPE_VARCHAR:
2375 case MysqlDefs.FIELD_TYPE_DECIMAL:
2376 case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
2377 if (value instanceof byte[]) {
2378 packet.writeLenBytes((byte[]) value);
2379 } else if (!this.isLoadDataQuery) {
2380 packet.writeLenString((String) value, this.charEncoding,
2381 this.connection.getServerCharacterEncoding(),
2382 this.charConverter, this.connection
2383 .parserKnowsUnicode(),
2384 this.connection);
2385 } else {
2386 packet.writeLenBytes(((String) value).getBytes());
2387 }
2388
2389 return;
2390 }
2391
2392
2393 } catch (UnsupportedEncodingException uEE) {
2394 throw SQLError.createSQLException(Messages
2395 .getString("ServerPreparedStatement.22") //$NON-NLS-1$
2396 + this.connection.getEncoding() + "'", //$NON-NLS-1$
2397 SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2398 }
2399 }
2400
2401 private void storeDateTime412AndOlder(Buffer intoBuf, java.util.Date dt, int bufferType)
2402 throws SQLException {
2403
2404 Calendar sessionCalendar = null;
2405
2406 if (!this.useLegacyDatetimeCode) {
2407 if (bufferType == MysqlDefs.FIELD_TYPE_DATE) {
2408 sessionCalendar = getDefaultTzCalendar();
2409 } else {
2410 sessionCalendar = getServerTzCalendar();
2411 }
2412 } else {
2413 sessionCalendar = (dt instanceof Timestamp &&
2414 this.connection.getUseJDBCCompliantTimezoneShift()) ?
2415 this.connection.getUtcCalendar() : getCalendarInstanceForSessionOrNew();
2416 }
2417
2418 synchronized (sessionCalendar) {
2419 java.util.Date oldTime = sessionCalendar.getTime();
2420
2421 try {
2422 intoBuf.ensureCapacity(8);
2423 intoBuf.writeByte((byte) 7); // length
2424
2425 sessionCalendar.setTime(dt);
2426
2427 int year = sessionCalendar.get(Calendar.YEAR);
2428 int month = sessionCalendar.get(Calendar.MONTH) + 1;
2429 int date = sessionCalendar.get(Calendar.DATE);
2430
2431 intoBuf.writeInt(year);
2432 intoBuf.writeByte((byte) month);
2433 intoBuf.writeByte((byte) date);
2434
2435 if (dt instanceof java.sql.Date) {
2436 intoBuf.writeByte((byte) 0);
2437 intoBuf.writeByte((byte) 0);
2438 intoBuf.writeByte((byte) 0);
2439 } else {
2440 intoBuf.writeByte((byte) sessionCalendar
2441 .get(Calendar.HOUR_OF_DAY));
2442 intoBuf.writeByte((byte) sessionCalendar
2443 .get(Calendar.MINUTE));
2444 intoBuf.writeByte((byte) sessionCalendar
2445 .get(Calendar.SECOND));
2446 }
2447 } finally {
2448 sessionCalendar.setTime(oldTime);
2449 }
2450 }
2451 }
2452
2453 private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql, int bufferType)
2454 throws SQLException {
2455 if (this.connection.versionMeetsMinimum(4, 1, 3)) {
2456 storeDateTime413AndNewer(intoBuf, dt, bufferType);
2457 } else {
2458 storeDateTime412AndOlder(intoBuf, dt, bufferType);
2459 }
2460 }
2461
2462 private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt, int bufferType)
2463 throws SQLException {
2464 Calendar sessionCalendar = null;
2465
2466 if (!this.useLegacyDatetimeCode) {
2467 if (bufferType == MysqlDefs.FIELD_TYPE_DATE) {
2468 sessionCalendar = getDefaultTzCalendar();
2469 } else {
2470 sessionCalendar = getServerTzCalendar();
2471 }
2472 } else {
2473 sessionCalendar = (dt instanceof Timestamp &&
2474 this.connection.getUseJDBCCompliantTimezoneShift()) ?
2475 this.connection.getUtcCalendar() : getCalendarInstanceForSessionOrNew();
2476 }
2477
2478 synchronized (sessionCalendar) {
2479 java.util.Date oldTime = sessionCalendar.getTime();
2480
2481 try {
2482 sessionCalendar.setTime(dt);
2483
2484 if (dt instanceof java.sql.Date) {
2485 sessionCalendar.set(Calendar.HOUR_OF_DAY, 0);
2486 sessionCalendar.set(Calendar.MINUTE, 0);
2487 sessionCalendar.set(Calendar.SECOND, 0);
2488 }
2489
2490 byte length = (byte) 7;
2491
2492 if (dt instanceof java.sql.Timestamp) {
2493 length = (byte) 11;
2494 }
2495
2496 intoBuf.ensureCapacity(length);
2497
2498 intoBuf.writeByte(length); // length
2499
2500 int year = sessionCalendar.get(Calendar.YEAR);
2501 int month = sessionCalendar.get(Calendar.MONTH) + 1;
2502 int date = sessionCalendar.get(Calendar.DAY_OF_MONTH);
2503
2504 intoBuf.writeInt(year);
2505 intoBuf.writeByte((byte) month);
2506 intoBuf.writeByte((byte) date);
2507
2508 if (dt instanceof java.sql.Date) {
2509 intoBuf.writeByte((byte) 0);
2510 intoBuf.writeByte((byte) 0);
2511 intoBuf.writeByte((byte) 0);
2512 } else {
2513 intoBuf.writeByte((byte) sessionCalendar
2514 .get(Calendar.HOUR_OF_DAY));
2515 intoBuf.writeByte((byte) sessionCalendar
2516 .get(Calendar.MINUTE));
2517 intoBuf.writeByte((byte) sessionCalendar
2518 .get(Calendar.SECOND));
2519 }
2520
2521 if (length == 11) {
2522 // MySQL expects microseconds, not nanos
2523 intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos() / 1000);
2524 }
2525
2526 } finally {
2527 sessionCalendar.setTime(oldTime);
2528 }
2529 }
2530 }
2531
2532 private Calendar getServerTzCalendar() {
2533 synchronized (this) {
2534 if (serverTzCalendar == null) {
2535 serverTzCalendar = new GregorianCalendar(this.connection.getServerTimezoneTZ());
2536 }
2537
2538 return this.serverTzCalendar;
2539 }
2540 }
2541
2542 private Calendar getDefaultTzCalendar() {
2543 synchronized (this) {
2544 if (defaultTzCalendar == null) {
2545 defaultTzCalendar = new GregorianCalendar(TimeZone.getDefault());
2546 }
2547
2548 return this.defaultTzCalendar;
2549 }
2550 }
2551
2552 //
2553 // TO DO: Investigate using NIO to do this faster
2554 //
2555 private void storeReader(MysqlIO mysql, int parameterIndex, Buffer packet,
2556 Reader inStream) throws SQLException {
2557 String forcedEncoding = this.connection.getClobCharacterEncoding();
2558
2559 String clobEncoding =
2560 (forcedEncoding == null ? this.connection.getEncoding() : forcedEncoding);
2561
2562 int maxBytesChar = 2;
2563
2564 if (clobEncoding != null) {
2565 if (!clobEncoding.equals("UTF-16")) {
2566 maxBytesChar = this.connection.getMaxBytesPerChar(clobEncoding);
2567
2568 if (maxBytesChar == 1) {
2569 maxBytesChar = 2; // for safety
2570 }
2571 } else {
2572 maxBytesChar = 4;
2573 }
2574 }
2575
2576 char[] buf = new char[BLOB_STREAM_READ_BUF_SIZE / maxBytesChar];
2577
2578 int numRead = 0;
2579
2580 int bytesInPacket = 0;
2581 int totalBytesRead = 0;
2582 int bytesReadAtLastSend = 0;
2583 int packetIsFullAt = this.connection.getBlobSendChunkSize();
2584
2585
2586
2587 try {
2588 packet.clear();
2589 packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2590 packet.writeLong(this.serverStatementId);
2591 packet.writeInt((parameterIndex));
2592
2593 boolean readAny = false;
2594
2595 while ((numRead = inStream.read(buf)) != -1) {
2596 readAny = true;
2597
2598 byte[] valueAsBytes = StringUtils.getBytes(buf, null,
2599 clobEncoding, this.connection
2600 .getServerCharacterEncoding(), 0, numRead,
2601 this.connection.parserKnowsUnicode(), getExceptionInterceptor());
2602
2603 packet.writeBytesNoNull(valueAsBytes, 0, valueAsBytes.length);
2604
2605 bytesInPacket += valueAsBytes.length;
2606 totalBytesRead += valueAsBytes.length;
2607
2608 if (bytesInPacket >= packetIsFullAt) {
2609 bytesReadAtLastSend = totalBytesRead;
2610
2611 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet,
2612 true, null, 0);
2613
2614 bytesInPacket = 0;
2615 packet.clear();
2616 packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2617 packet.writeLong(this.serverStatementId);
2618 packet.writeInt((parameterIndex));
2619 }
2620 }
2621
2622 if (totalBytesRead != bytesReadAtLastSend) {
2623 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
2624 null, 0);
2625 }
2626
2627 if (!readAny) {
2628 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
2629 null, 0);
2630 }
2631 } catch (IOException ioEx) {
2632 SQLException sqlEx = SQLError.createSQLException(Messages
2633 .getString("ServerPreparedStatement.24") //$NON-NLS-1$
2634 + ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2635 sqlEx.initCause(ioEx);
2636
2637 throw sqlEx;
2638 } finally {
2639 if (this.connection.getAutoClosePStmtStreams()) {
2640 if (inStream != null) {
2641 try {
2642 inStream.close();
2643 } catch (IOException ioEx) {
2644 ; // ignore
2645 }
2646 }
2647 }
2648 }
2649 }
2650
2651 private void storeStream(MysqlIO mysql, int parameterIndex, Buffer packet,
2652 InputStream inStream) throws SQLException {
2653 byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE];
2654
2655 int numRead = 0;
2656
2657 try {
2658 int bytesInPacket = 0;
2659 int totalBytesRead = 0;
2660 int bytesReadAtLastSend = 0;
2661 int packetIsFullAt = this.connection.getBlobSendChunkSize();
2662
2663 packet.clear();
2664 packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2665 packet.writeLong(this.serverStatementId);
2666 packet.writeInt((parameterIndex));
2667
2668 boolean readAny = false;
2669
2670 while ((numRead = inStream.read(buf)) != -1) {
2671
2672 readAny = true;
2673
2674 packet.writeBytesNoNull(buf, 0, numRead);
2675 bytesInPacket += numRead;
2676 totalBytesRead += numRead;
2677
2678 if (bytesInPacket >= packetIsFullAt) {
2679 bytesReadAtLastSend = totalBytesRead;
2680
2681 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet,
2682 true, null, 0);
2683
2684 bytesInPacket = 0;
2685 packet.clear();
2686 packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
2687 packet.writeLong(this.serverStatementId);
2688 packet.writeInt((parameterIndex));
2689 }
2690 }
2691
2692 if (totalBytesRead != bytesReadAtLastSend) {
2693 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
2694 null, 0);
2695 }
2696
2697 if (!readAny) {
2698 mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
2699 null, 0);
2700 }
2701 } catch (IOException ioEx) {
2702 SQLException sqlEx = SQLError.createSQLException(Messages
2703 .getString("ServerPreparedStatement.25") //$NON-NLS-1$
2704 + ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2705 sqlEx.initCause(ioEx);
2706
2707 throw sqlEx;
2708 } finally {
2709 if (this.connection.getAutoClosePStmtStreams()) {
2710 if (inStream != null) {
2711 try {
2712 inStream.close();
2713 } catch (IOException ioEx) {
2714 ; // ignore
2715 }
2716 }
2717 }
2718 }
2719 }
2720
2721 /**
2722 * @see java.lang.Object#toString()
2723 */
2724 public String toString() {
2725 StringBuffer toStringBuf = new StringBuffer();
2726
2727 toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); //$NON-NLS-1$
2728 toStringBuf.append(this.serverStatementId);
2729 toStringBuf.append("] - "); //$NON-NLS-1$
2730
2731 try {
2732 toStringBuf.append(asSql());
2733 } catch (SQLException sqlEx) {
2734 toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); //$NON-NLS-1$
2735 toStringBuf.append(sqlEx);
2736 }
2737
2738 return toStringBuf.toString();
2739 }
2740
2741 protected long getServerStatementId() {
2742 return serverStatementId;
2743 }
2744
2745 private boolean hasCheckedRewrite = false;
2746 private boolean canRewrite = false;
2747
2748 public synchronized boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException {
2749 if (!hasCheckedRewrite) {
2750 this.hasCheckedRewrite = true;
2751 this.canRewrite = canRewrite(this.originalSql, isOnDuplicateKeyUpdate(), getLocationOfOnDuplicateKeyUpdate(), 0);
2752 // We need to client-side parse this to get the VALUES clause, etc.
2753 this.parseInfo = new ParseInfo(this.originalSql, this.connection, this.connection.getMetaData(), this.charEncoding, this.charConverter);
2754 }
2755
2756 return this.canRewrite;
2757 }
2758
2759
2760 public synchronized boolean canRewriteAsMultivalueInsertStatement() throws SQLException {
2761 if (!canRewriteAsMultiValueInsertAtSqlLevel()) {
2762 return false;
2763 }
2764
2765 BindValue[] currentBindValues = null;
2766 BindValue[] previousBindValues = null;
2767
2768 int nbrCommands = this.batchedArgs.size();
2769
2770 // Can't have type changes between sets of bindings for this to work...
2771
2772 for (int commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
2773 Object arg = this.batchedArgs.get(commandIndex);
2774
2775 if (!(arg instanceof String)) {
2776
2777 currentBindValues = ((BatchedBindValues) arg).batchedParameterValues;
2778
2779 // We need to check types each time, as
2780 // the user might have bound different
2781 // types in each addBatch()
2782
2783 if (previousBindValues != null) {
2784 for (int j = 0; j < this.parameterBindings.length; j++) {
2785 if (currentBindValues[j].bufferType != previousBindValues[j].bufferType) {
2786 return false;
2787 }
2788 }
2789 }
2790 }
2791 }
2792
2793
2794
2795 return true;
2796 }
2797
2798 private int locationOfOnDuplicateKeyUpdate = -2;
2799
2800 protected synchronized int getLocationOfOnDuplicateKeyUpdate() {
2801 if (this.locationOfOnDuplicateKeyUpdate == -2) {
2802 this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(this.originalSql);
2803 }
2804
2805 return this.locationOfOnDuplicateKeyUpdate;
2806 }
2807
2808 protected synchronized boolean isOnDuplicateKeyUpdate() {
2809 return getLocationOfOnDuplicateKeyUpdate() != -1;
2810 }
2811
2812
2813
2814
2815 /**
2816 * Computes the maximum parameter set size, and entire batch size given
2817 * the number of arguments in the batch.
2818 */
2819 protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) {
2820 long sizeOfEntireBatch = 1 + /* com_execute */ + 4 /* stmt id */ + 1 /* flags */ + 4 /* batch count padding */;
2821 long maxSizeOfParameterSet = 0;
2822
2823 for (int i = 0; i < numBatchedArgs; i++) {
2824 BindValue[] paramArg = ((BatchedBindValues) this.batchedArgs.get(i)).batchedParameterValues;
2825
2826 long sizeOfParameterSet = 0;
2827
2828 sizeOfParameterSet += (this.parameterCount + 7) / 8; // for isNull
2829
2830 sizeOfParameterSet += this.parameterCount * 2; // have to send types
2831
2832 for (int j = 0; j < this.parameterBindings.length; j++) {
2833 if (!paramArg[j].isNull) {
2834
2835 long size = paramArg[j].getBoundLength();
2836
2837 if (paramArg[j].isLongData) {
2838 if (size != -1) {
2839 sizeOfParameterSet += size;
2840 }
2841 } else {
2842 sizeOfParameterSet += size;
2843 }
2844 }
2845 }
2846
2847 sizeOfEntireBatch += sizeOfParameterSet;
2848
2849 if (sizeOfParameterSet > maxSizeOfParameterSet) {
2850 maxSizeOfParameterSet = sizeOfParameterSet;
2851 }
2852 }
2853
2854 return new long[] {maxSizeOfParameterSet, sizeOfEntireBatch};
2855 }
2856
2857 protected int setOneBatchedParameterSet(
2858 java.sql.PreparedStatement batchedStatement, int batchedParamIndex,
2859 Object paramSet) throws SQLException {
2860 BindValue[] paramArg = ((BatchedBindValues) paramSet).batchedParameterValues;
2861
2862 for (int j = 0; j < paramArg.length; j++) {
2863 if (paramArg[j].isNull) {
2864 batchedStatement.setNull(batchedParamIndex++, Types.NULL);
2865 } else {
2866 if (paramArg[j].isLongData) {
2867 Object value = paramArg[j].value;
2868
2869 if (value instanceof InputStream) {
2870 batchedStatement.setBinaryStream(batchedParamIndex++,
2871 (InputStream) value,
2872 (int) paramArg[j].bindLength);
2873 } else {
2874 batchedStatement.setCharacterStream(
2875 batchedParamIndex++, (Reader) value,
2876 (int) paramArg[j].bindLength);
2877 }
2878 } else {
2879
2880 switch (paramArg[j].bufferType) {
2881
2882 case MysqlDefs.FIELD_TYPE_TINY:
2883 batchedStatement.setByte(batchedParamIndex++,
2884 paramArg[j].byteBinding);
2885 break;
2886 case MysqlDefs.FIELD_TYPE_SHORT:
2887 batchedStatement.setShort(batchedParamIndex++,
2888 paramArg[j].shortBinding);
2889 break;
2890 case MysqlDefs.FIELD_TYPE_LONG:
2891 batchedStatement.setInt(batchedParamIndex++,
2892 paramArg[j].intBinding);
2893 break;
2894 case MysqlDefs.FIELD_TYPE_LONGLONG:
2895 batchedStatement.setLong(batchedParamIndex++,
2896 paramArg[j].longBinding);
2897 break;
2898 case MysqlDefs.FIELD_TYPE_FLOAT:
2899 batchedStatement.setFloat(batchedParamIndex++,
2900 paramArg[j].floatBinding);
2901 break;
2902 case MysqlDefs.FIELD_TYPE_DOUBLE:
2903 batchedStatement.setDouble(batchedParamIndex++,
2904 paramArg[j].doubleBinding);
2905 break;
2906 case MysqlDefs.FIELD_TYPE_TIME:
2907 batchedStatement.setTime(batchedParamIndex++,
2908 (Time) paramArg[j].value);
2909 break;
2910 case MysqlDefs.FIELD_TYPE_DATE:
2911 batchedStatement.setDate(batchedParamIndex++,
2912 (Date) paramArg[j].value);
2913 break;
2914 case MysqlDefs.FIELD_TYPE_DATETIME:
2915 case MysqlDefs.FIELD_TYPE_TIMESTAMP:
2916 batchedStatement.setTimestamp(batchedParamIndex++,
2917 (Timestamp) paramArg[j].value);
2918 break;
2919 case MysqlDefs.FIELD_TYPE_VAR_STRING:
2920 case MysqlDefs.FIELD_TYPE_STRING:
2921 case MysqlDefs.FIELD_TYPE_VARCHAR:
2922 case MysqlDefs.FIELD_TYPE_DECIMAL:
2923 case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
2924 Object value = paramArg[j].value;
2925
2926 if (value instanceof byte[]) {
2927 batchedStatement.setBytes(batchedParamIndex,
2928 (byte[]) value);
2929 } else {
2930 batchedStatement.setString(batchedParamIndex,
2931 (String) value);
2932 }
2933
2934 // If we ended up here as a multi-statement, we're not working with a server prepared statement
2935
2936 if (batchedStatement instanceof ServerPreparedStatement) {
2937 BindValue asBound = ((ServerPreparedStatement) batchedStatement)
2938 .getBinding(
2939 batchedParamIndex,
2940 false);
2941 asBound.bufferType = paramArg[j].bufferType;
2942 }
2943
2944 batchedParamIndex++;
2945
2946 break;
2947 default:
2948 throw new IllegalArgumentException(
2949 "Unknown type when re-binding parameter into batched statement for parameter index "
2950 + batchedParamIndex);
2951 }
2952 }
2953 }
2954 }
2955
2956 return batchedParamIndex;
2957 }
2958
2959 protected boolean containsOnDuplicateKeyUpdateInSQL() {
2960 return this.hasOnDuplicateKeyUpdate;
2961 }
2962
2963 protected PreparedStatement prepareBatchedInsertSQL(ConnectionImpl localConn, int numBatches) throws SQLException {
2964 try {
2965 PreparedStatement pstmt = new ServerPreparedStatement(localConn, this.parseInfo.getSqlForBatch(numBatches), this.currentCatalog, this.resultSetConcurrency, this.resultSetType);
2966 pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys);
2967
2968 return pstmt;
2969 } catch (UnsupportedEncodingException e) {
2970 SQLException sqlEx = SQLError.createSQLException("Unable to prepare batch statement", SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
2971 sqlEx.initCause(e);
2972
2973 throw sqlEx;
2974 }
2975
2976 }
2977 }