| 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 | 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 com.mysql.jdbc.profiler.ProfileEventSink; |
| 28 | import com.mysql.jdbc.profiler.ProfilerEvent; |
| 29 | |
| 30 | import java.io.ByteArrayInputStream; |
| 31 | import java.io.IOException; |
| 32 | import java.io.InputStream; |
| 33 | import java.io.Reader; |
| 34 | import java.io.UnsupportedEncodingException; |
| 35 | |
| 36 | import java.math.BigDecimal; |
| 37 | |
| 38 | import java.net.URL; |
| 39 | |
| 40 | import java.sql.Array; |
| 41 | import java.sql.Blob; |
| 42 | import java.sql.Clob; |
| 43 | import java.sql.Date; |
| 44 | import java.sql.ParameterMetaData; |
| 45 | import java.sql.Ref; |
| 46 | import java.sql.SQLException; |
| 47 | import java.sql.Time; |
| 48 | import java.sql.Types; |
| 49 | |
| 50 | import java.util.ArrayList; |
| 51 | import java.util.BitSet; |
| 52 | import java.util.Calendar; |
| 53 | import java.util.Locale; |
| 54 | import java.util.TimeZone; |
| 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 | protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192; |
| 65 | |
| 66 | static class BatchedBindValues { |
| 67 | BindValue[] batchedParameterValues; |
| 68 | |
| 69 | BatchedBindValues(BindValue[] paramVals) { |
| 70 | int numParams = paramVals.length; |
| 71 | |
| 72 | this.batchedParameterValues = new BindValue[numParams]; |
| 73 | |
| 74 | for (int i = 0; i < numParams; i++) { |
| 75 | this.batchedParameterValues[i] = new BindValue(paramVals[i]); |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | static class BindValue { |
| 81 | |
| 82 | long boundBeforeExecutionNum = 0; |
| 83 | |
| 84 | long bindLength; /* Default length of data */ |
| 85 | |
| 86 | int bufferType; /* buffer type */ |
| 87 | |
| 88 | byte byteBinding; |
| 89 | |
| 90 | double doubleBinding; |
| 91 | |
| 92 | float floatBinding; |
| 93 | |
| 94 | int intBinding; |
| 95 | |
| 96 | boolean isLongData; /* long data indicator */ |
| 97 | |
| 98 | boolean isNull; /* NULL indicator */ |
| 99 | |
| 100 | boolean isSet = false; /* has this parameter been set? */ |
| 101 | |
| 102 | long longBinding; |
| 103 | |
| 104 | short shortBinding; |
| 105 | |
| 106 | Object value; /* The value to store */ |
| 107 | |
| 108 | BindValue() { |
| 109 | } |
| 110 | |
| 111 | BindValue(BindValue copyMe) { |
| 112 | this.value = copyMe.value; |
| 113 | this.isSet = copyMe.isSet; |
| 114 | this.isLongData = copyMe.isLongData; |
| 115 | this.isNull = copyMe.isNull; |
| 116 | this.bufferType = copyMe.bufferType; |
| 117 | this.bindLength = copyMe.bindLength; |
| 118 | this.byteBinding = copyMe.byteBinding; |
| 119 | this.shortBinding = copyMe.shortBinding; |
| 120 | this.intBinding = copyMe.intBinding; |
| 121 | this.longBinding = copyMe.longBinding; |
| 122 | this.floatBinding = copyMe.floatBinding; |
| 123 | this.doubleBinding = copyMe.doubleBinding; |
| 124 | } |
| 125 | |
| 126 | void reset() { |
| 127 | this.isSet = false; |
| 128 | this.value = null; |
| 129 | this.isLongData = false; |
| 130 | |
| 131 | this.byteBinding = 0; |
| 132 | this.shortBinding = 0; |
| 133 | this.intBinding = 0; |
| 134 | this.longBinding = 0L; |
| 135 | this.floatBinding = 0; |
| 136 | this.doubleBinding = 0D; |
| 137 | } |
| 138 | |
| 139 | public String toString() { |
| 140 | return toString(false); |
| 141 | } |
| 142 | |
| 143 | public String toString(boolean quoteIfNeeded) { |
| 144 | if (this.isLongData) { |
| 145 | return "' STREAM DATA '"; |
| 146 | } |
| 147 | |
| 148 | switch (this.bufferType) { |
| 149 | case MysqlDefs.FIELD_TYPE_TINY: |
| 150 | return String.valueOf(byteBinding); |
| 151 | case MysqlDefs.FIELD_TYPE_SHORT: |
| 152 | return String.valueOf(shortBinding); |
| 153 | case MysqlDefs.FIELD_TYPE_LONG: |
| 154 | return String.valueOf(intBinding); |
| 155 | case MysqlDefs.FIELD_TYPE_LONGLONG: |
| 156 | return String.valueOf(longBinding); |
| 157 | case MysqlDefs.FIELD_TYPE_FLOAT: |
| 158 | return String.valueOf(floatBinding); |
| 159 | case MysqlDefs.FIELD_TYPE_DOUBLE: |
| 160 | return String.valueOf(doubleBinding); |
| 161 | case MysqlDefs.FIELD_TYPE_TIME: |
| 162 | case MysqlDefs.FIELD_TYPE_DATE: |
| 163 | case MysqlDefs.FIELD_TYPE_DATETIME: |
| 164 | case MysqlDefs.FIELD_TYPE_TIMESTAMP: |
| 165 | case MysqlDefs.FIELD_TYPE_VAR_STRING: |
| 166 | case MysqlDefs.FIELD_TYPE_STRING: |
| 167 | case MysqlDefs.FIELD_TYPE_VARCHAR: |
| 168 | if (quoteIfNeeded) { |
| 169 | return "'" + String.valueOf(value) + "'"; |
| 170 | } else { |
| 171 | return String.valueOf(value); |
| 172 | } |
| 173 | default: |
| 174 | if (value instanceof byte[]) { |
| 175 | return "byte data"; |
| 176 | |
| 177 | } else { |
| 178 | if (quoteIfNeeded) { |
| 179 | return "'" + String.valueOf(value) + "'"; |
| 180 | } else { |
| 181 | return String.valueOf(value); |
| 182 | } |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /* 1 (length) + 2 (year) + 1 (month) + 1 (day) */ |
| 189 | private static final byte MAX_DATE_REP_LENGTH = (byte) 5; |
| 190 | |
| 191 | /* |
| 192 | * 1 (length) + 2 (year) + 1 (month) + 1 (day) + 1 (hour) + 1 (minute) + 1 |
| 193 | * (second) + 4 (microseconds) |
| 194 | */ |
| 195 | private static final byte MAX_DATETIME_REP_LENGTH = 12; |
| 196 | |
| 197 | /* |
| 198 | * 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) + 1 (minute) + 1 |
| 199 | * (seconds) + 4 (microseconds) |
| 200 | */ |
| 201 | private static final byte MAX_TIME_REP_LENGTH = 13; |
| 202 | |
| 203 | private static void storeTime(Buffer intoBuf, Time tm) throws SQLException { |
| 204 | intoBuf.ensureCapacity(9); |
| 205 | intoBuf.writeByte((byte) 8); // length |
| 206 | intoBuf.writeByte((byte) 0); // neg flag |
| 207 | intoBuf.writeLong(0); // tm->day, not used |
| 208 | |
| 209 | Calendar cal = Calendar.getInstance(); |
| 210 | cal.setTime(tm); |
| 211 | intoBuf.writeByte((byte) cal.get(Calendar.HOUR_OF_DAY)); |
| 212 | intoBuf.writeByte((byte) cal.get(Calendar.MINUTE)); |
| 213 | intoBuf.writeByte((byte) cal.get(Calendar.SECOND)); |
| 214 | |
| 215 | // intoBuf.writeLongInt(0); // tm-second_part |
| 216 | } |
| 217 | |
| 218 | /** The Calendar instance used to create date/time bindings */ |
| 219 | private Calendar dateTimeBindingCal = null; |
| 220 | |
| 221 | /** |
| 222 | * Flag indicating whether or not the long parameters have been 'switched' |
| 223 | * back to normal parameters. We can not execute() if clearParameters() |
| 224 | * hasn't been called in this case. |
| 225 | */ |
| 226 | private boolean detectedLongParameterSwitch = false; |
| 227 | |
| 228 | /** |
| 229 | * The number of fields in the result set (if any) for this |
| 230 | * PreparedStatement. |
| 231 | */ |
| 232 | private int fieldCount; |
| 233 | |
| 234 | /** Has this prepared statement been marked invalid? */ |
| 235 | private boolean invalid = false; |
| 236 | |
| 237 | /** If this statement has been marked invalid, what was the reason? */ |
| 238 | private SQLException invalidationException; |
| 239 | |
| 240 | /** Does this query modify data? */ |
| 241 | private boolean isSelectQuery; |
| 242 | |
| 243 | private Buffer outByteBuffer; |
| 244 | |
| 245 | /** Bind values for individual fields */ |
| 246 | private BindValue[] parameterBindings; |
| 247 | |
| 248 | /** Field-level metadata for parameters */ |
| 249 | private Field[] parameterFields; |
| 250 | |
| 251 | /** Field-level metadata for result sets. */ |
| 252 | private Field[] resultFields; |
| 253 | |
| 254 | /** Do we need to send/resend types to the server? */ |
| 255 | private boolean sendTypesToServer = false; |
| 256 | |
| 257 | /** The ID that the server uses to identify this PreparedStatement */ |
| 258 | private long serverStatementId; |
| 259 | |
| 260 | /** The type used for string bindings, changes from version-to-version */ |
| 261 | private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING; |
| 262 | |
| 263 | private boolean serverNeedsResetBeforeEachExecution; |
| 264 | |
| 265 | /** |
| 266 | * Creates a new ServerPreparedStatement object. |
| 267 | * |
| 268 | * @param conn |
| 269 | * the connection creating us. |
| 270 | * @param sql |
| 271 | * the SQL containing the statement to prepare. |
| 272 | * @param catalog |
| 273 | * the catalog in use when we were created. |
| 274 | * |
| 275 | * @throws SQLException |
| 276 | * If an error occurs |
| 277 | */ |
| 278 | public ServerPreparedStatement(Connection conn, String sql, String catalog) |
| 279 | throws SQLException { |
| 280 | super(conn, catalog); |
| 281 | |
| 282 | checkNullOrEmptyQuery(sql); |
| 283 | |
| 284 | this.isSelectQuery = StringUtils.startsWithIgnoreCaseAndWs(sql, |
| 285 | "SELECT"); //$NON-NLS-1$ |
| 286 | |
| 287 | if (this.connection.versionMeetsMinimum(5, 0, 0)) { |
| 288 | this.serverNeedsResetBeforeEachExecution = |
| 289 | !this.connection.versionMeetsMinimum(5, 0, 3); |
| 290 | } else { |
| 291 | this.serverNeedsResetBeforeEachExecution = |
| 292 | !this.connection.versionMeetsMinimum(4, 1, 10); |
| 293 | } |
| 294 | |
| 295 | this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23); |
| 296 | this.hasLimitClause = (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1); //$NON-NLS-1$ |
| 297 | this.firstCharOfStmt = StringUtils.firstNonWsCharUc(sql); |
| 298 | this.originalSql = sql; |
| 299 | |
| 300 | if (this.connection.versionMeetsMinimum(4, 1, 2)) { |
| 301 | this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING; |
| 302 | } else { |
| 303 | this.stringTypeCode = MysqlDefs.FIELD_TYPE_STRING; |
| 304 | } |
| 305 | |
| 306 | try { |
| 307 | serverPrepare(sql); |
| 308 | } catch (SQLException sqlEx) { |
| 309 | realClose(false); |
| 310 | // don't wrap SQLExceptions |
| 311 | throw sqlEx; |
| 312 | } catch (Exception ex) { |
| 313 | realClose(false); |
| 314 | |
| 315 | throw new SQLException(ex.toString(), |
| 316 | SQLError.SQL_STATE_GENERAL_ERROR); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * JDBC 2.0 Add a set of parameters to the batch. |
| 322 | * |
| 323 | * @exception SQLException |
| 324 | * if a database-access error occurs. |
| 325 | * |
| 326 | * @see Statement#addBatch |
| 327 | */ |
| 328 | public synchronized void addBatch() throws SQLException { |
| 329 | checkClosed(); |
| 330 | |
| 331 | if (this.batchedArgs == null) { |
| 332 | this.batchedArgs = new ArrayList(); |
| 333 | } |
| 334 | |
| 335 | this.batchedArgs.add(new BatchedBindValues(this.parameterBindings)); |
| 336 | } |
| 337 | |
| 338 | protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException { |
| 339 | |
| 340 | PreparedStatement pStmtForSub = null; |
| 341 | |
| 342 | try { |
| 343 | pStmtForSub = new PreparedStatement(this.connection, |
| 344 | this.originalSql, this.currentCatalog); |
| 345 | |
| 346 | int numParameters = pStmtForSub.parameterCount; |
| 347 | int ourNumParameters = this.parameterCount; |
| 348 | |
| 349 | for (int i = 0; (i < numParameters) && (i < ourNumParameters); i++) { |
| 350 | if (this.parameterBindings[i] != null) { |
| 351 | if (this.parameterBindings[i].isNull) { |
| 352 | pStmtForSub.setNull(i + 1, Types.NULL); |
| 353 | } else { |
| 354 | BindValue bindValue = this.parameterBindings[i]; |
| 355 | |
| 356 | // |
| 357 | // Handle primitives first |
| 358 | // |
| 359 | switch (bindValue.bufferType) { |
| 360 | |
| 361 | case MysqlDefs.FIELD_TYPE_TINY: |
| 362 | pStmtForSub.setByte(i + 1, bindValue.byteBinding); |
| 363 | break; |
| 364 | case MysqlDefs.FIELD_TYPE_SHORT: |
| 365 | pStmtForSub.setShort(i + 1, bindValue.shortBinding); |
| 366 | break; |
| 367 | case MysqlDefs.FIELD_TYPE_LONG: |
| 368 | pStmtForSub.setInt(i + 1, bindValue.intBinding); |
| 369 | break; |
| 370 | case MysqlDefs.FIELD_TYPE_LONGLONG: |
| 371 | pStmtForSub.setLong(i + 1, bindValue.longBinding); |
| 372 | break; |
| 373 | case MysqlDefs.FIELD_TYPE_FLOAT: |
| 374 | pStmtForSub.setFloat(i + 1, bindValue.floatBinding); |
| 375 | break; |
| 376 | case MysqlDefs.FIELD_TYPE_DOUBLE: |
| 377 | pStmtForSub.setDouble(i + 1, |
| 378 | bindValue.doubleBinding); |
| 379 | break; |
| 380 | default: |
| 381 | pStmtForSub.setObject(i + 1, |
| 382 | this.parameterBindings[i].value); |
| 383 | break; |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | return pStmtForSub.asSql(quoteStreamsAndUnknowns); |
| 390 | } finally { |
| 391 | if (pStmtForSub != null) { |
| 392 | try { |
| 393 | pStmtForSub.close(); |
| 394 | } catch (SQLException sqlEx) { |
| 395 | ; // ignore |
| 396 | } |
| 397 | } |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | /* |
| 402 | * (non-Javadoc) |
| 403 | * |
| 404 | * @see com.mysql.jdbc.Statement#checkClosed() |
| 405 | */ |
| 406 | protected void checkClosed() throws SQLException { |
| 407 | if (this.invalid) { |
| 408 | throw this.invalidationException; |
| 409 | } |
| 410 | |
| 411 | super.checkClosed(); |
| 412 | } |
| 413 | |
| 414 | /** |
| 415 | * @see java.sql.PreparedStatement#clearParameters() |
| 416 | */ |
| 417 | public synchronized void clearParameters() throws SQLException { |
| 418 | checkClosed(); |
| 419 | clearParametersInternal(true); |
| 420 | } |
| 421 | |
| 422 | private void clearParametersInternal(boolean clearServerParameters) |
| 423 | throws SQLException { |
| 424 | boolean hadLongData = false; |
| 425 | |
| 426 | if (this.parameterBindings != null) { |
| 427 | for (int i = 0; i < this.parameterCount; i++) { |
| 428 | if ((this.parameterBindings[i] != null) |
| 429 | && this.parameterBindings[i].isLongData) { |
| 430 | hadLongData = true; |
| 431 | } |
| 432 | |
| 433 | this.parameterBindings[i].reset(); |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | if (clearServerParameters && hadLongData) { |
| 438 | serverResetStatement(); |
| 439 | |
| 440 | this.detectedLongParameterSwitch = false; |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | protected boolean isCached = false; |
| 445 | |
| 446 | protected void setClosed(boolean flag) { |
| 447 | this.isClosed = flag; |
| 448 | } |
| 449 | /** |
| 450 | * @see java.sql.Statement#close() |
| 451 | */ |
| 452 | public void close() throws SQLException { |
| 453 | if (this.isCached) { |
| 454 | this.isClosed = true; |
| 455 | this.connection.recachePreparedStatement(this); |
| 456 | return; |
| 457 | } |
| 458 | |
| 459 | realClose(true); |
| 460 | } |
| 461 | |
| 462 | private void dumpCloseForTestcase() { |
| 463 | StringBuffer buf = new StringBuffer(); |
| 464 | this.connection.generateConnectionCommentBlock(buf); |
| 465 | buf.append("DEALLOCATE PREPARE debug_stmt_"); |
| 466 | buf.append(this.statementId); |
| 467 | buf.append(";\n"); |
| 468 | |
| 469 | this.connection.dumpTestcaseQuery(buf.toString()); |
| 470 | } |
| 471 | |
| 472 | private void dumpExecuteForTestcase() throws SQLException { |
| 473 | StringBuffer buf = new StringBuffer(); |
| 474 | |
| 475 | for (int i = 0; i < this.parameterCount; i++) { |
| 476 | this.connection.generateConnectionCommentBlock(buf); |
| 477 | |
| 478 | buf.append("SET @debug_stmt_param"); |
| 479 | buf.append(this.statementId); |
| 480 | buf.append("_"); |
| 481 | buf.append(i); |
| 482 | buf.append("="); |
| 483 | |
| 484 | if (this.parameterBindings[i].isNull) { |
| 485 | buf.append("NULL"); |
| 486 | } else { |
| 487 | buf.append(this.parameterBindings[i].toString(true)); |
| 488 | } |
| 489 | |
| 490 | buf.append(";\n"); |
| 491 | } |
| 492 | |
| 493 | this.connection.generateConnectionCommentBlock(buf); |
| 494 | |
| 495 | buf.append("EXECUTE debug_stmt_"); |
| 496 | buf.append(this.statementId); |
| 497 | |
| 498 | if (this.parameterCount > 0) { |
| 499 | buf.append(" USING "); |
| 500 | for (int i = 0; i < this.parameterCount; i++) { |
| 501 | if (i > 0) { |
| 502 | buf.append(", "); |
| 503 | } |
| 504 | |
| 505 | buf.append("@debug_stmt_param"); |
| 506 | buf.append(this.statementId); |
| 507 | buf.append("_"); |
| 508 | buf.append(i); |
| 509 | |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | buf.append(";\n"); |
| 514 | |
| 515 | this.connection.dumpTestcaseQuery(buf.toString()); |
| 516 | } |
| 517 | |
| 518 | private void dumpPrepareForTestcase() throws SQLException { |
| 519 | |
| 520 | StringBuffer buf = new StringBuffer(this.originalSql.length() + 64); |
| 521 | |
| 522 | this.connection.generateConnectionCommentBlock(buf); |
| 523 | |
| 524 | buf.append("PREPARE debug_stmt_"); |
| 525 | buf.append(this.statementId); |
| 526 | buf.append(" FROM \""); |
| 527 | buf.append(this.originalSql); |
| 528 | buf.append("\";\n"); |
| 529 | |
| 530 | this.connection.dumpTestcaseQuery(buf.toString()); |
| 531 | } |
| 532 | |
| 533 | /** |
| 534 | * @see java.sql.Statement#executeBatch() |
| 535 | */ |
| 536 | public synchronized int[] executeBatch() throws SQLException { |
| 537 | if (this.connection.isReadOnly()) { |
| 538 | throw new SQLException(Messages |
| 539 | .getString("ServerPreparedStatement.2") //$NON-NLS-1$ |
| 540 | + Messages.getString("ServerPreparedStatement.3"), //$NON-NLS-1$ |
| 541 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
| 542 | } |
| 543 | |
| 544 | checkClosed(); |
| 545 | |
| 546 | synchronized (this.connection.getMutex()) { |
| 547 | clearWarnings(); |
| 548 | |
| 549 | // Store this for later, we're going to 'swap' them out |
| 550 | // as we execute each batched statement... |
| 551 | BindValue[] oldBindValues = this.parameterBindings; |
| 552 | |
| 553 | try { |
| 554 | int[] updateCounts = null; |
| 555 | |
| 556 | if (this.batchedArgs != null) { |
| 557 | int nbrCommands = this.batchedArgs.size(); |
| 558 | updateCounts = new int[nbrCommands]; |
| 559 | |
| 560 | if (this.retrieveGeneratedKeys) { |
| 561 | this.batchedGeneratedKeys = new ArrayList(nbrCommands); |
| 562 | } |
| 563 | |
| 564 | for (int i = 0; i < nbrCommands; i++) { |
| 565 | updateCounts[i] = -3; |
| 566 | } |
| 567 | |
| 568 | SQLException sqlEx = null; |
| 569 | |
| 570 | int commandIndex = 0; |
| 571 | |
| 572 | BindValue[] previousBindValuesForBatch = null; |
| 573 | |
| 574 | for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { |
| 575 | Object arg = this.batchedArgs.get(commandIndex); |
| 576 | |
| 577 | if (arg instanceof String) { |
| 578 | updateCounts[commandIndex] = executeUpdate((String) arg); |
| 579 | } else { |
| 580 | this.parameterBindings = ((BatchedBindValues) arg).batchedParameterValues; |
| 581 | |
| 582 | try { |
| 583 | // We need to check types each time, as |
| 584 | // the user might have bound different |
| 585 | // types in each addBatch() |
| 586 | |
| 587 | if (previousBindValuesForBatch != null) { |
| 588 | for (int j = 0; j < this.parameterBindings.length; j++) { |
| 589 | if (this.parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) { |
| 590 | this.sendTypesToServer = true; |
| 591 | |
| 592 | break; |
| 593 | } |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | try { |
| 598 | updateCounts[commandIndex] = executeUpdate(false); |
| 599 | } finally { |
| 600 | previousBindValuesForBatch = this.parameterBindings; |
| 601 | } |
| 602 | |
| 603 | if (this.retrieveGeneratedKeys) { |
| 604 | java.sql.ResultSet rs = null; |
| 605 | |
| 606 | try { |
| 607 | // we don't want to use our version, |
| 608 | // because we've altered the behavior of |
| 609 | // ours to support batch updates |
| 610 | // (catch-22) |
| 611 | // Ideally, what we need here is |
| 612 | // super.super.getGeneratedKeys() |
| 613 | // but that construct doesn't exist in |
| 614 | // Java, so that's why there's |
| 615 | // this kludge. |
| 616 | rs = getGeneratedKeysInternal(); |
| 617 | |
| 618 | while (rs.next()) { |
| 619 | this.batchedGeneratedKeys |
| 620 | .add(new byte[][] { rs |
| 621 | .getBytes(1) }); |
| 622 | } |
| 623 | } finally { |
| 624 | if (rs != null) { |
| 625 | rs.close(); |
| 626 | } |
| 627 | } |
| 628 | } |
| 629 | } catch (SQLException ex) { |
| 630 | updateCounts[commandIndex] = EXECUTE_FAILED; |
| 631 | |
| 632 | if (this.connection.getContinueBatchOnError()) { |
| 633 | sqlEx = ex; |
| 634 | } else { |
| 635 | int[] newUpdateCounts = new int[commandIndex]; |
| 636 | System.arraycopy(updateCounts, 0, |
| 637 | newUpdateCounts, 0, commandIndex); |
| 638 | |
| 639 | throw new java.sql.BatchUpdateException(ex |
| 640 | .getMessage(), ex.getSQLState(), ex |
| 641 | .getErrorCode(), newUpdateCounts); |
| 642 | } |
| 643 | } |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | if (sqlEx != null) { |
| 648 | throw new java.sql.BatchUpdateException(sqlEx |
| 649 | .getMessage(), sqlEx.getSQLState(), sqlEx |
| 650 | .getErrorCode(), updateCounts); |
| 651 | } |
| 652 | } |
| 653 | |
| 654 | return (updateCounts != null) ? updateCounts : new int[0]; |
| 655 | } finally { |
| 656 | this.parameterBindings = oldBindValues; |
| 657 | this.sendTypesToServer = true; |
| 658 | |
| 659 | clearBatch(); |
| 660 | } |
| 661 | } |
| 662 | } |
| 663 | |
| 664 | /** |
| 665 | * @see com.mysql.jdbc.PreparedStatement#executeInternal(int, |
| 666 | * com.mysql.jdbc.Buffer, boolean, boolean) |
| 667 | */ |
| 668 | protected com.mysql.jdbc.ResultSet executeInternal(int maxRowsToRetrieve, |
| 669 | Buffer sendPacket, boolean createStreamingResultSet, |
| 670 | boolean queryIsSelectOnly, boolean unpackFields, |
| 671 | boolean isBatch) |
| 672 | throws SQLException { |
| 673 | this.numberOfExecutions++; |
| 674 | |
| 675 | // We defer to server-side execution |
| 676 | try { |
| 677 | return serverExecute(maxRowsToRetrieve, createStreamingResultSet); |
| 678 | } catch (SQLException sqlEx) { |
| 679 | // don't wrap SQLExceptions |
| 680 | if (this.connection.getEnablePacketDebug()) { |
| 681 | this.connection.getIO().dumpPacketRingBuffer(); |
| 682 | } |
| 683 | |
| 684 | if (this.connection.getDumpQueriesOnException()) { |
| 685 | String extractedSql = toString(); |
| 686 | StringBuffer messageBuf = new StringBuffer(extractedSql |
| 687 | .length() + 32); |
| 688 | messageBuf |
| 689 | .append("\n\nQuery being executed when exception was thrown:\n\n"); |
| 690 | messageBuf.append(extractedSql); |
| 691 | |
| 692 | sqlEx = Connection.appendMessageToException(sqlEx, messageBuf |
| 693 | .toString()); |
| 694 | } |
| 695 | |
| 696 | throw sqlEx; |
| 697 | } catch (Exception ex) { |
| 698 | if (this.connection.getEnablePacketDebug()) { |
| 699 | this.connection.getIO().dumpPacketRingBuffer(); |
| 700 | } |
| 701 | |
| 702 | SQLException sqlEx = new SQLException(ex.toString(), |
| 703 | SQLError.SQL_STATE_GENERAL_ERROR); |
| 704 | |
| 705 | if (this.connection.getDumpQueriesOnException()) { |
| 706 | String extractedSql = toString(); |
| 707 | StringBuffer messageBuf = new StringBuffer(extractedSql |
| 708 | .length() + 32); |
| 709 | messageBuf |
| 710 | .append("\n\nQuery being executed when exception was thrown:\n\n"); |
| 711 | messageBuf.append(extractedSql); |
| 712 | |
| 713 | sqlEx = Connection.appendMessageToException(sqlEx, messageBuf |
| 714 | .toString()); |
| 715 | } |
| 716 | |
| 717 | throw sqlEx; |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | /** |
| 722 | * @see com.mysql.jdbc.PreparedStatement#fillSendPacket() |
| 723 | */ |
| 724 | protected Buffer fillSendPacket() throws SQLException { |
| 725 | return null; // we don't use this type of packet |
| 726 | } |
| 727 | |
| 728 | /** |
| 729 | * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte, |
| 730 | * java.io.InputStream, boolean, int) |
| 731 | */ |
| 732 | protected Buffer fillSendPacket(byte[][] batchedParameterStrings, |
| 733 | InputStream[] batchedParameterStreams, boolean[] batchedIsStream, |
| 734 | int[] batchedStreamLengths) throws SQLException { |
| 735 | return null; // we don't use this type of packet |
| 736 | } |
| 737 | |
| 738 | private BindValue getBinding(int parameterIndex, boolean forLongData) |
| 739 | throws SQLException { |
| 740 | checkClosed(); |
| 741 | |
| 742 | if (this.parameterBindings.length == 0) { |
| 743 | throw new SQLException(Messages |
| 744 | .getString("ServerPreparedStatement.8"), //$NON-NLS-1$ |
| 745 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
| 746 | } |
| 747 | |
| 748 | parameterIndex--; |
| 749 | |
| 750 | if ((parameterIndex < 0) |
| 751 | || (parameterIndex >= this.parameterBindings.length)) { |
| 752 | throw new SQLException(Messages |
| 753 | .getString("ServerPreparedStatement.9") //$NON-NLS-1$ |
| 754 | + (parameterIndex + 1) |
| 755 | + Messages.getString("ServerPreparedStatement.10") //$NON-NLS-1$ |
| 756 | + this.parameterBindings.length, |
| 757 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
| 758 | } |
| 759 | |
| 760 | if (this.parameterBindings[parameterIndex] == null) { |
| 761 | this.parameterBindings[parameterIndex] = new BindValue(); |
| 762 | } else { |
| 763 | if (this.parameterBindings[parameterIndex].isLongData |
| 764 | && !forLongData) { |
| 765 | this.detectedLongParameterSwitch = true; |
| 766 | } |
| 767 | } |
| 768 | |
| 769 | this.parameterBindings[parameterIndex].isSet = true; |
| 770 | this.parameterBindings[parameterIndex].boundBeforeExecutionNum = this.numberOfExecutions; |
| 771 | |
| 772 | return this.parameterBindings[parameterIndex]; |
| 773 | } |
| 774 | |
| 775 | /** |
| 776 | * @see com.mysql.jdbc.PreparedStatement#getBytes(int) |
| 777 | */ |
| 778 | synchronized byte[] getBytes(int parameterIndex) throws SQLException { |
| 779 | BindValue bindValue = getBinding(parameterIndex, false); |
| 780 | |
| 781 | if (bindValue.isNull) { |
| 782 | return null; |
| 783 | } else if (bindValue.isLongData) { |
| 784 | throw new NotImplemented(); |
| 785 | } else { |
| 786 | if (this.outByteBuffer == null) { |
| 787 | this.outByteBuffer = Buffer.allocateNew(this.connection |
| 788 | .getNetBufferLength(), false); |
| 789 | } |
| 790 | |
| 791 | this.outByteBuffer.clear(); |
| 792 | |
| 793 | int originalPosition = this.outByteBuffer.getPosition(); |
| 794 | |
| 795 | storeBinding(this.outByteBuffer, bindValue, this.connection.getIO()); |
| 796 | |
| 797 | int newPosition = this.outByteBuffer.getPosition(); |
| 798 | |
| 799 | int length = newPosition - originalPosition; |
| 800 | |
| 801 | byte[] valueAsBytes = new byte[length]; |
| 802 | |
| 803 | System.arraycopy(this.outByteBuffer.getByteBuffer(), |
| 804 | originalPosition, valueAsBytes, 0, length); |
| 805 | |
| 806 | return valueAsBytes; |
| 807 | } |
| 808 | } |
| 809 | |
| 810 | /** |
| 811 | * @see java.sql.PreparedStatement#getMetaData() |
| 812 | */ |
| 813 | public java.sql.ResultSetMetaData getMetaData() throws SQLException { |
| 814 | checkClosed(); |
| 815 | |
| 816 | if (this.resultFields == null) { |
| 817 | return null; |
| 818 | } |
| 819 | |
| 820 | return new ResultSetMetaData(this.resultFields); |
| 821 | } |
| 822 | |
| 823 | /** |
| 824 | * @see java.sql.PreparedStatement#getParameterMetaData() |
| 825 | */ |
| 826 | public synchronized ParameterMetaData getParameterMetaData() throws SQLException { |
| 827 | checkClosed(); |
| 828 | |
| 829 | if (this.parameterMetaData == null) { |
| 830 | this.parameterMetaData = new MysqlParameterMetadata( |
| 831 | this.parameterFields, this.parameterCount); |
| 832 | } |
| 833 | |
| 834 | return this.parameterMetaData; |
| 835 | } |
| 836 | |
| 837 | /** |
| 838 | * @see com.mysql.jdbc.PreparedStatement#isNull(int) |
| 839 | */ |
| 840 | boolean isNull(int paramIndex) { |
| 841 | throw new IllegalArgumentException(Messages |
| 842 | .getString("ServerPreparedStatement.7")); //$NON-NLS-1$ |
| 843 | } |
| 844 | |
| 845 | /** |
| 846 | * Closes this connection and frees all resources. |
| 847 | * |
| 848 | * @param calledExplicitly |
| 849 | * was this called from close()? |
| 850 | * |
| 851 | * @throws SQLException |
| 852 | * if an error occurs |
| 853 | */ |
| 854 | protected synchronized void realClose(boolean calledExplicitly) throws SQLException { |
| 855 | if (this.isClosed) { |
| 856 | return; |
| 857 | } |
| 858 | |
| 859 | |
| 860 | if (this.connection != null) { |
| 861 | |
| 862 | if (this.connection.getAutoGenerateTestcaseScript()) { |
| 863 | dumpCloseForTestcase(); |
| 864 | } |
| 865 | |
| 866 | synchronized (this.connection) { |
| 867 | synchronized (this.connection.getMutex()) { |
| 868 | |
| 869 | // |
| 870 | // Don't communicate with the server if we're being |
| 871 | // called from the finalizer... |
| 872 | // |
| 873 | // This will leak server resources, but if we don't do this, |
| 874 | // we'll deadlock (potentially, because there's no guarantee |
| 875 | // when, what order, and what concurrency finalizers will be |
| 876 | // called with). Well-behaved programs won't rely on finalizers |
| 877 | // to clean up their statements. |
| 878 | // |
| 879 | |
| 880 | SQLException exceptionDuringClose = null; |
| 881 | |
| 882 | |
| 883 | if (calledExplicitly) { |
| 884 | try { |
| 885 | |
| 886 | MysqlIO mysql = this.connection.getIO(); |
| 887 | |
| 888 | Buffer packet = mysql.getSharedSendPacket(); |
| 889 | |
| 890 | packet.writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT); |
| 891 | packet.writeLong(this.serverStatementId); |
| 892 | |
| 893 | mysql.sendCommand(MysqlDefs.COM_CLOSE_STATEMENT, null, |
| 894 | packet, true, null); |
| 895 | } catch (SQLException sqlEx) { |
| 896 | exceptionDuringClose = sqlEx; |
| 897 | } |
| 898 | |
| 899 | } |
| 900 | |
| 901 | |
| 902 | super.realClose(calledExplicitly); |
| 903 | |
| 904 | |
| 905 | clearParametersInternal(false); |
| 906 | this.parameterBindings = null; |
| 907 | |
| 908 | this.parameterFields = null; |
| 909 | this.resultFields = null; |
| 910 | |
| 911 | if (exceptionDuringClose != null) { |
| 912 | throw exceptionDuringClose; |
| 913 | } |
| 914 | } |
| 915 | } |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | /** |
| 920 | * Used by Connection when auto-reconnecting to retrieve 'lost' prepared |
| 921 | * statements. |
| 922 | * |
| 923 | * @throws SQLException |
| 924 | * if an error occurs. |
| 925 | */ |
| 926 | protected void rePrepare() throws SQLException { |
| 927 | this.invalidationException = null; |
| 928 | |
| 929 | try { |
| 930 | serverPrepare(this.originalSql); |
| 931 | } catch (SQLException sqlEx) { |
| 932 | // don't wrap SQLExceptions |
| 933 | this.invalidationException = sqlEx; |
| 934 | } catch (Exception ex) { |
| 935 | this.invalidationException = new SQLException(ex.toString(), |
| 936 | SQLError.SQL_STATE_GENERAL_ERROR); |
| 937 | } |
| 938 | |
| 939 | if (this.invalidationException != null) { |
| 940 | this.invalid = true; |
| 941 | |
| 942 | this.parameterBindings = null; |
| 943 | |
| 944 | this.parameterFields = null; |
| 945 | this.resultFields = null; |
| 946 | |
| 947 | if (this.results != null) { |
| 948 | try { |
| 949 | this.results.close(); |
| 950 | } catch (Exception ex) { |
| 951 | ; |
| 952 | } |
| 953 | } |
| 954 | |
| 955 | if (this.connection != null) { |
| 956 | if (this.maxRowsChanged) { |
| 957 | this.connection.unsetMaxRows(this); |
| 958 | } |
| 959 | |
| 960 | if (!this.connection.getDontTrackOpenResources()) { |
| 961 | this.connection.unregisterStatement(this); |
| 962 | } |
| 963 | } |
| 964 | } |
| 965 | } |
| 966 | |
| 967 | /** |
| 968 | * Tells the server to execute this prepared statement with the current |
| 969 | * parameter bindings. |
| 970 | * |
| 971 | * <pre> |
| 972 | * |
| 973 | * |
| 974 | * - Server gets the command 'COM_EXECUTE' to execute the |
| 975 | * previously prepared query. If there is any param markers; |
| 976 | * then client will send the data in the following format: |
| 977 | * |
| 978 | * [COM_EXECUTE:1] |
| 979 | * [STMT_ID:4] |
| 980 | * [NULL_BITS:(param_count+7)/8)] |
| 981 | * [TYPES_SUPPLIED_BY_CLIENT(0/1):1] |
| 982 | * [[length]data] |
| 983 | * [[length]data] .. [[length]data]. |
| 984 | * |
| 985 | * (Note: Except for string/binary types; all other types will not be |
| 986 | * supplied with length field) |
| 987 | * |
| 988 | * |
| 989 | * </pre> |
| 990 | * |
| 991 | * @param maxRowsToRetrieve |
| 992 | * DOCUMENT ME! |
| 993 | * @param createStreamingResultSet |
| 994 | * DOCUMENT ME! |
| 995 | * |
| 996 | * @return DOCUMENT ME! |
| 997 | * |
| 998 | * @throws SQLException |
| 999 | */ |
| 1000 | private com.mysql.jdbc.ResultSet serverExecute(int maxRowsToRetrieve, |
| 1001 | boolean createStreamingResultSet) throws SQLException { |
| 1002 | synchronized (this.connection.getMutex()) { |
| 1003 | if (this.detectedLongParameterSwitch) { |
| 1004 | // Check when values were bound |
| 1005 | boolean firstFound = false; |
| 1006 | long boundTimeToCheck = 0; |
| 1007 | |
| 1008 | for (int i = 0; i < this.parameterCount - 1; i++) { |
| 1009 | if (this.parameterBindings[i].isLongData) { |
| 1010 | if (firstFound && boundTimeToCheck != |
| 1011 | this.parameterBindings[i].boundBeforeExecutionNum) { |
| 1012 | throw new SQLException(Messages |
| 1013 | .getString("ServerPreparedStatement.11") //$NON-NLS-1$ |
| 1014 | + Messages.getString("ServerPreparedStatement.12"), //$NON-NLS-1$ |
| 1015 | SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); |
| 1016 | } else { |
| 1017 | firstFound = true; |
| 1018 | boundTimeToCheck = this.parameterBindings[i].boundBeforeExecutionNum; |
| 1019 | } |
| 1020 | } |
| 1021 | } |
| 1022 | |
| 1023 | // Okay, we've got all "newly"-bound streams, so reset |
| 1024 | // server-side state to clear out previous bindings |
| 1025 | |
| 1026 | serverResetStatement(); |
| 1027 | } |
| 1028 | |
| 1029 | // Check bindings |
| 1030 | for (int i = 0; i < this.parameterCount; i++) { |
| 1031 | if (!this.parameterBindings[i].isSet) { |
| 1032 | throw new SQLException(Messages |
| 1033 | .getString("ServerPreparedStatement.13") + (i + 1) //$NON-NLS-1$ |
| 1034 | + Messages.getString("ServerPreparedStatement.14"), |
| 1035 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ |
| 1036 | } |
| 1037 | } |
| 1038 | |
| 1039 | // |
| 1040 | // Send all long data |
| 1041 | // |
| 1042 | for (int i = 0; i < this.parameterCount; i++) { |
| 1043 | if (this.parameterBindings[i].isLongData) { |
| 1044 | serverLongData(i, this.parameterBindings[i]); |
| 1045 | } |
| 1046 | } |
| 1047 | |
| 1048 | if (this.connection.getAutoGenerateTestcaseScript()) { |
| 1049 | dumpExecuteForTestcase(); |
| 1050 | } |
| 1051 | |
| 1052 | // |
| 1053 | // store the parameter values |
| 1054 | // |
| 1055 | MysqlIO mysql = this.connection.getIO(); |
| 1056 | |
| 1057 | Buffer packet = mysql.getSharedSendPacket(); |
| 1058 | |
| 1059 | packet.clear(); |
| 1060 | packet.writeByte((byte) MysqlDefs.COM_EXECUTE); |
| 1061 | packet.writeLong(this.serverStatementId); |
| 1062 | |
| 1063 | if (this.connection.versionMeetsMinimum(4, 1, 2)) { |
| 1064 | packet.writeByte((byte) 0); // placeholder for flags |
| 1065 | packet.writeLong(1); // placeholder for parameter iterations |
| 1066 | } |
| 1067 | |
| 1068 | /* Reserve place for null-marker bytes */ |
| 1069 | int nullCount = (this.parameterCount + 7) / 8; |
| 1070 | |
| 1071 | // if (mysql.versionMeetsMinimum(4, 1, 2)) { |
| 1072 | // nullCount = (this.parameterCount + 9) / 8; |
| 1073 | // } |
| 1074 | int nullBitsPosition = packet.getPosition(); |
| 1075 | |
| 1076 | for (int i = 0; i < nullCount; i++) { |
| 1077 | packet.writeByte((byte) 0); |
| 1078 | } |
| 1079 | |
| 1080 | byte[] nullBitsBuffer = new byte[nullCount]; |
| 1081 | |
| 1082 | /* In case if buffers (type) altered, indicate to server */ |
| 1083 | packet.writeByte(this.sendTypesToServer ? (byte) 1 : (byte) 0); |
| 1084 | |
| 1085 | if (this.sendTypesToServer) { |
| 1086 | /* |
| 1087 | * Store types of parameters in first in first package that is |
| 1088 | * sent to the server. |
| 1089 | */ |
| 1090 | for (int i = 0; i < this.parameterCount; i++) { |
| 1091 | packet.writeInt(this.parameterBindings[i].bufferType); |
| 1092 | } |
| 1093 | } |
| 1094 | |
| 1095 | // |
| 1096 | // store the parameter values |
| 1097 | // |
| 1098 | for (int i = 0; i < this.parameterCount; i++) { |
| 1099 | if (!this.parameterBindings[i].isLongData) { |
| 1100 | if (!this.parameterBindings[i].isNull) { |
| 1101 | storeBinding(packet, this.parameterBindings[i], mysql); |
| 1102 | } else { |
| 1103 | nullBitsBuffer[i / 8] |= (1 << (i & 7)); |
| 1104 | } |
| 1105 | } |
| 1106 | } |
| 1107 | |
| 1108 | // |
| 1109 | // Go back and write the NULL flags |
| 1110 | // to the beginning of the packet |
| 1111 | // |
| 1112 | int endPosition = packet.getPosition(); |
| 1113 | packet.setPosition(nullBitsPosition); |
| 1114 | packet.writeBytesNoNull(nullBitsBuffer); |
| 1115 | packet.setPosition(endPosition); |
| 1116 | |
| 1117 | long begin = 0; |
| 1118 | |
| 1119 | if (this.connection.getProfileSql() |
| 1120 | || this.connection.getLogSlowQueries() |
| 1121 | || this.connection.getGatherPerformanceMetrics()) { |
| 1122 | begin = System.currentTimeMillis(); |
| 1123 | } |
| 1124 | |
| 1125 | Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE, |
| 1126 | null, packet, false, null); |
| 1127 | |
| 1128 | |
| 1129 | |
| 1130 | this.connection.incrementNumberOfPreparedExecutes(); |
| 1131 | |
| 1132 | if (this.connection.getProfileSql()) { |
| 1133 | this.eventSink = ProfileEventSink.getInstance(this.connection); |
| 1134 | |
| 1135 | this.eventSink.consumeEvent(new ProfilerEvent( |
| 1136 | ProfilerEvent.TYPE_EXECUTE, "", this.currentCatalog, //$NON-NLS-1$ |
| 1137 | this.connection.getId(), this.statementId, -1, System |
| 1138 | .currentTimeMillis(), (int) (System |
| 1139 | .currentTimeMillis() - begin), null, |
| 1140 | new Throwable(), truncateQueryToLog(asSql(true)))); |
| 1141 | } |
| 1142 | |
| 1143 | com.mysql.jdbc.ResultSet rs = mysql.readAllResults(this, |
| 1144 | maxRowsToRetrieve, this.resultSetType, |
| 1145 | this.resultSetConcurrency, createStreamingResultSet, |
| 1146 | this.currentCatalog, resultPacket, true, this.fieldCount, |
| 1147 | true); |
| 1148 | |
| 1149 | |
| 1150 | if (!createStreamingResultSet && |
| 1151 | this.serverNeedsResetBeforeEachExecution) { |
| 1152 | serverResetStatement(); // clear any long data... |
| 1153 | } |
| 1154 | |
| 1155 | this.sendTypesToServer = false; |
| 1156 | this.results = rs; |
| 1157 | |
| 1158 | if (this.connection.getLogSlowQueries() |
| 1159 | || this.connection.getGatherPerformanceMetrics()) { |
| 1160 | long elapsedTime = System.currentTimeMillis() - begin; |
| 1161 | |
| 1162 | if (this.connection.getLogSlowQueries() |
| 1163 | && (elapsedTime >= this.connection |
| 1164 | .getSlowQueryThresholdMillis())) { |
| 1165 | StringBuffer mesgBuf = new StringBuffer( |
| 1166 | 48 + this.originalSql.length()); |
| 1167 | mesgBuf.append(Messages |
| 1168 | .getString("ServerPreparedStatement.15")); //$NON-NLS-1$ |
| 1169 | mesgBuf.append(this.connection |
| 1170 | .getSlowQueryThresholdMillis()); |
| 1171 | mesgBuf.append(Messages |
| 1172 | .getString("ServerPreparedStatement.15a")); //$NON-NLS-1$ |
| 1173 | mesgBuf.append(elapsedTime); |
| 1174 | mesgBuf.append(Messages |
| 1175 | .getString("ServerPreparedStatement.16")); //$NON-NLS-1$ |
| 1176 | |
| 1177 | mesgBuf.append("as prepared: "); |
| 1178 | mesgBuf.append(this.originalSql); |
| 1179 | mesgBuf.append("\n\n with parameters bound:\n\n"); |
| 1180 | mesgBuf.append(asSql(true)); |
| 1181 | |
| 1182 | this.connection.getLog().logWarn(mesgBuf.toString()); |
| 1183 | |
| 1184 | if (this.connection.getExplainSlowQueries()) { |
| 1185 | String queryAsString = asSql(true); |
| 1186 | |
| 1187 | mysql.explainSlowQuery(queryAsString.getBytes(), |
| 1188 | queryAsString); |
| 1189 | } |
| 1190 | } |
| 1191 | |
| 1192 | if (this.connection.getGatherPerformanceMetrics()) { |
| 1193 | this.connection.registerQueryExecutionTime(elapsedTime); |
| 1194 | } |
| 1195 | } |
| 1196 | |
| 1197 | if (mysql.hadWarnings()) { |
| 1198 | mysql.scanForAndThrowDataTruncation(); |
| 1199 | } |
| 1200 | |
| 1201 | return rs; |
| 1202 | } |
| 1203 | } |
| 1204 | |
| 1205 | /** |
| 1206 | * Sends stream-type data parameters to the server. |
| 1207 | * |
| 1208 | * <pre> |
| 1209 | * |
| 1210 | * Long data handling: |
| 1211 | * |
| 1212 | * - Server gets the long data in pieces with command type 'COM_LONG_DATA'. |
| 1213 | * - The packet recieved will have the format as: |
| 1214 | * [COM_LONG_DATA: 1][STMT_ID:4][parameter_number:2][type:2][data] |
| 1215 | * - Checks if the type is specified by client, and if yes reads the type, |
| 1216 | * and stores the data in that format. |
| 1217 | * - It's up to the client to check for read data ended. The server doesn't |
| 1218 | * care; and also server doesn't notify to the client that it got the |
| 1219 | * data or not; if there is any error; then during execute; the error |
| 1220 | * will be returned |
| 1221 | * |
| 1222 | * </pre> |
| 1223 | * |
| 1224 | * @param parameterIndex |
| 1225 | * DOCUMENT ME! |
| 1226 | * @param longData |
| 1227 | * DOCUMENT ME! |
| 1228 | * |
| 1229 | * @throws SQLException |
| 1230 | * if an error occurs. |
| 1231 | */ |
| 1232 | private void serverLongData(int parameterIndex, BindValue longData) |
| 1233 | throws SQLException { |
| 1234 | synchronized (this.connection.getMutex()) { |
| 1235 | MysqlIO mysql = this.connection.getIO(); |
| 1236 | |
| 1237 | Buffer packet = mysql.getSharedSendPacket(); |
| 1238 | |
| 1239 | Object value = longData.value; |
| 1240 | |
| 1241 | if (value instanceof byte[]) { |
| 1242 | packet.clear(); |
| 1243 | packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); |
| 1244 | packet.writeLong(this.serverStatementId); |
| 1245 | packet.writeInt((parameterIndex)); |
| 1246 | |
| 1247 | packet.writeBytesNoNull((byte[]) longData.value); |
| 1248 | |
| 1249 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, |
| 1250 | null); |
| 1251 | } else if (value instanceof InputStream) { |
| 1252 | storeStream(mysql, parameterIndex, packet, (InputStream) value); |
| 1253 | } else if (value instanceof java.sql.Blob) { |
| 1254 | storeStream(mysql, parameterIndex, packet, |
| 1255 | ((java.sql.Blob) value).getBinaryStream()); |
| 1256 | } else if (value instanceof Reader) { |
| 1257 | storeReader(mysql, parameterIndex, packet, (Reader) value); |
| 1258 | } else { |
| 1259 | throw new SQLException(Messages |
| 1260 | .getString("ServerPreparedStatement.18") //$NON-NLS-1$ |
| 1261 | + value.getClass().getName() + "'", //$NON-NLS-1$ |
| 1262 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
| 1263 | } |
| 1264 | } |
| 1265 | } |
| 1266 | |
| 1267 | private void serverPrepare(String sql) throws SQLException { |
| 1268 | synchronized (this.connection.getMutex()) { |
| 1269 | MysqlIO mysql = this.connection.getIO(); |
| 1270 | |
| 1271 | if (this.connection.getAutoGenerateTestcaseScript()) { |
| 1272 | dumpPrepareForTestcase(); |
| 1273 | } |
| 1274 | |
| 1275 | try { |
| 1276 | long begin = 0; |
| 1277 | |
| 1278 | if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$ |
| 1279 | this.isLoadDataQuery = true; |
| 1280 | } else { |
| 1281 | this.isLoadDataQuery = false; |
| 1282 | } |
| 1283 | |
| 1284 | if (this.connection.getProfileSql()) { |
| 1285 | begin = System.currentTimeMillis(); |
| 1286 | } |
| 1287 | |
| 1288 | String characterEncoding = null; |
| 1289 | String connectionEncoding = this.connection.getEncoding(); |
| 1290 | |
| 1291 | if (!this.isLoadDataQuery && this.connection.getUseUnicode() |
| 1292 | && (connectionEncoding != null)) { |
| 1293 | characterEncoding = connectionEncoding; |
| 1294 | } |
| 1295 | |
| 1296 | Buffer prepareResultPacket = mysql.sendCommand( |
| 1297 | MysqlDefs.COM_PREPARE, sql, null, false, |
| 1298 | characterEncoding); |
| 1299 | |
| 1300 | if (this.connection.versionMeetsMinimum(4, 1, 1)) { |
| 1301 | // 4.1.1 and newer use the first byte |
| 1302 | // as an 'ok' or 'error' flag, so move |
| 1303 | // the buffer pointer past it to |
| 1304 | // start reading the statement id. |
| 1305 | prepareResultPacket.setPosition(1); |
| 1306 | } else { |
| 1307 | // 4.1.0 doesn't use the first byte as an |
| 1308 | // 'ok' or 'error' flag |
| 1309 | prepareResultPacket.setPosition(0); |
| 1310 | } |
| 1311 | |
| 1312 | this.serverStatementId = prepareResultPacket.readLong(); |
| 1313 | this.fieldCount = prepareResultPacket.readInt(); |
| 1314 | this.parameterCount = prepareResultPacket.readInt(); |
| 1315 | this.parameterBindings = new BindValue[this.parameterCount]; |
| 1316 | |
| 1317 | for (int i = 0; i < this.parameterCount; i++) { |
| 1318 | this.parameterBindings[i] = new BindValue(); |
| 1319 | } |
| 1320 | |
| 1321 | this.connection.incrementNumberOfPrepares(); |
| 1322 | |
| 1323 | if (this.connection.getProfileSql()) { |
| 1324 | this.eventSink = ProfileEventSink |
| 1325 | .getInstance(this.connection); |
| 1326 | |
| 1327 | this.eventSink.consumeEvent(new ProfilerEvent( |
| 1328 | ProfilerEvent.TYPE_PREPARE, |
| 1329 | "", this.currentCatalog, //$NON-NLS-1$ |
| 1330 | this.connection.getId(), this.statementId, -1, |
| 1331 | System.currentTimeMillis(), (int) (System |
| 1332 | .currentTimeMillis() - begin), null, |
| 1333 | new Throwable(), truncateQueryToLog(sql))); |
| 1334 | } |
| 1335 | |
| 1336 | if (this.parameterCount > 0) { |
| 1337 | if (this.connection.versionMeetsMinimum(4, 1, 2) |
| 1338 | && !mysql.isVersion(5, 0, 0)) { |
| 1339 | this.parameterFields = new Field[this.parameterCount]; |
| 1340 | |
| 1341 | Buffer metaDataPacket = mysql.readPacket(); |
| 1342 | |
| 1343 | int i = 0; |
| 1344 | |
| 1345 | while (!metaDataPacket.isLastDataPacket() |
| 1346 | && (i < this.parameterCount)) { |
| 1347 | this.parameterFields[i++] = mysql.unpackField( |
| 1348 | metaDataPacket, false); |
| 1349 | metaDataPacket = mysql.readPacket(); |
| 1350 | } |
| 1351 | } |
| 1352 | } |
| 1353 | |
| 1354 | if (this.fieldCount > 0) { |
| 1355 | this.resultFields = new Field[this.fieldCount]; |
| 1356 | |
| 1357 | Buffer fieldPacket = mysql.readPacket(); |
| 1358 | |
| 1359 | int i = 0; |
| 1360 | |
| 1361 | // Read in the result set column information |
| 1362 | while (!fieldPacket.isLastDataPacket() |
| 1363 | && (i < this.fieldCount)) { |
| 1364 | this.resultFields[i++] = mysql.unpackField(fieldPacket, |
| 1365 | false); |
| 1366 | fieldPacket = mysql.readPacket(); |
| 1367 | } |
| 1368 | } |
| 1369 | } catch (SQLException sqlEx) { |
| 1370 | if (this.connection.getDumpQueriesOnException()) { |
| 1371 | StringBuffer messageBuf = new StringBuffer(this.originalSql |
| 1372 | .length() + 32); |
| 1373 | messageBuf |
| 1374 | .append("\n\nQuery being prepared when exception was thrown:\n\n"); |
| 1375 | messageBuf.append(this.originalSql); |
| 1376 | |
| 1377 | sqlEx = Connection.appendMessageToException(sqlEx, |
| 1378 | messageBuf.toString()); |
| 1379 | } |
| 1380 | |
| 1381 | throw sqlEx; |
| 1382 | } finally { |
| 1383 | // Leave the I/O channel in a known state...there might be |
| 1384 | // packets out there |
| 1385 | // that we're not interested in |
| 1386 | this.connection.getIO().clearInputStream(); |
| 1387 | } |
| 1388 | } |
| 1389 | } |
| 1390 | |
| 1391 | private String truncateQueryToLog(String sql) { |
| 1392 | String query = null; |
| 1393 | |
| 1394 | if (sql.length() > this.connection.getMaxQuerySizeToLog()) { |
| 1395 | StringBuffer queryBuf = new StringBuffer( |
| 1396 | this.connection.getMaxQuerySizeToLog() + 12); |
| 1397 | queryBuf.append(sql.substring(0, this.connection.getMaxQuerySizeToLog())); |
| 1398 | queryBuf.append(Messages.getString("MysqlIO.25")); |
| 1399 | |
| 1400 | query = queryBuf.toString(); |
| 1401 | } else { |
| 1402 | query = sql; |
| 1403 | } |
| 1404 | |
| 1405 | return query; |
| 1406 | } |
| 1407 | |
| 1408 | private void serverResetStatement() throws SQLException { |
| 1409 | synchronized (this.connection.getMutex()) { |
| 1410 | |
| 1411 | MysqlIO mysql = this.connection.getIO(); |
| 1412 | |
| 1413 | Buffer packet = mysql.getSharedSendPacket(); |
| 1414 | |
| 1415 | packet.clear(); |
| 1416 | packet.writeByte((byte) MysqlDefs.COM_RESET_STMT); |
| 1417 | packet.writeLong(this.serverStatementId); |
| 1418 | |
| 1419 | try { |
| 1420 | mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null, packet, |
| 1421 | !this.connection.versionMeetsMinimum(4, 1, 2), null); |
| 1422 | } catch (SQLException sqlEx) { |
| 1423 | throw sqlEx; |
| 1424 | } catch (Exception ex) { |
| 1425 | throw new SQLException(ex.toString(), |
| 1426 | SQLError.SQL_STATE_GENERAL_ERROR); |
| 1427 | } finally { |
| 1428 | mysql.clearInputStream(); |
| 1429 | } |
| 1430 | } |
| 1431 | } |
| 1432 | |
| 1433 | /** |
| 1434 | * @see java.sql.PreparedStatement#setArray(int, java.sql.Array) |
| 1435 | */ |
| 1436 | public void setArray(int i, Array x) throws SQLException { |
| 1437 | throw new NotImplemented(); |
| 1438 | } |
| 1439 | |
| 1440 | /** |
| 1441 | * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream, |
| 1442 | * int) |
| 1443 | */ |
| 1444 | public void setAsciiStream(int parameterIndex, InputStream x, int length) |
| 1445 | throws SQLException { |
| 1446 | checkClosed(); |
| 1447 | |
| 1448 | if (x == null) { |
| 1449 | setNull(parameterIndex, java.sql.Types.BINARY); |
| 1450 | } else { |
| 1451 | BindValue binding = getBinding(parameterIndex, true); |
| 1452 | setType(binding, MysqlDefs.FIELD_TYPE_BLOB); |
| 1453 | |
| 1454 | binding.value = x; |
| 1455 | binding.isNull = false; |
| 1456 | binding.isLongData = true; |
| 1457 | |
| 1458 | if (this.connection.getUseStreamLengthsInPrepStmts()) { |
| 1459 | binding.bindLength = length; |
| 1460 | } else { |
| 1461 | binding.bindLength = -1; |
| 1462 | } |
| 1463 | } |
| 1464 | } |
| 1465 | |
| 1466 | /** |
| 1467 | * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal) |
| 1468 | */ |
| 1469 | public void setBigDecimal(int parameterIndex, BigDecimal x) |
| 1470 | throws SQLException { |
| 1471 | checkClosed(); |
| 1472 | |
| 1473 | if (x == null) { |
| 1474 | setNull(parameterIndex, java.sql.Types.DECIMAL); |
| 1475 | } else { |
| 1476 | setString(parameterIndex, StringUtils.fixDecimalExponent(x |
| 1477 | .toString())); |
| 1478 | } |
| 1479 | } |
| 1480 | |
| 1481 | /** |
| 1482 | * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream, |
| 1483 | * int) |
| 1484 | */ |
| 1485 | public void setBinaryStream(int parameterIndex, InputStream x, int length) |
| 1486 | throws SQLException { |
| 1487 | checkClosed(); |
| 1488 | |
| 1489 | if (x == null) { |
| 1490 | setNull(parameterIndex, java.sql.Types.BINARY); |
| 1491 | } else { |
| 1492 | BindValue binding = getBinding(parameterIndex, true); |
| 1493 | setType(binding, MysqlDefs.FIELD_TYPE_BLOB); |
| 1494 | |
| 1495 | binding.value = x; |
| 1496 | binding.isNull = false; |
| 1497 | binding.isLongData = true; |
| 1498 | |
| 1499 | if (this.connection.getUseStreamLengthsInPrepStmts()) { |
| 1500 | binding.bindLength = length; |
| 1501 | } else { |
| 1502 | binding.bindLength = -1; |
| 1503 | } |
| 1504 | } |
| 1505 | } |
| 1506 | |
| 1507 | /** |
| 1508 | * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob) |
| 1509 | */ |
| 1510 | public void setBlob(int parameterIndex, Blob x) throws SQLException { |
| 1511 | checkClosed(); |
| 1512 | |
| 1513 | if (x == null) { |
| 1514 | setNull(parameterIndex, java.sql.Types.BINARY); |
| 1515 | } else { |
| 1516 | BindValue binding = getBinding(parameterIndex, true); |
| 1517 | setType(binding, MysqlDefs.FIELD_TYPE_BLOB); |
| 1518 | |
| 1519 | binding.value = x; |
| 1520 | binding.isNull = false; |
| 1521 | binding.isLongData = true; |
| 1522 | |
| 1523 | if (this.connection.getUseStreamLengthsInPrepStmts()) { |
| 1524 | binding.bindLength = x.length(); |
| 1525 | } else { |
| 1526 | binding.bindLength = -1; |
| 1527 | } |
| 1528 | } |
| 1529 | } |
| 1530 | |
| 1531 | /** |
| 1532 | * @see java.sql.PreparedStatement#setBoolean(int, boolean) |
| 1533 | */ |
| 1534 | public void setBoolean(int parameterIndex, boolean x) throws SQLException { |
| 1535 | setByte(parameterIndex, (x ? (byte) 1 : (byte) 0)); |
| 1536 | } |
| 1537 | |
| 1538 | /** |
| 1539 | * @see java.sql.PreparedStatement#setByte(int, byte) |
| 1540 | */ |
| 1541 | public void setByte(int parameterIndex, byte x) throws SQLException { |
| 1542 | checkClosed(); |
| 1543 | |
| 1544 | BindValue binding = getBinding(parameterIndex, false); |
| 1545 | setType(binding, MysqlDefs.FIELD_TYPE_TINY); |
| 1546 | |
| 1547 | binding.value = null; |
| 1548 | binding.byteBinding = x; |
| 1549 | binding.isNull = false; |
| 1550 | binding.isLongData = false; |
| 1551 | } |
| 1552 | |
| 1553 | /** |
| 1554 | * @see java.sql.PreparedStatement#setBytes(int, byte) |
| 1555 | */ |
| 1556 | public void setBytes(int parameterIndex, byte[] x) throws SQLException { |
| 1557 | checkClosed(); |
| 1558 | |
| 1559 | if (x == null) { |
| 1560 | setNull(parameterIndex, java.sql.Types.BINARY); |
| 1561 | } else { |
| 1562 | BindValue binding = getBinding(parameterIndex, false); |
| 1563 | setType(binding, MysqlDefs.FIELD_TYPE_VAR_STRING); |
| 1564 | |
| 1565 | binding.value = x; |
| 1566 | binding.isNull = false; |
| 1567 | binding.isLongData = false; |
| 1568 | } |
| 1569 | } |
| 1570 | |
| 1571 | /** |
| 1572 | * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader, |
| 1573 | * int) |
| 1574 | */ |
| 1575 | public void setCharacterStream(int parameterIndex, Reader reader, int length) |
| 1576 | throws SQLException { |
| 1577 | checkClosed(); |
| 1578 | |
| 1579 | if (reader == null) { |
| 1580 | setNull(parameterIndex, java.sql.Types.BINARY); |
| 1581 | } else { |
| 1582 | BindValue binding = getBinding(parameterIndex, true); |
| 1583 | setType(binding, MysqlDefs.FIELD_TYPE_BLOB); |
| 1584 | |
| 1585 | binding.value = reader; |
| 1586 | binding.isNull = false; |
| 1587 | binding.isLongData = true; |
| 1588 | |
| 1589 | if (this.connection.getUseStreamLengthsInPrepStmts()) { |
| 1590 | binding.bindLength = length; |
| 1591 | } else { |
| 1592 | binding.bindLength = -1; |
| 1593 | } |
| 1594 | } |
| 1595 | } |
| 1596 | |
| 1597 | /** |
| 1598 | * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob) |
| 1599 | */ |
| 1600 | public void setClob(int parameterIndex, Clob x) throws SQLException { |
| 1601 | checkClosed(); |
| 1602 | |
| 1603 | if (x == null) { |
| 1604 | setNull(parameterIndex, java.sql.Types.BINARY); |
| 1605 | } else { |
| 1606 | BindValue binding = getBinding(parameterIndex, true); |
| 1607 | setType(binding, MysqlDefs.FIELD_TYPE_BLOB); |
| 1608 | |
| 1609 | binding.value = x.getCharacterStream(); |
| 1610 | binding.isNull = false; |
| 1611 | binding.isLongData = true; |
| 1612 | |
| 1613 | if (this.connection.getUseStreamLengthsInPrepStmts()) { |
| 1614 | binding.bindLength = x.length(); |
| 1615 | } else { |
| 1616 | binding.bindLength = -1; |
| 1617 | } |
| 1618 | } |
| 1619 | } |
| 1620 | |
| 1621 | /** |
| 1622 | * Set a parameter to a java.sql.Date value. The driver converts this to a |
| 1623 | * SQL DATE value when it sends it to the database. |
| 1624 | * |
| 1625 | * @param parameterIndex |
| 1626 | * the first parameter is 1, the second is 2, ... |
| 1627 | * @param x |
| 1628 | * the parameter value |
| 1629 | * |
| 1630 | * @exception SQLException |
| 1631 | * if a database-access error occurs. |
| 1632 | */ |
| 1633 | public void setDate(int parameterIndex, Date x) throws SQLException { |
| 1634 | setDate(parameterIndex, x, null); |
| 1635 | } |
| 1636 | |
| 1637 | /** |
| 1638 | * Set a parameter to a java.sql.Date value. The driver converts this to a |
| 1639 | * SQL DATE value when it sends it to the database. |
| 1640 | * |
| 1641 | * @param parameterIndex |
| 1642 | * the first parameter is 1, the second is 2, ... |
| 1643 | * @param x |
| 1644 | * the parameter value |
| 1645 | * @param cal |
| 1646 | * the calendar to interpret the date with |
| 1647 | * |
| 1648 | * @exception SQLException |
| 1649 | * if a database-access error occurs. |
| 1650 | */ |
| 1651 | public void setDate(int parameterIndex, Date x, Calendar cal) |
| 1652 | throws SQLException { |
| 1653 | if (x == null) { |
| 1654 | setNull(parameterIndex, java.sql.Types.DATE); |
| 1655 | } else { |
| 1656 | BindValue binding = getBinding(parameterIndex, false); |
| 1657 | setType(binding, MysqlDefs.FIELD_TYPE_DATE); |
| 1658 | |
| 1659 | binding.value = x; |
| 1660 | binding.isNull = false; |
| 1661 | binding.isLongData = false; |
| 1662 | } |
| 1663 | } |
| 1664 | |
| 1665 | /** |
| 1666 | * @see java.sql.PreparedStatement#setDouble(int, double) |
| 1667 | */ |
| 1668 | public void setDouble(int parameterIndex, double x) throws SQLException { |
| 1669 | checkClosed(); |
| 1670 | |
| 1671 | if (!this.connection.getAllowNanAndInf() |
| 1672 | && (x == Double.POSITIVE_INFINITY |
| 1673 | || x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) { |
| 1674 | throw new SQLException("'" + x |
| 1675 | + "' is not a valid numeric or approximate numeric value", |
| 1676 | SQLError.SQL_STATE_ILLEGAL_ARGUMENT); |
| 1677 | |
| 1678 | } |
| 1679 | |
| 1680 | BindValue binding = getBinding(parameterIndex, false); |
| 1681 | setType(binding, MysqlDefs.FIELD_TYPE_DOUBLE); |
| 1682 | |
| 1683 | binding.value = null; |
| 1684 | binding.doubleBinding = x; |
| 1685 | binding.isNull = false; |
| 1686 | binding.isLongData = false; |
| 1687 | } |
| 1688 | |
| 1689 | /** |
| 1690 | * @see java.sql.PreparedStatement#setFloat(int, float) |
| 1691 | */ |
| 1692 | public void setFloat(int parameterIndex, float x) throws SQLException { |
| 1693 | checkClosed(); |
| 1694 | |
| 1695 | BindValue binding = getBinding(parameterIndex, false); |
| 1696 | setType(binding, MysqlDefs.FIELD_TYPE_FLOAT); |
| 1697 | |
| 1698 | binding.value = null; |
| 1699 | binding.floatBinding = x; |
| 1700 | binding.isNull = false; |
| 1701 | binding.isLongData = false; |
| 1702 | } |
| 1703 | |
| 1704 | /** |
| 1705 | * @see java.sql.PreparedStatement#setInt(int, int) |
| 1706 | */ |
| 1707 | public void setInt(int parameterIndex, int x) throws SQLException { |
| 1708 | checkClosed(); |
| 1709 | |
| 1710 | BindValue binding = getBinding(parameterIndex, false); |
| 1711 | setType(binding, MysqlDefs.FIELD_TYPE_LONG); |
| 1712 | |
| 1713 | binding.value = null; |
| 1714 | binding.intBinding = x; |
| 1715 | binding.isNull = false; |
| 1716 | binding.isLongData = false; |
| 1717 | } |
| 1718 | |
| 1719 | /** |
| 1720 | * @see java.sql.PreparedStatement#setLong(int, long) |
| 1721 | */ |
| 1722 | public void setLong(int parameterIndex, long x) throws SQLException { |
| 1723 | checkClosed(); |
| 1724 | |
| 1725 | BindValue binding = getBinding(parameterIndex, false); |
| 1726 | setType(binding, MysqlDefs.FIELD_TYPE_LONGLONG); |
| 1727 | |
| 1728 | binding.value = null; |
| 1729 | binding.longBinding = x; |
| 1730 | binding.isNull = false; |
| 1731 | binding.isLongData = false; |
| 1732 | } |
| 1733 | |
| 1734 | /** |
| 1735 | * @see java.sql.PreparedStatement#setNull(int, int) |
| 1736 | */ |
| 1737 | public void setNull(int parameterIndex, int sqlType) throws SQLException { |
| 1738 | checkClosed(); |
| 1739 | |
| 1740 | BindValue binding = getBinding(parameterIndex, false); |
| 1741 | |
| 1742 | // |
| 1743 | // Don't re-set types, but use something if this |
| 1744 | // parameter was never specified |
| 1745 | // |
| 1746 | if (binding.bufferType == 0) { |
| 1747 | setType(binding, MysqlDefs.FIELD_TYPE_NULL); |
| 1748 | } |
| 1749 | |
| 1750 | binding.value = null; |
| 1751 | binding.isNull = true; |
| 1752 | binding.isLongData = false; |
| 1753 | } |
| 1754 | |
| 1755 | /** |
| 1756 | * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String) |
| 1757 | */ |
| 1758 | public void setNull(int parameterIndex, int sqlType, String typeName) |
| 1759 | throws SQLException { |
| 1760 | checkClosed(); |
| 1761 | |
| 1762 | BindValue binding = getBinding(parameterIndex, false); |
| 1763 | |
| 1764 | // |
| 1765 | // Don't re-set types, but use something if this |
| 1766 | // parameter was never specified |
| 1767 | // |
| 1768 | if (binding.bufferType == 0) { |
| 1769 | setType(binding, MysqlDefs.FIELD_TYPE_NULL); |
| 1770 | } |
| 1771 | |
| 1772 | binding.value = null; |
| 1773 | binding.isNull = true; |
| 1774 | binding.isLongData = false; |
| 1775 | } |
| 1776 | |
| 1777 | /** |
| 1778 | * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref) |
| 1779 | */ |
| 1780 | public void setRef(int i, Ref x) throws SQLException { |
| 1781 | throw new NotImplemented(); |
| 1782 | } |
| 1783 | |
| 1784 | /** |
| 1785 | * @see java.sql.PreparedStatement#setShort(int, short) |
| 1786 | */ |
| 1787 | public void setShort(int parameterIndex, short x) throws SQLException { |
| 1788 | checkClosed(); |
| 1789 | |
| 1790 | BindValue binding = getBinding(parameterIndex, false); |
| 1791 | setType(binding, MysqlDefs.FIELD_TYPE_SHORT); |
| 1792 | |
| 1793 | binding.value = null; |
| 1794 | binding.shortBinding = x; |
| 1795 | binding.isNull = false; |
| 1796 | binding.isLongData = false; |
| 1797 | } |
| 1798 | |
| 1799 | /** |
| 1800 | * @see java.sql.PreparedStatement#setString(int, java.lang.String) |
| 1801 | */ |
| 1802 | public void setString(int parameterIndex, String x) throws SQLException { |
| 1803 | checkClosed(); |
| 1804 | |
| 1805 | if (x == null) { |
| 1806 | setNull(parameterIndex, java.sql.Types.CHAR); |
| 1807 | } else { |
| 1808 | BindValue binding = getBinding(parameterIndex, false); |
| 1809 | |
| 1810 | setType(binding, this.stringTypeCode); |
| 1811 | |
| 1812 | binding.value = x; |
| 1813 | binding.isNull = false; |
| 1814 | binding.isLongData = false; |
| 1815 | } |
| 1816 | } |
| 1817 | |
| 1818 | /** |
| 1819 | * Set a parameter to a java.sql.Time value. |
| 1820 | * |
| 1821 | * @param parameterIndex |
| 1822 | * the first parameter is 1...)); |
| 1823 | * @param x |
| 1824 | * the parameter value |
| 1825 | * |
| 1826 | * @throws SQLException |
| 1827 | * if a database access error occurs |
| 1828 | */ |
| 1829 | public void setTime(int parameterIndex, java.sql.Time x) |
| 1830 | throws SQLException { |
| 1831 | setTimeInternal(parameterIndex, x, TimeZone.getDefault(), false); |
| 1832 | } |
| 1833 | |
| 1834 | /** |
| 1835 | * Set a parameter to a java.sql.Time value. The driver converts this to a |
| 1836 | * SQL TIME value when it sends it to the database, using the given |
| 1837 | * timezone. |
| 1838 | * |
| 1839 | * @param parameterIndex |
| 1840 | * the first parameter is 1...)); |
| 1841 | * @param x |
| 1842 | * the parameter value |
| 1843 | * @param cal |
| 1844 | * the timezone to use |
| 1845 | * |
| 1846 | * @throws SQLException |
| 1847 | * if a database access error occurs |
| 1848 | */ |
| 1849 | public void setTime(int parameterIndex, java.sql.Time x, Calendar cal) |
| 1850 | throws SQLException { |
| 1851 | setTimeInternal(parameterIndex, x, cal.getTimeZone(), true); |
| 1852 | } |
| 1853 | |
| 1854 | /** |
| 1855 | * Set a parameter to a java.sql.Time value. The driver converts this to a |
| 1856 | * SQL TIME value when it sends it to the database, using the given |
| 1857 | * timezone. |
| 1858 | * |
| 1859 | * @param parameterIndex |
| 1860 | * the first parameter is 1...)); |
| 1861 | * @param x |
| 1862 | * the parameter value |
| 1863 | * @param tz |
| 1864 | * the timezone to use |
| 1865 | * |
| 1866 | * @throws SQLException |
| 1867 | * if a database access error occurs |
| 1868 | */ |
| 1869 | public void setTimeInternal(int parameterIndex, java.sql.Time x, |
| 1870 | TimeZone tz, boolean rollForward) throws SQLException { |
| 1871 | if (x == null) { |
| 1872 | setNull(parameterIndex, java.sql.Types.TIME); |
| 1873 | } else { |
| 1874 | BindValue binding = getBinding(parameterIndex, false); |
| 1875 | setType(binding, MysqlDefs.FIELD_TYPE_TIME); |
| 1876 | |
| 1877 | binding.value = TimeUtil.changeTimezone(this.connection, x, tz, |
| 1878 | this.connection.getServerTimezoneTZ(), rollForward); |
| 1879 | binding.isNull = false; |
| 1880 | binding.isLongData = false; |
| 1881 | } |
| 1882 | } |
| 1883 | |
| 1884 | /** |
| 1885 | * Set a parameter to a java.sql.Timestamp value. The driver converts this |
| 1886 | * to a SQL TIMESTAMP value when it sends it to the database. |
| 1887 | * |
| 1888 | * @param parameterIndex |
| 1889 | * the first parameter is 1, the second is 2, ... |
| 1890 | * @param x |
| 1891 | * the parameter value |
| 1892 | * |
| 1893 | * @throws SQLException |
| 1894 | * if a database-access error occurs. |
| 1895 | */ |
| 1896 | public void setTimestamp(int parameterIndex, java.sql.Timestamp x) |
| 1897 | throws SQLException { |
| 1898 | setTimestampInternal(parameterIndex, x, TimeZone.getDefault(), false); |
| 1899 | } |
| 1900 | |
| 1901 | /** |
| 1902 | * Set a parameter to a java.sql.Timestamp value. The driver converts this |
| 1903 | * to a SQL TIMESTAMP value when it sends it to the database. |
| 1904 | * |
| 1905 | * @param parameterIndex |
| 1906 | * the first parameter is 1, the second is 2, ... |
| 1907 | * @param x |
| 1908 | * the parameter value |
| 1909 | * @param cal |
| 1910 | * the timezone to use |
| 1911 | * |
| 1912 | * @throws SQLException |
| 1913 | * if a database-access error occurs. |
| 1914 | */ |
| 1915 | public void setTimestamp(int parameterIndex, java.sql.Timestamp x, |
| 1916 | Calendar cal) throws SQLException { |
| 1917 | setTimestampInternal(parameterIndex, x, cal.getTimeZone(), true); |
| 1918 | } |
| 1919 | |
| 1920 | protected void setTimestampInternal(int parameterIndex, |
| 1921 | java.sql.Timestamp x, TimeZone tz, boolean rollForward) |
| 1922 | throws SQLException { |
| 1923 | if (x == null) { |
| 1924 | setNull(parameterIndex, java.sql.Types.TIMESTAMP); |
| 1925 | } else { |
| 1926 | BindValue binding = getBinding(parameterIndex, false); |
| 1927 | setType(binding, MysqlDefs.FIELD_TYPE_DATETIME); |
| 1928 | |
| 1929 | binding.value = TimeUtil.changeTimezone(this.connection, x, tz, |
| 1930 | this.connection.getServerTimezoneTZ(), rollForward); |
| 1931 | binding.isNull = false; |
| 1932 | binding.isLongData = false; |
| 1933 | } |
| 1934 | } |
| 1935 | |
| 1936 | private void setType(BindValue oldValue, int bufferType) { |
| 1937 | if (oldValue.bufferType != bufferType) { |
| 1938 | this.sendTypesToServer = true; |
| 1939 | } |
| 1940 | |
| 1941 | oldValue.bufferType = bufferType; |
| 1942 | } |
| 1943 | |
| 1944 | /** |
| 1945 | * DOCUMENT ME! |
| 1946 | * |
| 1947 | * @param parameterIndex |
| 1948 | * DOCUMENT ME! |
| 1949 | * @param x |
| 1950 | * DOCUMENT ME! |
| 1951 | * @param length |
| 1952 | * DOCUMENT ME! |
| 1953 | * |
| 1954 | * @throws SQLException |
| 1955 | * DOCUMENT ME! |
| 1956 | * @throws NotImplemented |
| 1957 | * DOCUMENT ME! |
| 1958 | * |
| 1959 | * @see java.sql.PreparedStatement#setUnicodeStream(int, |
| 1960 | * java.io.InputStream, int) |
| 1961 | * @deprecated |
| 1962 | */ |
| 1963 | public void setUnicodeStream(int parameterIndex, InputStream x, int length) |
| 1964 | throws SQLException { |
| 1965 | checkClosed(); |
| 1966 | |
| 1967 | throw new NotImplemented(); |
| 1968 | } |
| 1969 | |
| 1970 | /** |
| 1971 | * @see java.sql.PreparedStatement#setURL(int, java.net.URL) |
| 1972 | */ |
| 1973 | public void setURL(int parameterIndex, URL x) throws SQLException { |
| 1974 | checkClosed(); |
| 1975 | |
| 1976 | setString(parameterIndex, x.toString()); |
| 1977 | } |
| 1978 | |
| 1979 | /** |
| 1980 | * Method storeBinding. |
| 1981 | * |
| 1982 | * @param packet |
| 1983 | * @param bindValue |
| 1984 | * @param mysql |
| 1985 | * DOCUMENT ME! |
| 1986 | * |
| 1987 | * @throws SQLException |
| 1988 | * DOCUMENT ME! |
| 1989 | */ |
| 1990 | private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql) |
| 1991 | throws SQLException { |
| 1992 | try { |
| 1993 | Object value = bindValue.value; |
| 1994 | |
| 1995 | // |
| 1996 | // Handle primitives first |
| 1997 | // |
| 1998 | switch (bindValue.bufferType) { |
| 1999 | |
| 2000 | case MysqlDefs.FIELD_TYPE_TINY: |
| 2001 | packet.writeByte(bindValue.byteBinding); |
| 2002 | return; |
| 2003 | case MysqlDefs.FIELD_TYPE_SHORT: |
| 2004 | packet.ensureCapacity(2); |
| 2005 | packet.writeInt(bindValue.shortBinding); |
| 2006 | return; |
| 2007 | case MysqlDefs.FIELD_TYPE_LONG: |
| 2008 | packet.ensureCapacity(4); |
| 2009 | packet.writeLong(bindValue.intBinding); |
| 2010 | return; |
| 2011 | case MysqlDefs.FIELD_TYPE_LONGLONG: |
| 2012 | packet.ensureCapacity(8); |
| 2013 | packet.writeLongLong(bindValue.longBinding); |
| 2014 | return; |
| 2015 | case MysqlDefs.FIELD_TYPE_FLOAT: |
| 2016 | packet.ensureCapacity(4); |
| 2017 | packet.writeFloat(bindValue.floatBinding); |
| 2018 | return; |
| 2019 | case MysqlDefs.FIELD_TYPE_DOUBLE: |
| 2020 | packet.ensureCapacity(8); |
| 2021 | packet.writeDouble(bindValue.doubleBinding); |
| 2022 | return; |
| 2023 | case MysqlDefs.FIELD_TYPE_TIME: |
| 2024 | storeTime(packet, (Time) value); |
| 2025 | return; |
| 2026 | case MysqlDefs.FIELD_TYPE_DATE: |
| 2027 | case MysqlDefs.FIELD_TYPE_DATETIME: |
| 2028 | case MysqlDefs.FIELD_TYPE_TIMESTAMP: |
| 2029 | storeDateTime(packet, (java.util.Date) value, mysql); |
| 2030 | return; |
| 2031 | case MysqlDefs.FIELD_TYPE_VAR_STRING: |
| 2032 | case MysqlDefs.FIELD_TYPE_STRING: |
| 2033 | case MysqlDefs.FIELD_TYPE_VARCHAR: |
| 2034 | if (value instanceof byte[]) { |
| 2035 | packet.writeLenBytes((byte[]) value); |
| 2036 | } else if (!this.isLoadDataQuery) { |
| 2037 | packet.writeLenString((String) value, this.charEncoding, |
| 2038 | this.connection.getServerCharacterEncoding(), |
| 2039 | this.charConverter, this.connection |
| 2040 | .parserKnowsUnicode()); |
| 2041 | } else { |
| 2042 | packet.writeLenBytes(((String) value).getBytes()); |
| 2043 | } |
| 2044 | |
| 2045 | return; |
| 2046 | } |
| 2047 | |
| 2048 | |
| 2049 | } catch (UnsupportedEncodingException uEE) { |
| 2050 | throw new SQLException(Messages |
| 2051 | .getString("ServerPreparedStatement.22") //$NON-NLS-1$ |
| 2052 | + this.connection.getEncoding() + "'", //$NON-NLS-1$ |
| 2053 | SQLError.SQL_STATE_GENERAL_ERROR); |
| 2054 | } |
| 2055 | } |
| 2056 | |
| 2057 | private void storeDataTime412AndOlder(Buffer intoBuf, java.util.Date dt) |
| 2058 | throws SQLException { |
| 2059 | // This is synchronized on the connection by callers, so it is |
| 2060 | // safe to lazily-instantiate this... |
| 2061 | if (this.dateTimeBindingCal == null) { |
| 2062 | this.dateTimeBindingCal = Calendar.getInstance(); |
| 2063 | } |
| 2064 | |
| 2065 | this.dateTimeBindingCal.setTime(dt); |
| 2066 | |
| 2067 | intoBuf.ensureCapacity(8); |
| 2068 | intoBuf.writeByte((byte) 7); // length |
| 2069 | |
| 2070 | int year = this.dateTimeBindingCal.get(Calendar.YEAR); |
| 2071 | int month = this.dateTimeBindingCal.get(Calendar.MONTH) + 1; |
| 2072 | int date = this.dateTimeBindingCal.get(Calendar.DATE); |
| 2073 | |
| 2074 | intoBuf.writeInt(year); |
| 2075 | intoBuf.writeByte((byte) month); |
| 2076 | intoBuf.writeByte((byte) date); |
| 2077 | |
| 2078 | if (dt instanceof java.sql.Date) { |
| 2079 | intoBuf.writeByte((byte) 0); |
| 2080 | intoBuf.writeByte((byte) 0); |
| 2081 | intoBuf.writeByte((byte) 0); |
| 2082 | } else { |
| 2083 | intoBuf.writeByte((byte) this.dateTimeBindingCal |
| 2084 | .get(Calendar.HOUR_OF_DAY)); |
| 2085 | intoBuf.writeByte((byte) this.dateTimeBindingCal |
| 2086 | .get(Calendar.MINUTE)); |
| 2087 | intoBuf.writeByte((byte) this.dateTimeBindingCal |
| 2088 | .get(Calendar.SECOND)); |
| 2089 | } |
| 2090 | } |
| 2091 | |
| 2092 | private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql) |
| 2093 | throws SQLException { |
| 2094 | if (this.connection.versionMeetsMinimum(4, 1, 3)) { |
| 2095 | storeDateTime413AndNewer(intoBuf, dt); |
| 2096 | } else { |
| 2097 | storeDataTime412AndOlder(intoBuf, dt); |
| 2098 | } |
| 2099 | } |
| 2100 | |
| 2101 | private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt) |
| 2102 | throws SQLException { |
| 2103 | // This is synchronized on the connection by callers, so it is |
| 2104 | // safe to lazily-instantiate this... |
| 2105 | if (this.dateTimeBindingCal == null) { |
| 2106 | this.dateTimeBindingCal = Calendar.getInstance(); |
| 2107 | } |
| 2108 | |
| 2109 | this.dateTimeBindingCal.setTime(dt); |
| 2110 | |
| 2111 | byte length = (byte) 7; |
| 2112 | |
| 2113 | intoBuf.ensureCapacity(length); |
| 2114 | |
| 2115 | if (dt instanceof java.sql.Timestamp) { |
| 2116 | length = (byte) 11; |
| 2117 | } |
| 2118 | |
| 2119 | intoBuf.writeByte(length); // length |
| 2120 | |
| 2121 | int year = this.dateTimeBindingCal.get(Calendar.YEAR); |
| 2122 | int month = this.dateTimeBindingCal.get(Calendar.MONTH) + 1; |
| 2123 | int date = this.dateTimeBindingCal.get(Calendar.DATE); |
| 2124 | |
| 2125 | intoBuf.writeInt(year); |
| 2126 | intoBuf.writeByte((byte) month); |
| 2127 | intoBuf.writeByte((byte) date); |
| 2128 | |
| 2129 | if (dt instanceof java.sql.Date) { |
| 2130 | intoBuf.writeByte((byte) 0); |
| 2131 | intoBuf.writeByte((byte) 0); |
| 2132 | intoBuf.writeByte((byte) 0); |
| 2133 | } else { |
| 2134 | intoBuf.writeByte((byte) this.dateTimeBindingCal |
| 2135 | .get(Calendar.HOUR_OF_DAY)); |
| 2136 | intoBuf.writeByte((byte) this.dateTimeBindingCal |
| 2137 | .get(Calendar.MINUTE)); |
| 2138 | intoBuf.writeByte((byte) this.dateTimeBindingCal |
| 2139 | .get(Calendar.SECOND)); |
| 2140 | } |
| 2141 | |
| 2142 | if (length == 11) { |
| 2143 | intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos()); |
| 2144 | } |
| 2145 | } |
| 2146 | |
| 2147 | // |
| 2148 | // TO DO: Investigate using NIO to do this faster |
| 2149 | // |
| 2150 | private void storeReader(MysqlIO mysql, int parameterIndex, Buffer packet, |
| 2151 | Reader inStream) throws SQLException { |
| 2152 | int maxBytesChar = 2; |
| 2153 | |
| 2154 | if (this.connection.getEncoding() != null) { |
| 2155 | maxBytesChar = this.connection.getMaxBytesPerChar( |
| 2156 | this.connection.getEncoding()); |
| 2157 | |
| 2158 | if (maxBytesChar == 1) { |
| 2159 | maxBytesChar = 2; // for safety |
| 2160 | } |
| 2161 | } |
| 2162 | |
| 2163 | char[] buf = new char[BLOB_STREAM_READ_BUF_SIZE / maxBytesChar]; |
| 2164 | |
| 2165 | int numRead = 0; |
| 2166 | |
| 2167 | int bytesInPacket = 0; |
| 2168 | int totalBytesRead = 0; |
| 2169 | int bytesReadAtLastSend = 0; |
| 2170 | int packetIsFullAt = this.connection.getBlobSendChunkSize(); |
| 2171 | |
| 2172 | try { |
| 2173 | packet.clear(); |
| 2174 | packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); |
| 2175 | packet.writeLong(this.serverStatementId); |
| 2176 | packet.writeInt((parameterIndex)); |
| 2177 | |
| 2178 | boolean readAny = false; |
| 2179 | |
| 2180 | while ((numRead = inStream.read(buf)) != -1) { |
| 2181 | readAny = true; |
| 2182 | |
| 2183 | byte[] valueAsBytes = StringUtils.getBytes(buf, null, |
| 2184 | this.connection.getEncoding(), this.connection |
| 2185 | .getServerCharacterEncoding(), 0, numRead, |
| 2186 | this.connection.parserKnowsUnicode()); |
| 2187 | |
| 2188 | packet.writeBytesNoNull(valueAsBytes, 0, valueAsBytes.length); |
| 2189 | |
| 2190 | bytesInPacket += valueAsBytes.length; |
| 2191 | totalBytesRead += valueAsBytes.length; |
| 2192 | |
| 2193 | if (bytesInPacket >= packetIsFullAt) { |
| 2194 | bytesReadAtLastSend = totalBytesRead; |
| 2195 | |
| 2196 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, |
| 2197 | true, null); |
| 2198 | |
| 2199 | bytesInPacket = 0; |
| 2200 | packet.clear(); |
| 2201 | packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); |
| 2202 | packet.writeLong(this.serverStatementId); |
| 2203 | packet.writeInt((parameterIndex)); |
| 2204 | } |
| 2205 | } |
| 2206 | |
| 2207 | if (totalBytesRead != bytesReadAtLastSend) { |
| 2208 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, |
| 2209 | null); |
| 2210 | } |
| 2211 | |
| 2212 | if (!readAny) { |
| 2213 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, |
| 2214 | null); |
| 2215 | } |
| 2216 | } catch (IOException ioEx) { |
| 2217 | throw new SQLException(Messages |
| 2218 | .getString("ServerPreparedStatement.24") //$NON-NLS-1$ |
| 2219 | + ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR); |
| 2220 | } finally { |
| 2221 | if (this.connection.getAutoClosePStmtStreams()) { |
| 2222 | if (inStream != null) { |
| 2223 | try { |
| 2224 | inStream.close(); |
| 2225 | } catch (IOException ioEx) { |
| 2226 | ; // ignore |
| 2227 | } |
| 2228 | } |
| 2229 | } |
| 2230 | } |
| 2231 | } |
| 2232 | |
| 2233 | private void storeStream(MysqlIO mysql, int parameterIndex, Buffer packet, |
| 2234 | InputStream inStream) throws SQLException { |
| 2235 | byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE]; |
| 2236 | |
| 2237 | int numRead = 0; |
| 2238 | |
| 2239 | try { |
| 2240 | int bytesInPacket = 0; |
| 2241 | int totalBytesRead = 0; |
| 2242 | int bytesReadAtLastSend = 0; |
| 2243 | int packetIsFullAt = this.connection.getBlobSendChunkSize(); |
| 2244 | |
| 2245 | packet.clear(); |
| 2246 | packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); |
| 2247 | packet.writeLong(this.serverStatementId); |
| 2248 | packet.writeInt((parameterIndex)); |
| 2249 | |
| 2250 | boolean readAny = false; |
| 2251 | |
| 2252 | while ((numRead = inStream.read(buf)) != -1) { |
| 2253 | |
| 2254 | readAny = true; |
| 2255 | |
| 2256 | packet.writeBytesNoNull(buf, 0, numRead); |
| 2257 | bytesInPacket += numRead; |
| 2258 | totalBytesRead += numRead; |
| 2259 | |
| 2260 | if (bytesInPacket >= packetIsFullAt) { |
| 2261 | bytesReadAtLastSend = totalBytesRead; |
| 2262 | |
| 2263 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, |
| 2264 | true, null); |
| 2265 | |
| 2266 | bytesInPacket = 0; |
| 2267 | packet.clear(); |
| 2268 | packet.writeByte((byte) MysqlDefs.COM_LONG_DATA); |
| 2269 | packet.writeLong(this.serverStatementId); |
| 2270 | packet.writeInt((parameterIndex)); |
| 2271 | } |
| 2272 | } |
| 2273 | |
| 2274 | if (totalBytesRead != bytesReadAtLastSend) { |
| 2275 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, |
| 2276 | null); |
| 2277 | } |
| 2278 | |
| 2279 | if (!readAny) { |
| 2280 | mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, |
| 2281 | null); |
| 2282 | } |
| 2283 | } catch (IOException ioEx) { |
| 2284 | throw new SQLException(Messages |
| 2285 | .getString("ServerPreparedStatement.25") //$NON-NLS-1$ |
| 2286 | + ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR); |
| 2287 | } finally { |
| 2288 | if (this.connection.getAutoClosePStmtStreams()) { |
| 2289 | if (inStream != null) { |
| 2290 | try { |
| 2291 | inStream.close(); |
| 2292 | } catch (IOException ioEx) { |
| 2293 | ; // ignore |
| 2294 | } |
| 2295 | } |
| 2296 | } |
| 2297 | } |
| 2298 | } |
| 2299 | |
| 2300 | /** |
| 2301 | * @see java.lang.Object#toString() |
| 2302 | */ |
| 2303 | public String toString() { |
| 2304 | StringBuffer toStringBuf = new StringBuffer(); |
| 2305 | |
| 2306 | toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); //$NON-NLS-1$ |
| 2307 | toStringBuf.append(this.serverStatementId); |
| 2308 | toStringBuf.append("] - "); //$NON-NLS-1$ |
| 2309 | |
| 2310 | try { |
| 2311 | toStringBuf.append(asSql()); |
| 2312 | } catch (SQLException sqlEx) { |
| 2313 | toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); //$NON-NLS-1$ |
| 2314 | toStringBuf.append(sqlEx); |
| 2315 | } |
| 2316 | |
| 2317 | return toStringBuf.toString(); |
| 2318 | } |
| 2319 | } |