PageRenderTime 36ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/EQEmuJSM/mysql-connector-java-5.1.13/src/com/mysql/jdbc/PreparedStatement.java

http://cubbers-eqemu-utils.googlecode.com/
Java | 2249 lines | 2118 code | 58 blank | 73 comment | 76 complexity | 65e2843bdff65e8c0e614863d1704fb4 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  3. The MySQL Connector/J is licensed under the terms of the GPL,
  4. like most MySQL Connectors. There are special exceptions to the
  5. terms and conditions of the GPL as it is applied to this software,
  6. see the FLOSS License Exception available on mysql.com.
  7. This program is free software; you can redistribute it and/or
  8. modify it under the terms of the GNU General Public License as
  9. published by the Free Software Foundation; version 2 of the
  10. License.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  18. 02110-1301 USA
  19. */
  20. package com.mysql.jdbc;
  21. import java.io.ByteArrayInputStream;
  22. import java.io.ByteArrayOutputStream;
  23. import java.io.IOException;
  24. import java.io.InputStream;
  25. import java.io.ObjectOutputStream;
  26. import java.io.Reader;
  27. import java.io.StringReader;
  28. import java.io.UnsupportedEncodingException;
  29. import java.lang.reflect.Constructor;
  30. import java.math.BigDecimal;
  31. import java.math.BigInteger;
  32. import java.net.URL;
  33. import java.nio.ByteBuffer;
  34. import java.nio.CharBuffer;
  35. import java.nio.charset.Charset;
  36. import java.nio.charset.CharsetEncoder;
  37. import java.sql.Array;
  38. import java.sql.Clob;
  39. import java.sql.DatabaseMetaData;
  40. import java.sql.Date;
  41. import java.sql.ParameterMetaData;
  42. import java.sql.Ref;
  43. import java.sql.ResultSet;
  44. import java.sql.SQLException;
  45. import java.sql.Time;
  46. import java.sql.Timestamp;
  47. import java.sql.Types;
  48. import java.text.ParsePosition;
  49. import java.text.SimpleDateFormat;
  50. import java.util.ArrayList;
  51. import java.util.Calendar;
  52. import java.util.Iterator;
  53. import java.util.LinkedList;
  54. import java.util.List;
  55. import java.util.Locale;
  56. import java.util.TimeZone;
  57. import com.mysql.jdbc.exceptions.DeadlockTimeoutRollbackMarker;
  58. import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
  59. import com.mysql.jdbc.exceptions.MySQLTimeoutException;
  60. import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException;
  61. import com.mysql.jdbc.profiler.ProfilerEvent;
  62. /**
  63. * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
  64. * This object can then be used to efficiently execute this statement multiple
  65. * times.
  66. *
  67. * <p>
  68. * <B>Note:</B> The setXXX methods for setting IN parameter values must specify
  69. * types that are compatible with the defined SQL type of the input parameter.
  70. * For instance, if the IN parameter has SQL type Integer, then setInt should be
  71. * used.
  72. * </p>
  73. *
  74. * <p>
  75. * If arbitrary parameter type conversions are required, then the setObject
  76. * method should be used with a target SQL type.
  77. * </p>
  78. *
  79. * @author Mark Matthews
  80. * @version $Id: PreparedStatement.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
  81. * Exp $
  82. *
  83. * @see java.sql.ResultSet
  84. * @see java.sql.PreparedStatement
  85. */
  86. public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements
  87. java.sql.PreparedStatement {
  88. private static final Constructor JDBC_4_PSTMT_2_ARG_CTOR;
  89. private static final Constructor JDBC_4_PSTMT_3_ARG_CTOR;
  90. private static final Constructor JDBC_4_PSTMT_4_ARG_CTOR;
  91. static {
  92. if (Util.isJdbc4()) {
  93. try {
  94. JDBC_4_PSTMT_2_ARG_CTOR = Class.forName(
  95. "com.mysql.jdbc.JDBC4PreparedStatement")
  96. .getConstructor(
  97. new Class[] { MySQLConnection.class, String.class });
  98. JDBC_4_PSTMT_3_ARG_CTOR = Class.forName(
  99. "com.mysql.jdbc.JDBC4PreparedStatement")
  100. .getConstructor(
  101. new Class[] { MySQLConnection.class, String.class,
  102. String.class });
  103. JDBC_4_PSTMT_4_ARG_CTOR = Class.forName(
  104. "com.mysql.jdbc.JDBC4PreparedStatement")
  105. .getConstructor(
  106. new Class[] { MySQLConnection.class, String.class,
  107. String.class, ParseInfo.class });
  108. } catch (SecurityException e) {
  109. throw new RuntimeException(e);
  110. } catch (NoSuchMethodException e) {
  111. throw new RuntimeException(e);
  112. } catch (ClassNotFoundException e) {
  113. throw new RuntimeException(e);
  114. }
  115. } else {
  116. JDBC_4_PSTMT_2_ARG_CTOR = null;
  117. JDBC_4_PSTMT_3_ARG_CTOR = null;
  118. JDBC_4_PSTMT_4_ARG_CTOR = null;
  119. }
  120. }
  121. class BatchParams {
  122. boolean[] isNull = null;
  123. boolean[] isStream = null;
  124. InputStream[] parameterStreams = null;
  125. byte[][] parameterStrings = null;
  126. int[] streamLengths = null;
  127. BatchParams(byte[][] strings, InputStream[] streams,
  128. boolean[] isStreamFlags, int[] lengths, boolean[] isNullFlags) {
  129. //
  130. // Make copies
  131. //
  132. this.parameterStrings = new byte[strings.length][];
  133. this.parameterStreams = new InputStream[streams.length];
  134. this.isStream = new boolean[isStreamFlags.length];
  135. this.streamLengths = new int[lengths.length];
  136. this.isNull = new boolean[isNullFlags.length];
  137. System.arraycopy(strings, 0, this.parameterStrings, 0,
  138. strings.length);
  139. System.arraycopy(streams, 0, this.parameterStreams, 0,
  140. streams.length);
  141. System.arraycopy(isStreamFlags, 0, this.isStream, 0,
  142. isStreamFlags.length);
  143. System.arraycopy(lengths, 0, this.streamLengths, 0, lengths.length);
  144. System
  145. .arraycopy(isNullFlags, 0, this.isNull, 0,
  146. isNullFlags.length);
  147. }
  148. }
  149. class EndPoint {
  150. int begin;
  151. int end;
  152. EndPoint(int b, int e) {
  153. this.begin = b;
  154. this.end = e;
  155. }
  156. }
  157. class ParseInfo {
  158. char firstStmtChar = 0;
  159. boolean foundLimitClause = false;
  160. boolean foundLoadData = false;
  161. long lastUsed = 0;
  162. int statementLength = 0;
  163. int statementStartPos = 0;
  164. boolean canRewriteAsMultiValueInsert = false;
  165. byte[][] staticSql = null;
  166. boolean isOnDuplicateKeyUpdate = false;
  167. int locationOfOnDuplicateKeyUpdate = -1;
  168. String valuesClause;
  169. boolean parametersInDuplicateKeyClause = false;
  170. /**
  171. * Represents the "parsed" state of a client-side
  172. * prepared statement, with the statement broken up into
  173. * it's static and dynamic (where parameters are bound)
  174. * parts.
  175. */
  176. ParseInfo(String sql, MySQLConnection conn,
  177. java.sql.DatabaseMetaData dbmd, String encoding,
  178. SingleByteCharsetConverter converter) throws SQLException {
  179. this(sql, conn, dbmd, encoding, converter, true);
  180. }
  181. public ParseInfo(String sql, MySQLConnection conn,
  182. java.sql.DatabaseMetaData dbmd, String encoding,
  183. SingleByteCharsetConverter converter, boolean buildRewriteInfo) throws SQLException {
  184. try {
  185. if (sql == null) {
  186. throw SQLError.createSQLException(Messages
  187. .getString("PreparedStatement.61"), //$NON-NLS-1$
  188. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  189. }
  190. this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(sql);
  191. this.isOnDuplicateKeyUpdate = this.locationOfOnDuplicateKeyUpdate != -1;
  192. this.lastUsed = System.currentTimeMillis();
  193. String quotedIdentifierString = dbmd.getIdentifierQuoteString();
  194. char quotedIdentifierChar = 0;
  195. if ((quotedIdentifierString != null)
  196. && !quotedIdentifierString.equals(" ") //$NON-NLS-1$
  197. && (quotedIdentifierString.length() > 0)) {
  198. quotedIdentifierChar = quotedIdentifierString.charAt(0);
  199. }
  200. this.statementLength = sql.length();
  201. ArrayList endpointList = new ArrayList();
  202. boolean inQuotes = false;
  203. char quoteChar = 0;
  204. boolean inQuotedId = false;
  205. int lastParmEnd = 0;
  206. int i;
  207. int stopLookingForLimitClause = this.statementLength - 5;
  208. this.foundLimitClause = false;
  209. boolean noBackslashEscapes = connection.isNoBackslashEscapesSet();
  210. // we're not trying to be real pedantic here, but we'd like to
  211. // skip comments at the beginning of statements, as frameworks
  212. // such as Hibernate use them to aid in debugging
  213. statementStartPos = findStartOfStatement(sql);
  214. for (i = statementStartPos; i < this.statementLength; ++i) {
  215. char c = sql.charAt(i);
  216. if ((this.firstStmtChar == 0) && Character.isLetter(c)) {
  217. // Determine what kind of statement we're doing (_S_elect,
  218. // _I_nsert, etc.)
  219. this.firstStmtChar = Character.toUpperCase(c);
  220. }
  221. if (!noBackslashEscapes &&
  222. c == '\\' && i < (this.statementLength - 1)) {
  223. i++;
  224. continue; // next character is escaped
  225. }
  226. // are we in a quoted identifier?
  227. // (only valid when the id is not inside a 'string')
  228. if (!inQuotes && (quotedIdentifierChar != 0)
  229. && (c == quotedIdentifierChar)) {
  230. inQuotedId = !inQuotedId;
  231. } else if (!inQuotedId) {
  232. // only respect quotes when not in a quoted identifier
  233. if (inQuotes) {
  234. if (((c == '\'') || (c == '"')) && c == quoteChar) {
  235. if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) {
  236. i++;
  237. continue; // inline quote escape
  238. }
  239. inQuotes = !inQuotes;
  240. quoteChar = 0;
  241. } else if (((c == '\'') || (c == '"')) && c == quoteChar) {
  242. inQuotes = !inQuotes;
  243. quoteChar = 0;
  244. }
  245. } else {
  246. if (c == '#'
  247. || (c == '-' && (i + 1) < this.statementLength && sql
  248. .charAt(i + 1) == '-')) {
  249. // run out to end of statement, or newline,
  250. // whichever comes first
  251. int endOfStmt = this.statementLength - 1;
  252. for (; i < endOfStmt; i++) {
  253. c = sql.charAt(i);
  254. if (c == '\r' || c == '\n') {
  255. break;
  256. }
  257. }
  258. continue;
  259. } else if (c == '/' && (i + 1) < this.statementLength) {
  260. // Comment?
  261. char cNext = sql.charAt(i + 1);
  262. if (cNext == '*') {
  263. i+= 2;
  264. for (int j = i; j < this.statementLength; j++) {
  265. i++;
  266. cNext = sql.charAt(j);
  267. if (cNext == '*' && (j + 1) < this.statementLength) {
  268. if (sql.charAt(j + 1) == '/') {
  269. i++;
  270. if (i < this.statementLength) {
  271. c = sql.charAt(i);
  272. }
  273. break; // comment done
  274. }
  275. }
  276. }
  277. }
  278. } else if ((c == '\'') || (c == '"')) {
  279. inQuotes = true;
  280. quoteChar = c;
  281. }
  282. }
  283. }
  284. if ((c == '?') && !inQuotes && !inQuotedId) {
  285. endpointList.add(new int[] { lastParmEnd, i });
  286. lastParmEnd = i + 1;
  287. if (isOnDuplicateKeyUpdate && i > locationOfOnDuplicateKeyUpdate) {
  288. parametersInDuplicateKeyClause = true;
  289. }
  290. }
  291. if (!inQuotes && (i < stopLookingForLimitClause)) {
  292. if ((c == 'L') || (c == 'l')) {
  293. char posI1 = sql.charAt(i + 1);
  294. if ((posI1 == 'I') || (posI1 == 'i')) {
  295. char posM = sql.charAt(i + 2);
  296. if ((posM == 'M') || (posM == 'm')) {
  297. char posI2 = sql.charAt(i + 3);
  298. if ((posI2 == 'I') || (posI2 == 'i')) {
  299. char posT = sql.charAt(i + 4);
  300. if ((posT == 'T') || (posT == 't')) {
  301. foundLimitClause = true;
  302. }
  303. }
  304. }
  305. }
  306. }
  307. }
  308. }
  309. if (this.firstStmtChar == 'L') {
  310. if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
  311. this.foundLoadData = true;
  312. } else {
  313. this.foundLoadData = false;
  314. }
  315. } else {
  316. this.foundLoadData = false;
  317. }
  318. endpointList.add(new int[] { lastParmEnd, this.statementLength });
  319. this.staticSql = new byte[endpointList.size()][];
  320. char[] asCharArray = sql.toCharArray();
  321. for (i = 0; i < this.staticSql.length; i++) {
  322. int[] ep = (int[]) endpointList.get(i);
  323. int end = ep[1];
  324. int begin = ep[0];
  325. int len = end - begin;
  326. if (this.foundLoadData) {
  327. String temp = new String(asCharArray, begin, len);
  328. this.staticSql[i] = temp.getBytes();
  329. } else if (encoding == null) {
  330. byte[] buf = new byte[len];
  331. for (int j = 0; j < len; j++) {
  332. buf[j] = (byte) sql.charAt(begin + j);
  333. }
  334. this.staticSql[i] = buf;
  335. } else {
  336. if (converter != null) {
  337. this.staticSql[i] = StringUtils.getBytes(sql,
  338. converter, encoding, connection
  339. .getServerCharacterEncoding(), begin,
  340. len, connection.parserKnowsUnicode(), getExceptionInterceptor());
  341. } else {
  342. String temp = new String(asCharArray, begin, len);
  343. this.staticSql[i] = StringUtils.getBytes(temp,
  344. encoding, connection
  345. .getServerCharacterEncoding(),
  346. connection.parserKnowsUnicode(), conn, getExceptionInterceptor());
  347. }
  348. }
  349. }
  350. } catch (StringIndexOutOfBoundsException oobEx) {
  351. SQLException sqlEx = new SQLException("Parse error for " + sql);
  352. sqlEx.initCause(oobEx);
  353. throw sqlEx;
  354. }
  355. if (buildRewriteInfo) {
  356. this.canRewriteAsMultiValueInsert = PreparedStatement
  357. .canRewrite(sql, this.isOnDuplicateKeyUpdate,
  358. this.locationOfOnDuplicateKeyUpdate,
  359. this.statementStartPos) && !this.parametersInDuplicateKeyClause;
  360. if (this.canRewriteAsMultiValueInsert
  361. && conn.getRewriteBatchedStatements()) {
  362. buildRewriteBatchedParams(sql, conn, dbmd, encoding,
  363. converter);
  364. }
  365. }
  366. }
  367. private ParseInfo batchHead;
  368. private ParseInfo batchValues;
  369. private ParseInfo batchODKUClause;
  370. private void buildRewriteBatchedParams(String sql, MySQLConnection conn,
  371. DatabaseMetaData metadata, String encoding,
  372. SingleByteCharsetConverter converter) throws SQLException {
  373. this.valuesClause = extractValuesClause(sql);
  374. String odkuClause = isOnDuplicateKeyUpdate ? sql
  375. .substring(locationOfOnDuplicateKeyUpdate) : null;
  376. String headSql = null;
  377. if (isOnDuplicateKeyUpdate) {
  378. headSql = sql.substring(0, locationOfOnDuplicateKeyUpdate);
  379. } else {
  380. headSql = sql;
  381. }
  382. this.batchHead = new ParseInfo(headSql, conn, metadata, encoding,
  383. converter, false);
  384. this.batchValues = new ParseInfo("," + this.valuesClause, conn,
  385. metadata, encoding, converter, false);
  386. this.batchODKUClause = null;
  387. if (odkuClause != null && odkuClause.length() > 0) {
  388. this.batchODKUClause = new ParseInfo("," + this.valuesClause
  389. + " " + odkuClause, conn, metadata, encoding,
  390. converter, false);
  391. }
  392. }
  393. private String extractValuesClause(String sql) throws SQLException {
  394. String quoteCharStr = connection.getMetaData()
  395. .getIdentifierQuoteString();
  396. int indexOfValues = -1;
  397. int valuesSearchStart = statementStartPos;
  398. while (indexOfValues == -1) {
  399. if (quoteCharStr.length() > 0) {
  400. indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(
  401. valuesSearchStart,
  402. originalSql, "VALUES", quoteCharStr.charAt(0), false);
  403. } else {
  404. indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart,
  405. originalSql,
  406. "VALUES");
  407. }
  408. if (indexOfValues > 0) {
  409. /* check if the char immediately preceding VALUES may be part of the table name */
  410. char c = originalSql.charAt(indexOfValues - 1);
  411. if(!(Character.isWhitespace(c) || c == ')' || c == '`')){
  412. valuesSearchStart = indexOfValues + 6;
  413. indexOfValues = -1;
  414. } else {
  415. /* check if the char immediately following VALUES may be whitespace or open parenthesis */
  416. c = originalSql.charAt(indexOfValues + 6);
  417. if(!(Character.isWhitespace(c) || c == '(')){
  418. valuesSearchStart = indexOfValues + 6;
  419. indexOfValues = -1;
  420. }
  421. }
  422. } else {
  423. break;
  424. }
  425. }
  426. if (indexOfValues == -1) {
  427. return null;
  428. }
  429. int indexOfFirstParen = sql.indexOf('(', indexOfValues + 6);
  430. if (indexOfFirstParen == -1) {
  431. return null;
  432. }
  433. int endOfValuesClause = sql.lastIndexOf(')');
  434. if (endOfValuesClause == -1) {
  435. return null;
  436. }
  437. if (isOnDuplicateKeyUpdate) {
  438. endOfValuesClause = this.locationOfOnDuplicateKeyUpdate - 1;
  439. }
  440. return sql.substring(indexOfFirstParen, endOfValuesClause + 1);
  441. }
  442. /**
  443. * Returns a ParseInfo for a multi-value INSERT for a batch of size numBatch (without parsing!).
  444. */
  445. synchronized ParseInfo getParseInfoForBatch(int numBatch) {
  446. AppendingBatchVisitor apv = new AppendingBatchVisitor();
  447. buildInfoForBatch(numBatch, apv);
  448. ParseInfo batchParseInfo = new ParseInfo(apv.getStaticSqlStrings(),
  449. this.firstStmtChar, this.foundLimitClause,
  450. this.foundLoadData, this.isOnDuplicateKeyUpdate,
  451. this.locationOfOnDuplicateKeyUpdate, this.statementLength,
  452. this.statementStartPos);
  453. return batchParseInfo;
  454. }
  455. /**
  456. * Returns a preparable SQL string for the number of batched parameters, used by server-side prepared statements
  457. * when re-writing batch INSERTs.
  458. */
  459. String getSqlForBatch(int numBatch) throws UnsupportedEncodingException {
  460. ParseInfo batchInfo = getParseInfoForBatch(numBatch);
  461. return getSqlForBatch(batchInfo);
  462. }
  463. /**
  464. * Used for filling in the SQL for getPreparedSql() - for debugging
  465. */
  466. String getSqlForBatch(ParseInfo batchInfo) throws UnsupportedEncodingException {
  467. int size = 0;
  468. final byte[][] sqlStrings = batchInfo.staticSql;
  469. final int sqlStringsLength = sqlStrings.length;
  470. for (int i = 0; i < sqlStringsLength; i++) {
  471. size += sqlStrings[i].length;
  472. size++; // for the '?'
  473. }
  474. StringBuffer buf = new StringBuffer(size);
  475. for (int i = 0; i < sqlStringsLength - 1; i++) {
  476. buf.append(new String(sqlStrings[i], charEncoding));
  477. buf.append("?");
  478. }
  479. buf.append(new String(sqlStrings[sqlStringsLength - 1]));
  480. return buf.toString();
  481. }
  482. /**
  483. * Builds a ParseInfo for the given batch size, without parsing. We use
  484. * a visitor pattern here, because the if {}s make computing a size for the
  485. * resultant byte[][] make this too complex, and we don't necessarily want to
  486. * use a List for this, because the size can be dynamic, and thus we'll not be
  487. * able to guess a good initial size for an array-based list, and it's not
  488. * efficient to convert a LinkedList to an array.
  489. */
  490. private void buildInfoForBatch(int numBatch, BatchVisitor visitor) {
  491. final byte[][] headStaticSql = this.batchHead.staticSql;
  492. final int headStaticSqlLength = headStaticSql.length;
  493. if (headStaticSqlLength > 1) {
  494. for (int i = 0; i < headStaticSqlLength - 1; i++) {
  495. visitor.append(headStaticSql[i]).increment();
  496. }
  497. }
  498. // merge end of head, with beginning of a value clause
  499. byte[] endOfHead = headStaticSql[headStaticSqlLength - 1];
  500. final byte[][] valuesStaticSql = this.batchValues.staticSql;
  501. byte[] beginOfValues = valuesStaticSql[0];
  502. visitor.merge(endOfHead, beginOfValues).increment();
  503. int numValueRepeats = numBatch - 1; // first one is in the "head"
  504. if (this.batchODKUClause != null) {
  505. numValueRepeats--; // Last one is in the ODKU clause
  506. }
  507. final int valuesStaticSqlLength = valuesStaticSql.length;
  508. byte[] endOfValues = valuesStaticSql[valuesStaticSqlLength - 1];
  509. for (int i = 0; i < numValueRepeats; i++) {
  510. for (int j = 1; j < valuesStaticSqlLength - 1; j++) {
  511. visitor.append(valuesStaticSql[j]).increment();
  512. }
  513. visitor.merge(endOfValues, beginOfValues).increment();
  514. }
  515. if (this.batchODKUClause != null) {
  516. final byte[][] batchOdkuStaticSql = this.batchODKUClause.staticSql;
  517. byte[] beginOfOdku = batchOdkuStaticSql[0];
  518. visitor.decrement().merge(endOfValues, beginOfOdku).increment();
  519. final int batchOdkuStaticSqlLength = batchOdkuStaticSql.length;
  520. if (numBatch > 1) {
  521. for (int i = 1; i < batchOdkuStaticSqlLength; i++) {
  522. visitor.append(batchOdkuStaticSql[i])
  523. .increment();
  524. }
  525. } else {
  526. visitor.decrement().append(batchOdkuStaticSql[(batchOdkuStaticSqlLength - 1)]);
  527. }
  528. } else {
  529. // Everything after the values clause, but not ODKU, which today is nothing
  530. // but a syntax error, but we should still not mangle the SQL!
  531. visitor.decrement().append(this.staticSql[this.staticSql.length - 1]);
  532. }
  533. }
  534. private ParseInfo(byte[][] staticSql, char firstStmtChar,
  535. boolean foundLimitClause, boolean foundLoadData,
  536. boolean isOnDuplicateKeyUpdate,
  537. int locationOfOnDuplicateKeyUpdate, int statementLength,
  538. int statementStartPos) {
  539. this.firstStmtChar = firstStmtChar;
  540. this.foundLimitClause = foundLimitClause;
  541. this.foundLoadData = foundLoadData;
  542. this.isOnDuplicateKeyUpdate = isOnDuplicateKeyUpdate;
  543. this.locationOfOnDuplicateKeyUpdate = locationOfOnDuplicateKeyUpdate;
  544. this.statementLength = statementLength;
  545. this.statementStartPos = statementStartPos;
  546. this.staticSql = staticSql;
  547. }
  548. }
  549. interface BatchVisitor {
  550. abstract BatchVisitor increment();
  551. abstract BatchVisitor decrement();
  552. abstract BatchVisitor append(byte[] values);
  553. abstract BatchVisitor merge(byte[] begin, byte[] end);
  554. }
  555. class AppendingBatchVisitor implements BatchVisitor {
  556. LinkedList statementComponents = new LinkedList();
  557. public BatchVisitor append(byte[] values) {
  558. statementComponents.addLast(values);
  559. return this;
  560. }
  561. public BatchVisitor increment() {
  562. // no-op
  563. return this;
  564. }
  565. public BatchVisitor decrement() {
  566. statementComponents.removeLast();
  567. return this;
  568. }
  569. public BatchVisitor merge(byte[] front, byte[] back) {
  570. int mergedLength = front.length + back.length;
  571. byte[] merged = new byte[mergedLength];
  572. System.arraycopy(front, 0, merged, 0, front.length);
  573. System.arraycopy(back, 0, merged, front.length, back.length);
  574. statementComponents.addLast(merged);
  575. return this;
  576. }
  577. public byte[][] getStaticSqlStrings() {
  578. byte[][] asBytes = new byte[this.statementComponents.size()][];
  579. this.statementComponents.toArray(asBytes);
  580. return asBytes;
  581. }
  582. public String toString() {
  583. StringBuffer buf = new StringBuffer();
  584. Iterator iter = this.statementComponents.iterator();
  585. while (iter.hasNext()) {
  586. buf.append(new String((byte[]) iter.next()));
  587. }
  588. return buf.toString();
  589. }
  590. }
  591. private final static byte[] HEX_DIGITS = new byte[] { (byte) '0',
  592. (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
  593. (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
  594. (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };
  595. /**
  596. * Reads length bytes from reader into buf. Blocks until enough input is
  597. * available
  598. *
  599. * @param reader
  600. * DOCUMENT ME!
  601. * @param buf
  602. * DOCUMENT ME!
  603. * @param length
  604. * DOCUMENT ME!
  605. *
  606. * @return DOCUMENT ME!
  607. *
  608. * @throws IOException
  609. * DOCUMENT ME!
  610. */
  611. protected static int readFully(Reader reader, char[] buf, int length)
  612. throws IOException {
  613. int numCharsRead = 0;
  614. while (numCharsRead < length) {
  615. int count = reader.read(buf, numCharsRead, length - numCharsRead);
  616. if (count < 0) {
  617. break;
  618. }
  619. numCharsRead += count;
  620. }
  621. return numCharsRead;
  622. }
  623. /**
  624. * Does the batch (if any) contain "plain" statements added by
  625. * Statement.addBatch(String)?
  626. *
  627. * If so, we can't re-write it to use multi-value or multi-queries.
  628. */
  629. protected boolean batchHasPlainStatements = false;
  630. private java.sql.DatabaseMetaData dbmd = null;
  631. /**
  632. * What is the first character of the prepared statement (used to check for
  633. * SELECT vs. INSERT/UPDATE/DELETE)
  634. */
  635. protected char firstCharOfStmt = 0;
  636. /** Does the SQL for this statement contain a 'limit' clause? */
  637. protected boolean hasLimitClause = false;
  638. /** Is this query a LOAD DATA query? */
  639. protected boolean isLoadDataQuery = false;
  640. private boolean[] isNull = null;
  641. private boolean[] isStream = null;
  642. protected int numberOfExecutions = 0;
  643. /** The SQL that was passed in to 'prepare' */
  644. protected String originalSql = null;
  645. /** The number of parameters in this PreparedStatement */
  646. protected int parameterCount;
  647. protected MysqlParameterMetadata parameterMetaData;
  648. private InputStream[] parameterStreams = null;
  649. private byte[][] parameterValues = null;
  650. /**
  651. * Only used by statement interceptors at the moment to
  652. * provide introspection of bound values
  653. */
  654. protected int[] parameterTypes = null;
  655. protected ParseInfo parseInfo;
  656. private java.sql.ResultSetMetaData pstmtResultMetaData;
  657. private byte[][] staticSqlStrings = null;
  658. private byte[] streamConvertBuf = new byte[4096];
  659. private int[] streamLengths = null;
  660. private SimpleDateFormat tsdf = null;
  661. /**
  662. * Are we using a version of MySQL where we can use 'true' boolean values?
  663. */
  664. protected boolean useTrueBoolean = false;
  665. protected boolean usingAnsiMode;
  666. protected String batchedValuesClause;
  667. private boolean doPingInstead;
  668. private SimpleDateFormat ddf;
  669. private SimpleDateFormat tdf;
  670. private boolean compensateForOnDuplicateKeyUpdate = false;
  671. /** Charset encoder used to escape if needed, such as Yen sign in SJIS */
  672. private CharsetEncoder charsetEncoder;
  673. /** Command index of currently executing batch command. */
  674. private int batchCommandIndex = -1;
  675. /**
  676. * Creates a prepared statement instance -- We need to provide factory-style
  677. * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  678. * otherwise the class verifier complains when it tries to load JDBC4-only
  679. * interface classes that are present in JDBC4 method signatures.
  680. */
  681. protected static PreparedStatement getInstance(MySQLConnection conn,
  682. String catalog) throws SQLException {
  683. if (!Util.isJdbc4()) {
  684. return new PreparedStatement(conn, catalog);
  685. }
  686. return (PreparedStatement) Util.handleNewInstance(
  687. JDBC_4_PSTMT_2_ARG_CTOR, new Object[] { conn, catalog }, conn.getExceptionInterceptor());
  688. }
  689. /**
  690. * Creates a prepared statement instance -- We need to provide factory-style
  691. * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  692. * otherwise the class verifier complains when it tries to load JDBC4-only
  693. * interface classes that are present in JDBC4 method signatures.
  694. */
  695. protected static PreparedStatement getInstance(MySQLConnection conn, String sql,
  696. String catalog) throws SQLException {
  697. if (!Util.isJdbc4()) {
  698. return new PreparedStatement(conn, sql, catalog);
  699. }
  700. return (PreparedStatement) Util.handleNewInstance(
  701. JDBC_4_PSTMT_3_ARG_CTOR, new Object[] { conn, sql, catalog }, conn.getExceptionInterceptor());
  702. }
  703. /**
  704. * Creates a prepared statement instance -- We need to provide factory-style
  705. * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  706. * otherwise the class verifier complains when it tries to load JDBC4-only
  707. * interface classes that are present in JDBC4 method signatures.
  708. */
  709. protected static PreparedStatement getInstance(MySQLConnection conn, String sql,
  710. String catalog, ParseInfo cachedParseInfo) throws SQLException {
  711. if (!Util.isJdbc4()) {
  712. return new PreparedStatement(conn, sql, catalog, cachedParseInfo);
  713. }
  714. return (PreparedStatement) Util.handleNewInstance(
  715. JDBC_4_PSTMT_4_ARG_CTOR, new Object[] { conn, sql, catalog,
  716. cachedParseInfo }, conn.getExceptionInterceptor());
  717. }
  718. /**
  719. * Constructor used by server-side prepared statements
  720. *
  721. * @param conn
  722. * the connection that created us
  723. * @param catalog
  724. * the catalog in use when we were created
  725. *
  726. * @throws SQLException
  727. * if an error occurs
  728. */
  729. public PreparedStatement(MySQLConnection conn, String catalog)
  730. throws SQLException {
  731. super(conn, catalog);
  732. this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
  733. }
  734. /**
  735. * Constructor for the PreparedStatement class.
  736. *
  737. * @param conn
  738. * the connection creating this statement
  739. * @param sql
  740. * the SQL for this statement
  741. * @param catalog
  742. * the catalog/database this statement should be issued against
  743. *
  744. * @throws SQLException
  745. * if a database error occurs.
  746. */
  747. public PreparedStatement(MySQLConnection conn, String sql, String catalog)
  748. throws SQLException {
  749. super(conn, catalog);
  750. if (sql == null) {
  751. throw SQLError.createSQLException(Messages.getString("PreparedStatement.0"), //$NON-NLS-1$
  752. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  753. }
  754. this.originalSql = sql;
  755. if (this.originalSql.startsWith(PING_MARKER)) {
  756. this.doPingInstead = true;
  757. } else {
  758. this.doPingInstead = false;
  759. }
  760. this.dbmd = this.connection.getMetaData();
  761. this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
  762. this.parseInfo = new ParseInfo(sql, this.connection, this.dbmd,
  763. this.charEncoding, this.charConverter);
  764. initializeFromParseInfo();
  765. this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
  766. if (conn.getRequiresEscapingEncoder())
  767. charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder();
  768. }
  769. /**
  770. * Creates a new PreparedStatement object.
  771. *
  772. * @param conn
  773. * the connection creating this statement
  774. * @param sql
  775. * the SQL for this statement
  776. * @param catalog
  777. * the catalog/database this statement should be issued against
  778. * @param cachedParseInfo
  779. * already created parseInfo.
  780. *
  781. * @throws SQLException
  782. * DOCUMENT ME!
  783. */
  784. public PreparedStatement(MySQLConnection conn, String sql, String catalog,
  785. ParseInfo cachedParseInfo) throws SQLException {
  786. super(conn, catalog);
  787. if (sql == null) {
  788. throw SQLError.createSQLException(Messages.getString("PreparedStatement.1"), //$NON-NLS-1$
  789. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  790. }
  791. this.originalSql = sql;
  792. this.dbmd = this.connection.getMetaData();
  793. this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
  794. this.parseInfo = cachedParseInfo;
  795. this.usingAnsiMode = !this.connection.useAnsiQuotedIdentifiers();
  796. initializeFromParseInfo();
  797. this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
  798. if (conn.getRequiresEscapingEncoder())
  799. charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder();
  800. }
  801. /**
  802. * JDBC 2.0 Add a set of parameters to the batch.
  803. *
  804. * @exception SQLException
  805. * if a database-access error occurs.
  806. *
  807. * @see StatementImpl#addBatch
  808. */
  809. public void addBatch() throws SQLException {
  810. if (this.batchedArgs == null) {
  811. this.batchedArgs = new ArrayList();
  812. }
  813. for (int i = 0; i < this.parameterValues.length; i++) {
  814. checkAllParametersSet(this.parameterValues[i],
  815. this.parameterStreams[i], i);
  816. }
  817. this.batchedArgs.add(new BatchParams(this.parameterValues,
  818. this.parameterStreams, this.isStream, this.streamLengths,
  819. this.isNull));
  820. }
  821. public synchronized void addBatch(String sql) throws SQLException {
  822. this.batchHasPlainStatements = true;
  823. super.addBatch(sql);
  824. }
  825. protected String asSql() throws SQLException {
  826. return asSql(false);
  827. }
  828. protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
  829. if (this.isClosed) {
  830. return "statement has been closed, no further internal information available";
  831. }
  832. StringBuffer buf = new StringBuffer();
  833. try {
  834. int realParameterCount = this.parameterCount + getParameterIndexOffset();
  835. Object batchArg = null;
  836. if (batchCommandIndex != -1)
  837. batchArg = batchedArgs.get(batchCommandIndex);
  838. for (int i = 0; i < realParameterCount; ++i) {
  839. if (this.charEncoding != null) {
  840. buf.append(new String(this.staticSqlStrings[i],
  841. this.charEncoding));
  842. } else {
  843. buf.append(new String(this.staticSqlStrings[i]));
  844. }
  845. byte val[] = null;
  846. if (batchArg != null && batchArg instanceof String) {
  847. buf.append((String)batchArg);
  848. continue;
  849. }
  850. if (batchCommandIndex == -1)
  851. val = parameterValues[i];
  852. else
  853. val = ((BatchParams)batchArg).parameterStrings[i];
  854. boolean isStreamParam = false;
  855. if (batchCommandIndex == -1)
  856. isStreamParam = isStream[i];
  857. else
  858. isStreamParam = ((BatchParams)batchArg).isStream[i];
  859. if ((val == null) && !isStreamParam) {
  860. if (quoteStreamsAndUnknowns) {
  861. buf.append("'");
  862. }
  863. buf.append("** NOT SPECIFIED **"); //$NON-NLS-1$
  864. if (quoteStreamsAndUnknowns) {
  865. buf.append("'");
  866. }
  867. } else if (isStreamParam) {
  868. if (quoteStreamsAndUnknowns) {
  869. buf.append("'");
  870. }
  871. buf.append("** STREAM DATA **"); //$NON-NLS-1$
  872. if (quoteStreamsAndUnknowns) {
  873. buf.append("'");
  874. }
  875. } else {
  876. if (this.charConverter != null) {
  877. buf.append(this.charConverter.toString(val));
  878. } else {
  879. if (this.charEncoding != null) {
  880. buf.append(new String(val, this.charEncoding));
  881. } else {
  882. buf.append(StringUtils.toAsciiString(val));
  883. }
  884. }
  885. }
  886. }
  887. if (this.charEncoding != null) {
  888. buf.append(new String(
  889. this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()],
  890. this.charEncoding));
  891. } else {
  892. buf
  893. .append(StringUtils
  894. .toAsciiString(this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()]));
  895. }
  896. } catch (UnsupportedEncodingException uue) {
  897. throw new RuntimeException(Messages
  898. .getString("PreparedStatement.32") //$NON-NLS-1$
  899. + this.charEncoding
  900. + Messages.getString("PreparedStatement.33")); //$NON-NLS-1$
  901. }
  902. return buf.toString();
  903. }
  904. public synchronized void clearBatch() throws SQLException {
  905. this.batchHasPlainStatements = false;
  906. super.clearBatch();
  907. }
  908. /**
  909. * In general, parameter values remain in force for repeated used of a
  910. * Statement. Setting a parameter value automatically clears its previous
  911. * value. However, in some cases, it is useful to immediately release the
  912. * resources used by the current parameter values; this can be done by
  913. * calling clearParameters
  914. *
  915. * @exception SQLException
  916. * if a database access error occurs
  917. */
  918. public synchronized void clearParameters() throws SQLException {
  919. checkClosed();
  920. for (int i = 0; i < this.parameterValues.length; i++) {
  921. this.parameterValues[i] = null;
  922. this.parameterStreams[i] = null;
  923. this.isStream[i] = false;
  924. this.isNull[i] = false;
  925. this.parameterTypes[i] = Types.NULL;
  926. }
  927. }
  928. /**
  929. * Closes this prepared statement and releases all resources.
  930. *
  931. * @throws SQLException
  932. * if database error occurs.
  933. */
  934. public synchronized void close() throws SQLException {
  935. realClose(true, true);
  936. }
  937. private final void escapeblockFast(byte[] buf, Buffer packet, int size)
  938. throws SQLException {
  939. int lastwritten = 0;
  940. for (int i = 0; i < size; i++) {
  941. byte b = buf[i];
  942. if (b == '\0') {
  943. // write stuff not yet written
  944. if (i > lastwritten) {
  945. packet.writeBytesNoNull(buf, lastwritten, i - lastwritten);
  946. }
  947. // write escape
  948. packet.writeByte((byte) '\\');
  949. packet.writeByte((byte) '0');
  950. lastwritten = i + 1;
  951. } else {
  952. if ((b == '\\') || (b == '\'')
  953. || (!this.usingAnsiMode && b == '"')) {
  954. // write stuff not yet written
  955. if (i > lastwritten) {
  956. packet.writeBytesNoNull(buf, lastwritten, i
  957. - lastwritten);
  958. }
  959. // write escape
  960. packet.writeByte((byte) '\\');
  961. lastwritten = i; // not i+1 as b wasn't written.
  962. }
  963. }
  964. }
  965. // write out remaining stuff from buffer
  966. if (lastwritten < size) {
  967. packet.writeBytesNoNull(buf, lastwritten, size - lastwritten);
  968. }
  969. }
  970. private final void escapeblockFast(byte[] buf,
  971. ByteArrayOutputStream bytesOut, int size) {
  972. int lastwritten = 0;
  973. for (int i = 0; i < size; i++) {
  974. byte b = buf[i];
  975. if (b == '\0') {
  976. // write stuff not yet written
  977. if (i > lastwritten) {
  978. bytesOut.write(buf, lastwritten, i - lastwritten);
  979. }
  980. // write escape
  981. bytesOut.write('\\');
  982. bytesOut.write('0');
  983. lastwritten = i + 1;
  984. } else {
  985. if ((b == '\\') || (b == '\'')
  986. || (!this.usingAnsiMode && b == '"')) {
  987. // write stuff not yet written
  988. if (i > lastwritten) {
  989. bytesOut.write(buf, lastwritten, i - lastwritten);
  990. }
  991. // write escape
  992. bytesOut.write('\\');
  993. lastwritten = i; // not i+1 as b wasn't written.
  994. }
  995. }
  996. }
  997. // write out remaining stuff from buffer
  998. if (lastwritten < size) {
  999. bytesOut.write(buf, lastwritten, size - lastwritten);
  1000. }
  1001. }
  1002. /**
  1003. * Check to see if the statement is safe for read-only slaves after failover.
  1004. *
  1005. * @return true if safe for read-only.
  1006. * @throws SQLException
  1007. */
  1008. protected boolean checkReadOnlySafeStatement() throws SQLException {
  1009. return ((!this.connection.isReadOnly()) || (this.firstCharOfStmt == 'S'));
  1010. }
  1011. /**
  1012. * Some prepared statements return multiple results; the execute method
  1013. * handles these complex statements as well as the simpler form of
  1014. * statements handled by executeQuery and executeUpdate
  1015. *
  1016. * @return true if the next result is a ResultSet; false if it is an update
  1017. * count or there are no more results
  1018. *
  1019. * @exception SQLException
  1020. * if a database access error occurs
  1021. */
  1022. public boolean execute() throws SQLException {
  1023. checkClosed();
  1024. MySQLConnection locallyScopedConn = this.connection;
  1025. if(!checkReadOnlySafeStatement()) {
  1026. throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") //$NON-NLS-1$
  1027. + Messages.getString("PreparedStatement.21"), //$NON-NLS-1$
  1028. SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  1029. }
  1030. ResultSetInternalMethods rs = null;
  1031. CachedResultSetMetaData cachedMetadata = null;
  1032. synchronized (locallyScopedConn.getMutex()) {
  1033. lastQueryIsOnDupKeyUpdate = false;
  1034. if (retrieveGeneratedKeys)
  1035. lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL();
  1036. boolean doStreaming = createStreamingResultSet();
  1037. clearWarnings();
  1038. // Adjust net_write_timeout to a higher value if we're
  1039. // streaming result sets. More often than not, someone runs into
  1040. // an issue where they blow net_write_timeout when using this
  1041. // feature, and if they're willing to hold a result set open
  1042. // for 30 seconds or more, one more round-trip isn't going to hurt
  1043. //
  1044. // This is reset by RowDataDynamic.close().
  1045. if (doStreaming
  1046. && this.connection.getNetTimeoutForStreamingResults() > 0) {
  1047. executeSimpleNonQuery(locallyScopedConn,
  1048. "SET net_write_timeout="
  1049. + this.connection
  1050. .getNetTimeoutForStreamingResults());
  1051. }
  1052. this.batchedGeneratedKeys = null;
  1053. Buffer sendPacket = fillSendPacket();
  1054. String oldCatalog = null;
  1055. if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
  1056. oldCatalog = locallyScopedConn.getCatalog();
  1057. locallyScopedConn.setCatalog(this.currentCatalog);
  1058. }
  1059. //
  1060. // Check if we have cached metadata for this query...
  1061. //
  1062. if (locallyScopedConn.getCacheResultSetMetadata()) {
  1063. cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
  1064. }
  1065. Field[] metadataFromCache = null;
  1066. if (cachedMetadata != null) {
  1067. metadataFromCache = cachedMetadata.fields;
  1068. }
  1069. boolean oldInfoMsgState = false;
  1070. if (this.retrieveGeneratedKeys) {
  1071. oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
  1072. locallyScopedConn.setReadInfoMsgEnabled(true);
  1073. }
  1074. // If there isn't a limit clause in the SQL
  1075. // then limit the number of rows to return in
  1076. // an efficient manner. Only do this if
  1077. // setMaxRows() hasn't been used on any Statements
  1078. // generated from the current Connection (saves
  1079. // a query, and network traffic).
  1080. //
  1081. // Only apply max_rows to selects
  1082. //
  1083. if (locallyScopedConn.useMaxRows()) {
  1084. int rowLimit = -1;
  1085. if (this.firstCharOfStmt == 'S') {
  1086. if (this.hasLimitClause) {
  1087. rowLimit = this.maxRows;
  1088. } else {
  1089. if (this.maxRows <= 0) {
  1090. executeSimpleNonQuery(locallyScopedConn,
  1091. "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
  1092. } else {
  1093. executeSimpleNonQuery(locallyScopedConn,
  1094. "SET OPTION SQL_SELECT_LIMIT="
  1095. + this.maxRows);
  1096. }
  1097. }
  1098. } else {
  1099. executeSimpleNonQuery(locallyScopedConn,
  1100. "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
  1101. }
  1102. // Finally, execute the query
  1103. rs = executeInternal(rowLimit, sendPacket,
  1104. doStreaming,
  1105. (this.firstCharOfStmt == 'S'), metadataFromCache, false);
  1106. } else {
  1107. rs = executeInternal(-1, sendPacket,
  1108. doStreaming,
  1109. (this.firstCharOfStmt == 'S'), metadataFromCache, false);
  1110. }
  1111. if (cachedMetadata != null) {
  1112. locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
  1113. cachedMetadata, this.results);
  1114. } else {
  1115. if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) {
  1116. locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
  1117. null /* will be created */, rs);
  1118. }
  1119. }
  1120. if (this.retrieveGeneratedKeys) {
  1121. locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
  1122. rs.setFirstCharOfQuery(this.firstCharOfStmt);
  1123. }
  1124. if (oldCatalog != null) {
  1125. locallyScopedConn.setCatalog(oldCatalog);
  1126. }
  1127. if (rs != null) {
  1128. this.lastInsertId = rs.getUpdateID();
  1129. this.results = rs;
  1130. }
  1131. }
  1132. return ((rs != null) && rs.reallyResult());
  1133. }
  1134. /**
  1135. * JDBC 2.0 Submit a batch of commands to the database for execution. This
  1136. * method is optional.
  1137. *
  1138. * @return an array of update counts containing one element for each command
  1139. * in the batch. The array is ordered according to the order in
  1140. * which commands were inserted into the batch
  1141. *
  1142. * @exception SQLException
  1143. * if a database-access error occurs, or the driver does not
  1144. * support batch statements
  1145. * @throws java.sql.BatchUpdateException
  1146. * DOCUMENT ME!
  1147. */
  1148. public int[] executeBatch() throws SQLException {
  1149. checkClosed();
  1150. if (this.connection.isReadOnly()) {
  1151. throw new SQLException(Messages.getString("PreparedStatement.25") //$NON-NLS-1$
  1152. + Messages.getString("PreparedStatement.26"), //$NON-NLS-1$
  1153. SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
  1154. }
  1155. synchronized (this.connection.getMutex()) {
  1156. if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
  1157. return new int[0];
  1158. }
  1159. // we timeout the entire batch, not individual statements
  1160. int batchTimeout = this.timeoutInMillis;
  1161. this.timeoutInMillis = 0;
  1162. resetCancelledState();
  1163. try {
  1164. clearWarnings();
  1165. if (!this.batchHasPlainStatements
  1166. && this.connection.getRewriteBatchedStatements()) {
  1167. if (canRewriteAsMultiValueInsertAtSqlLevel()) {
  1168. return executeBatchedInserts(batchTimeout);
  1169. }
  1170. if (this.connection.versionMeetsMinimum(4, 1, 0)
  1171. && !this.batchHasPlainStatements
  1172. && this.batchedArgs != null
  1173. && this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) {
  1174. return executePreparedBatchAsMultiStatement(batchTimeout);
  1175. }
  1176. }
  1177. return executeBatchSerially(batchTimeout);
  1178. } finally {
  1179. clearBatch();
  1180. }
  1181. }
  1182. }
  1183. public boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException {
  1184. return this.parseInfo.canRewriteAsMultiValueInsert;
  1185. }
  1186. protected int getLocationOfOnDuplicateKeyUpdate() {
  1187. return this.parseInfo.locationOfOnDuplicateKeyUpdate;
  1188. }
  1189. /**
  1190. * Rewrites the already prepared statement into a multi-statement
  1191. * query of 'statementsPerBatch' values and executes the entire batch
  1192. * using this new statement.
  1193. *
  1194. * @return update counts in the same fashion as executeBatch()
  1195. *
  1196. * @throws SQLException
  1197. */
  1198. protected int[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException {
  1199. synchronized (this.connection.getMutex()) {
  1200. // This is kind of an abuse, but it gets the job done
  1201. if (this.batchedValuesClause == null) {
  1202. this.batchedValuesClause = this.originalSql + ";";
  1203. }
  1204. MySQLConnection locallyScopedConn = this.connection;
  1205. boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
  1206. CancelTask timeoutTask = null;
  1207. try {
  1208. clearWarnings();
  1209. int numBatchedArgs = this.batchedArgs.size();
  1210. if (this.retrieveGeneratedKeys) {
  1211. this.batchedGeneratedKeys = new ArrayList(numBatchedArgs);
  1212. }
  1213. int numValuesPerBatch = computeBatchSize(numBatchedArgs);
  1214. if (numBatchedArgs < numValuesPerBatch) {
  1215. numValuesPerBatch = numBatchedArgs;
  1216. }
  1217. java.sql.PreparedStatement batchedStatement = null;
  1218. int batchedParamIndex = 1;
  1219. int numberToExecuteAsMultiValue = 0;
  1220. int batchCounter = 0;
  1221. int updateCountCounter = 0;
  1222. int[] updateCounts = new int[numBatchedArgs];
  1223. SQLException sqlEx = null;
  1224. try {
  1225. if (!multiQueriesEnabled) {
  1226. locallyScopedConn.getIO().enableMultiQueries();
  1227. }
  1228. if (this.retrieveGeneratedKeys) {
  1229. batchedStatement = locallyScopedConn.prepareStatement(
  1230. generateMultiStatementForBatch(numValuesPerBatch),
  1231. RETURN_GENERATED_KEYS);
  1232. } else {
  1233. batchedStatement = locallyScopedConn
  1234. .prepareStatement(generateMultiStatementForBatch(numValuesPerBatch));
  1235. }
  1236. if (locallyScopedConn.getEnableQueryTimeouts() &&
  1237. batchTimeout != 0
  1238. && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  1239. timeoutTask = new CancelTask((StatementImpl)batchedStatement);
  1240. locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  1241. batchTimeout);
  1242. }
  1243. if (numBatchedArgs < numValuesPerBatch) {
  1244. numberToExecuteAsMultiValue = numBatchedArgs;
  1245. } else {
  1246. numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch;
  1247. }
  1248. int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch;
  1249. for (int i = 0; i < numberArgsToExecute; i++) {
  1250. if (i != 0 && i % numValuesPerBatch == 0) {
  1251. try {
  1252. batchedStatement.execute();
  1253. } catch (SQLException ex) {
  1254. sqlEx = handleExceptionForBatch(batchCounter, numValuesPerBatch,
  1255. updateCounts, ex);
  1256. }
  1257. updateCountCounter = processMultiCountsAndKeys(
  1258. (StatementImpl)batchedStatement, updateCountCounter,
  1259. updateCounts);
  1260. batchedStatement.clearParameters();
  1261. batchedParamIndex = 1;
  1262. }
  1263. batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
  1264. batchedParamIndex, this.batchedArgs
  1265. .get(batchCounter++));
  1266. }
  1267. try {
  1268. batchedStatement.execute();
  1269. } catch (SQLException ex) {
  1270. sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch,
  1271. updateCounts, ex);
  1272. }
  1273. updateCountCounter = processMultiCountsAndKeys(
  1274. (StatementImpl)batchedStatement, updateCountCounter,
  1275. updateCounts);
  1276. batchedStatement.clearParameters();
  1277. numValuesPerBatch = numBatchedArgs - batchCounter;
  1278. } finally {
  1279. if (batchedStatement != null) {
  1280. batchedStatement.close();
  1281. }
  1282. }
  1283. try {
  1284. if (numValuesPerBatch > 0) {
  1285. if (this.retrieveGeneratedKeys) {
  1286. batchedStatement = locallyScopedConn.prepareStatement(
  1287. generateMultiStatementForBatch(numValuesPerBatch),
  1288. RETURN_GENERATED_KEYS);
  1289. } else {
  1290. batchedStatement = locallyScopedConn.prepareStatement(
  1291. generateMultiStatementForBatch(numValuesPerBatch));
  1292. }
  1293. if (timeoutTask != null) {
  1294. timeoutTask.toCancel = (StatementImpl)batchedStatement;
  1295. }
  1296. batchedParamIndex = 1;
  1297. while (batchCounter < numBatchedArgs) {
  1298. batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
  1299. batchedParamIndex, this.batchedArgs
  1300. .get(batchCounter++));
  1301. }
  1302. try {
  1303. batchedStatement.execute();
  1304. } catch (SQLException ex) {
  1305. sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch,
  1306. updateCounts, ex);
  1307. }
  1308. updateCountCounter = processMultiCountsAndKeys(
  1309. (StatementImpl)batchedStatement, updateCountCounter,
  1310. updateCounts);
  1311. batchedStatement.clearParameters();
  1312. }
  1313. if (timeoutTask != null) {
  1314. if (timeoutTask.caughtWhileCancelling != null) {
  1315. throw timeoutTask.caughtWhileCancelling;
  1316. }
  1317. timeoutTask.cancel();
  1318. locallyScopedConn.getCancelTimer().purge();
  1319. timeoutTask = null;
  1320. }
  1321. if (sqlEx != null) {
  1322. throw new java.sql.BatchUpdateException(sqlEx
  1323. .getMessage(), sqlEx.getSQLState(), sqlEx
  1324. .getErrorCode(), updateCounts);
  1325. }
  1326. return updateCounts;
  1327. } finally {
  1328. if (batchedStatement != null) {
  1329. batchedStatement.close();
  1330. }
  1331. }
  1332. } finally {
  1333. if (timeoutTask != null) {
  1334. timeoutTask.cancel();
  1335. locallyScopedConn.getCancelTimer().purge();
  1336. }
  1337. resetCancelledState();
  1338. if (!multiQueriesEnabled) {
  1339. locallyScopedConn.getIO().disableMultiQueries();
  1340. }
  1341. clearBatch();
  1342. }
  1343. }
  1344. }
  1345. private String generateMultiStatementForBatch(int numBatches) {
  1346. StringBuffer newStatementSql = new StringBuffer((this.originalSql
  1347. .length() + 1) * numBatches);
  1348. newStatementSql.append(this.originalSql);
  1349. for (int i = 0; i < numBatches - 1; i++) {
  1350. newStatementSql.append(';');
  1351. newStatementSql.append(this.originalSql);
  1352. }
  1353. return newStatementSql.toString();
  1354. }
  1355. /**
  1356. * Rewrites the already prepared statement into a multi-value insert
  1357. * statement of 'statementsPerBatch' values and executes the entire batch
  1358. * using this new statement.
  1359. *
  1360. * @return update counts in the same fashion as executeBatch()
  1361. *
  1362. * @throws SQLException
  1363. */
  1364. protected int[] executeBatchedInserts(int batchTimeout) throws SQLException {
  1365. String valuesClause = getValuesClause();
  1366. MySQLConnection locallyScopedConn = this.connection;
  1367. if (valuesClause == null) {
  1368. return executeBatchSerially(batchTimeout);
  1369. }
  1370. int numBatchedArgs = this.batchedArgs.size();
  1371. if (this.retrieveGeneratedKeys) {
  1372. this.batchedGeneratedKeys = new ArrayLis

Large files files are truncated, but you can click here to view the full file