/EQEmuJSM/mysql-connector-java-5.1.13/src/com/mysql/jdbc/PreparedStatement.java
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
- /*
- Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
- The MySQL Connector/J is licensed under the terms of the GPL,
- like most MySQL Connectors. There are special exceptions to the
- terms and conditions of the GPL as it is applied to this software,
- see the FLOSS License Exception available on mysql.com.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; version 2 of the
- License.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Â See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- 02110-1301 USA
-
- */
- package com.mysql.jdbc;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.ObjectOutputStream;
- import java.io.Reader;
- import java.io.StringReader;
- import java.io.UnsupportedEncodingException;
- import java.lang.reflect.Constructor;
- import java.math.BigDecimal;
- import java.math.BigInteger;
- import java.net.URL;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.charset.Charset;
- import java.nio.charset.CharsetEncoder;
- import java.sql.Array;
- import java.sql.Clob;
- import java.sql.DatabaseMetaData;
- import java.sql.Date;
- import java.sql.ParameterMetaData;
- import java.sql.Ref;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Time;
- import java.sql.Timestamp;
- import java.sql.Types;
- import java.text.ParsePosition;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.Calendar;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Locale;
- import java.util.TimeZone;
- import com.mysql.jdbc.exceptions.DeadlockTimeoutRollbackMarker;
- import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
- import com.mysql.jdbc.exceptions.MySQLTimeoutException;
- import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException;
- import com.mysql.jdbc.profiler.ProfilerEvent;
- /**
- * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
- * This object can then be used to efficiently execute this statement multiple
- * times.
- *
- * <p>
- * <B>Note:</B> The setXXX methods for setting IN parameter values must specify
- * types that are compatible with the defined SQL type of the input parameter.
- * For instance, if the IN parameter has SQL type Integer, then setInt should be
- * used.
- * </p>
- *
- * <p>
- * If arbitrary parameter type conversions are required, then the setObject
- * method should be used with a target SQL type.
- * </p>
- *
- * @author Mark Matthews
- * @version $Id: PreparedStatement.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
- * Exp $
- *
- * @see java.sql.ResultSet
- * @see java.sql.PreparedStatement
- */
- public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements
- java.sql.PreparedStatement {
- private static final Constructor JDBC_4_PSTMT_2_ARG_CTOR;
- private static final Constructor JDBC_4_PSTMT_3_ARG_CTOR;
- private static final Constructor JDBC_4_PSTMT_4_ARG_CTOR;
-
- static {
- if (Util.isJdbc4()) {
- try {
- JDBC_4_PSTMT_2_ARG_CTOR = Class.forName(
- "com.mysql.jdbc.JDBC4PreparedStatement")
- .getConstructor(
- new Class[] { MySQLConnection.class, String.class });
- JDBC_4_PSTMT_3_ARG_CTOR = Class.forName(
- "com.mysql.jdbc.JDBC4PreparedStatement")
- .getConstructor(
- new Class[] { MySQLConnection.class, String.class,
- String.class });
- JDBC_4_PSTMT_4_ARG_CTOR = Class.forName(
- "com.mysql.jdbc.JDBC4PreparedStatement")
- .getConstructor(
- new Class[] { MySQLConnection.class, String.class,
- String.class, ParseInfo.class });
- } catch (SecurityException e) {
- throw new RuntimeException(e);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- } else {
- JDBC_4_PSTMT_2_ARG_CTOR = null;
- JDBC_4_PSTMT_3_ARG_CTOR = null;
- JDBC_4_PSTMT_4_ARG_CTOR = null;
- }
- }
-
- class BatchParams {
- boolean[] isNull = null;
- boolean[] isStream = null;
- InputStream[] parameterStreams = null;
- byte[][] parameterStrings = null;
- int[] streamLengths = null;
- BatchParams(byte[][] strings, InputStream[] streams,
- boolean[] isStreamFlags, int[] lengths, boolean[] isNullFlags) {
- //
- // Make copies
- //
- this.parameterStrings = new byte[strings.length][];
- this.parameterStreams = new InputStream[streams.length];
- this.isStream = new boolean[isStreamFlags.length];
- this.streamLengths = new int[lengths.length];
- this.isNull = new boolean[isNullFlags.length];
- System.arraycopy(strings, 0, this.parameterStrings, 0,
- strings.length);
- System.arraycopy(streams, 0, this.parameterStreams, 0,
- streams.length);
- System.arraycopy(isStreamFlags, 0, this.isStream, 0,
- isStreamFlags.length);
- System.arraycopy(lengths, 0, this.streamLengths, 0, lengths.length);
- System
- .arraycopy(isNullFlags, 0, this.isNull, 0,
- isNullFlags.length);
- }
- }
- class EndPoint {
- int begin;
- int end;
- EndPoint(int b, int e) {
- this.begin = b;
- this.end = e;
- }
- }
- class ParseInfo {
- char firstStmtChar = 0;
- boolean foundLimitClause = false;
- boolean foundLoadData = false;
- long lastUsed = 0;
- int statementLength = 0;
- int statementStartPos = 0;
- boolean canRewriteAsMultiValueInsert = false;
-
- byte[][] staticSql = null;
- boolean isOnDuplicateKeyUpdate = false;
-
- int locationOfOnDuplicateKeyUpdate = -1;
-
- String valuesClause;
-
- boolean parametersInDuplicateKeyClause = false;
-
- /**
- * Represents the "parsed" state of a client-side
- * prepared statement, with the statement broken up into
- * it's static and dynamic (where parameters are bound)
- * parts.
- */
- ParseInfo(String sql, MySQLConnection conn,
- java.sql.DatabaseMetaData dbmd, String encoding,
- SingleByteCharsetConverter converter) throws SQLException {
- this(sql, conn, dbmd, encoding, converter, true);
- }
-
- public ParseInfo(String sql, MySQLConnection conn,
- java.sql.DatabaseMetaData dbmd, String encoding,
- SingleByteCharsetConverter converter, boolean buildRewriteInfo) throws SQLException {
- try {
- if (sql == null) {
- throw SQLError.createSQLException(Messages
- .getString("PreparedStatement.61"), //$NON-NLS-1$
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
- }
- this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(sql);
- this.isOnDuplicateKeyUpdate = this.locationOfOnDuplicateKeyUpdate != -1;
-
- this.lastUsed = System.currentTimeMillis();
- String quotedIdentifierString = dbmd.getIdentifierQuoteString();
- char quotedIdentifierChar = 0;
- if ((quotedIdentifierString != null)
- && !quotedIdentifierString.equals(" ") //$NON-NLS-1$
- && (quotedIdentifierString.length() > 0)) {
- quotedIdentifierChar = quotedIdentifierString.charAt(0);
- }
- this.statementLength = sql.length();
- ArrayList endpointList = new ArrayList();
- boolean inQuotes = false;
- char quoteChar = 0;
- boolean inQuotedId = false;
- int lastParmEnd = 0;
- int i;
- int stopLookingForLimitClause = this.statementLength - 5;
- this.foundLimitClause = false;
- boolean noBackslashEscapes = connection.isNoBackslashEscapesSet();
- // we're not trying to be real pedantic here, but we'd like to
- // skip comments at the beginning of statements, as frameworks
- // such as Hibernate use them to aid in debugging
- statementStartPos = findStartOfStatement(sql);
- for (i = statementStartPos; i < this.statementLength; ++i) {
- char c = sql.charAt(i);
- if ((this.firstStmtChar == 0) && Character.isLetter(c)) {
- // Determine what kind of statement we're doing (_S_elect,
- // _I_nsert, etc.)
- this.firstStmtChar = Character.toUpperCase(c);
- }
- if (!noBackslashEscapes &&
- c == '\\' && i < (this.statementLength - 1)) {
- i++;
- continue; // next character is escaped
- }
- // are we in a quoted identifier?
- // (only valid when the id is not inside a 'string')
- if (!inQuotes && (quotedIdentifierChar != 0)
- && (c == quotedIdentifierChar)) {
- inQuotedId = !inQuotedId;
- } else if (!inQuotedId) {
- // only respect quotes when not in a quoted identifier
- if (inQuotes) {
- if (((c == '\'') || (c == '"')) && c == quoteChar) {
- if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) {
- i++;
- continue; // inline quote escape
- }
- inQuotes = !inQuotes;
- quoteChar = 0;
- } else if (((c == '\'') || (c == '"')) && c == quoteChar) {
- inQuotes = !inQuotes;
- quoteChar = 0;
- }
- } else {
- if (c == '#'
- || (c == '-' && (i + 1) < this.statementLength && sql
- .charAt(i + 1) == '-')) {
- // run out to end of statement, or newline,
- // whichever comes first
- int endOfStmt = this.statementLength - 1;
- for (; i < endOfStmt; i++) {
- c = sql.charAt(i);
- if (c == '\r' || c == '\n') {
- break;
- }
- }
- continue;
- } else if (c == '/' && (i + 1) < this.statementLength) {
- // Comment?
- char cNext = sql.charAt(i + 1);
- if (cNext == '*') {
- i+= 2;
- for (int j = i; j < this.statementLength; j++) {
- i++;
- cNext = sql.charAt(j);
- if (cNext == '*' && (j + 1) < this.statementLength) {
- if (sql.charAt(j + 1) == '/') {
- i++;
- if (i < this.statementLength) {
- c = sql.charAt(i);
- }
- break; // comment done
- }
- }
- }
- }
- } else if ((c == '\'') || (c == '"')) {
- inQuotes = true;
- quoteChar = c;
- }
- }
- }
- if ((c == '?') && !inQuotes && !inQuotedId) {
- endpointList.add(new int[] { lastParmEnd, i });
- lastParmEnd = i + 1;
-
- if (isOnDuplicateKeyUpdate && i > locationOfOnDuplicateKeyUpdate) {
- parametersInDuplicateKeyClause = true;
- }
- }
- if (!inQuotes && (i < stopLookingForLimitClause)) {
- if ((c == 'L') || (c == 'l')) {
- char posI1 = sql.charAt(i + 1);
- if ((posI1 == 'I') || (posI1 == 'i')) {
- char posM = sql.charAt(i + 2);
- if ((posM == 'M') || (posM == 'm')) {
- char posI2 = sql.charAt(i + 3);
- if ((posI2 == 'I') || (posI2 == 'i')) {
- char posT = sql.charAt(i + 4);
- if ((posT == 'T') || (posT == 't')) {
- foundLimitClause = true;
- }
- }
- }
- }
- }
- }
- }
- if (this.firstStmtChar == 'L') {
- if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
- this.foundLoadData = true;
- } else {
- this.foundLoadData = false;
- }
- } else {
- this.foundLoadData = false;
- }
- endpointList.add(new int[] { lastParmEnd, this.statementLength });
- this.staticSql = new byte[endpointList.size()][];
- char[] asCharArray = sql.toCharArray();
- for (i = 0; i < this.staticSql.length; i++) {
- int[] ep = (int[]) endpointList.get(i);
- int end = ep[1];
- int begin = ep[0];
- int len = end - begin;
- if (this.foundLoadData) {
- String temp = new String(asCharArray, begin, len);
- this.staticSql[i] = temp.getBytes();
- } else if (encoding == null) {
- byte[] buf = new byte[len];
- for (int j = 0; j < len; j++) {
- buf[j] = (byte) sql.charAt(begin + j);
- }
- this.staticSql[i] = buf;
- } else {
- if (converter != null) {
- this.staticSql[i] = StringUtils.getBytes(sql,
- converter, encoding, connection
- .getServerCharacterEncoding(), begin,
- len, connection.parserKnowsUnicode(), getExceptionInterceptor());
- } else {
- String temp = new String(asCharArray, begin, len);
- this.staticSql[i] = StringUtils.getBytes(temp,
- encoding, connection
- .getServerCharacterEncoding(),
- connection.parserKnowsUnicode(), conn, getExceptionInterceptor());
- }
- }
- }
- } catch (StringIndexOutOfBoundsException oobEx) {
- SQLException sqlEx = new SQLException("Parse error for " + sql);
- sqlEx.initCause(oobEx);
- throw sqlEx;
- }
-
-
- if (buildRewriteInfo) {
- this.canRewriteAsMultiValueInsert = PreparedStatement
- .canRewrite(sql, this.isOnDuplicateKeyUpdate,
- this.locationOfOnDuplicateKeyUpdate,
- this.statementStartPos) && !this.parametersInDuplicateKeyClause;
- if (this.canRewriteAsMultiValueInsert
- && conn.getRewriteBatchedStatements()) {
- buildRewriteBatchedParams(sql, conn, dbmd, encoding,
- converter);
- }
- }
- }
- private ParseInfo batchHead;
- private ParseInfo batchValues;
- private ParseInfo batchODKUClause;
- private void buildRewriteBatchedParams(String sql, MySQLConnection conn,
- DatabaseMetaData metadata, String encoding,
- SingleByteCharsetConverter converter) throws SQLException {
- this.valuesClause = extractValuesClause(sql);
- String odkuClause = isOnDuplicateKeyUpdate ? sql
- .substring(locationOfOnDuplicateKeyUpdate) : null;
- String headSql = null;
- if (isOnDuplicateKeyUpdate) {
- headSql = sql.substring(0, locationOfOnDuplicateKeyUpdate);
- } else {
- headSql = sql;
- }
- this.batchHead = new ParseInfo(headSql, conn, metadata, encoding,
- converter, false);
- this.batchValues = new ParseInfo("," + this.valuesClause, conn,
- metadata, encoding, converter, false);
- this.batchODKUClause = null;
- if (odkuClause != null && odkuClause.length() > 0) {
- this.batchODKUClause = new ParseInfo("," + this.valuesClause
- + " " + odkuClause, conn, metadata, encoding,
- converter, false);
- }
- }
- private String extractValuesClause(String sql) throws SQLException {
- String quoteCharStr = connection.getMetaData()
- .getIdentifierQuoteString();
- int indexOfValues = -1;
- int valuesSearchStart = statementStartPos;
- while (indexOfValues == -1) {
- if (quoteCharStr.length() > 0) {
- indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(
- valuesSearchStart,
- originalSql, "VALUES", quoteCharStr.charAt(0), false);
- } else {
- indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart,
- originalSql,
- "VALUES");
- }
-
- if (indexOfValues > 0) {
- /* check if the char immediately preceding VALUES may be part of the table name */
- char c = originalSql.charAt(indexOfValues - 1);
- if(!(Character.isWhitespace(c) || c == ')' || c == '`')){
- valuesSearchStart = indexOfValues + 6;
- indexOfValues = -1;
- } else {
- /* check if the char immediately following VALUES may be whitespace or open parenthesis */
- c = originalSql.charAt(indexOfValues + 6);
- if(!(Character.isWhitespace(c) || c == '(')){
- valuesSearchStart = indexOfValues + 6;
- indexOfValues = -1;
- }
- }
- } else {
- break;
- }
- }
- if (indexOfValues == -1) {
- return null;
- }
- int indexOfFirstParen = sql.indexOf('(', indexOfValues + 6);
- if (indexOfFirstParen == -1) {
- return null;
- }
- int endOfValuesClause = sql.lastIndexOf(')');
- if (endOfValuesClause == -1) {
- return null;
- }
- if (isOnDuplicateKeyUpdate) {
- endOfValuesClause = this.locationOfOnDuplicateKeyUpdate - 1;
- }
- return sql.substring(indexOfFirstParen, endOfValuesClause + 1);
- }
- /**
- * Returns a ParseInfo for a multi-value INSERT for a batch of size numBatch (without parsing!).
- */
- synchronized ParseInfo getParseInfoForBatch(int numBatch) {
- AppendingBatchVisitor apv = new AppendingBatchVisitor();
- buildInfoForBatch(numBatch, apv);
- ParseInfo batchParseInfo = new ParseInfo(apv.getStaticSqlStrings(),
- this.firstStmtChar, this.foundLimitClause,
- this.foundLoadData, this.isOnDuplicateKeyUpdate,
- this.locationOfOnDuplicateKeyUpdate, this.statementLength,
- this.statementStartPos);
- return batchParseInfo;
- }
-
- /**
- * Returns a preparable SQL string for the number of batched parameters, used by server-side prepared statements
- * when re-writing batch INSERTs.
- */
-
- String getSqlForBatch(int numBatch) throws UnsupportedEncodingException {
- ParseInfo batchInfo = getParseInfoForBatch(numBatch);
-
- return getSqlForBatch(batchInfo);
- }
-
- /**
- * Used for filling in the SQL for getPreparedSql() - for debugging
- */
- String getSqlForBatch(ParseInfo batchInfo) throws UnsupportedEncodingException {
- int size = 0;
- final byte[][] sqlStrings = batchInfo.staticSql;
- final int sqlStringsLength = sqlStrings.length;
-
- for (int i = 0; i < sqlStringsLength; i++) {
- size += sqlStrings[i].length;
- size++; // for the '?'
- }
-
- StringBuffer buf = new StringBuffer(size);
-
- for (int i = 0; i < sqlStringsLength - 1; i++) {
- buf.append(new String(sqlStrings[i], charEncoding));
- buf.append("?");
- }
-
- buf.append(new String(sqlStrings[sqlStringsLength - 1]));
-
- return buf.toString();
- }
- /**
- * Builds a ParseInfo for the given batch size, without parsing. We use
- * a visitor pattern here, because the if {}s make computing a size for the
- * resultant byte[][] make this too complex, and we don't necessarily want to
- * use a List for this, because the size can be dynamic, and thus we'll not be
- * able to guess a good initial size for an array-based list, and it's not
- * efficient to convert a LinkedList to an array.
- */
- private void buildInfoForBatch(int numBatch, BatchVisitor visitor) {
- final byte[][] headStaticSql = this.batchHead.staticSql;
- final int headStaticSqlLength = headStaticSql.length;
- if (headStaticSqlLength > 1) {
- for (int i = 0; i < headStaticSqlLength - 1; i++) {
- visitor.append(headStaticSql[i]).increment();
- }
- }
- // merge end of head, with beginning of a value clause
- byte[] endOfHead = headStaticSql[headStaticSqlLength - 1];
- final byte[][] valuesStaticSql = this.batchValues.staticSql;
- byte[] beginOfValues = valuesStaticSql[0];
- visitor.merge(endOfHead, beginOfValues).increment();
- int numValueRepeats = numBatch - 1; // first one is in the "head"
- if (this.batchODKUClause != null) {
- numValueRepeats--; // Last one is in the ODKU clause
- }
- final int valuesStaticSqlLength = valuesStaticSql.length;
- byte[] endOfValues = valuesStaticSql[valuesStaticSqlLength - 1];
- for (int i = 0; i < numValueRepeats; i++) {
- for (int j = 1; j < valuesStaticSqlLength - 1; j++) {
- visitor.append(valuesStaticSql[j]).increment();
- }
- visitor.merge(endOfValues, beginOfValues).increment();
- }
- if (this.batchODKUClause != null) {
- final byte[][] batchOdkuStaticSql = this.batchODKUClause.staticSql;
- byte[] beginOfOdku = batchOdkuStaticSql[0];
- visitor.decrement().merge(endOfValues, beginOfOdku).increment();
- final int batchOdkuStaticSqlLength = batchOdkuStaticSql.length;
-
- if (numBatch > 1) {
- for (int i = 1; i < batchOdkuStaticSqlLength; i++) {
- visitor.append(batchOdkuStaticSql[i])
- .increment();
- }
- } else {
- visitor.decrement().append(batchOdkuStaticSql[(batchOdkuStaticSqlLength - 1)]);
- }
- } else {
- // Everything after the values clause, but not ODKU, which today is nothing
- // but a syntax error, but we should still not mangle the SQL!
- visitor.decrement().append(this.staticSql[this.staticSql.length - 1]);
- }
- }
- private ParseInfo(byte[][] staticSql, char firstStmtChar,
- boolean foundLimitClause, boolean foundLoadData,
- boolean isOnDuplicateKeyUpdate,
- int locationOfOnDuplicateKeyUpdate, int statementLength,
- int statementStartPos) {
- this.firstStmtChar = firstStmtChar;
- this.foundLimitClause = foundLimitClause;
- this.foundLoadData = foundLoadData;
- this.isOnDuplicateKeyUpdate = isOnDuplicateKeyUpdate;
- this.locationOfOnDuplicateKeyUpdate = locationOfOnDuplicateKeyUpdate;
- this.statementLength = statementLength;
- this.statementStartPos = statementStartPos;
- this.staticSql = staticSql;
- }
- }
- interface BatchVisitor {
- abstract BatchVisitor increment();
-
- abstract BatchVisitor decrement();
-
- abstract BatchVisitor append(byte[] values);
-
- abstract BatchVisitor merge(byte[] begin, byte[] end);
- }
-
- class AppendingBatchVisitor implements BatchVisitor {
- LinkedList statementComponents = new LinkedList();
-
- public BatchVisitor append(byte[] values) {
- statementComponents.addLast(values);
- return this;
- }
- public BatchVisitor increment() {
- // no-op
- return this;
- }
- public BatchVisitor decrement() {
- statementComponents.removeLast();
-
- return this;
- }
- public BatchVisitor merge(byte[] front, byte[] back) {
- int mergedLength = front.length + back.length;
- byte[] merged = new byte[mergedLength];
- System.arraycopy(front, 0, merged, 0, front.length);
- System.arraycopy(back, 0, merged, front.length, back.length);
- statementComponents.addLast(merged);
- return this;
- }
-
- public byte[][] getStaticSqlStrings() {
- byte[][] asBytes = new byte[this.statementComponents.size()][];
- this.statementComponents.toArray(asBytes);
-
- return asBytes;
- }
-
- public String toString() {
- StringBuffer buf = new StringBuffer();
- Iterator iter = this.statementComponents.iterator();
- while (iter.hasNext()) {
- buf.append(new String((byte[]) iter.next()));
- }
-
- return buf.toString();
- }
-
- }
-
- private final static byte[] HEX_DIGITS = new byte[] { (byte) '0',
- (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
- (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
- (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };
- /**
- * Reads length bytes from reader into buf. Blocks until enough input is
- * available
- *
- * @param reader
- * DOCUMENT ME!
- * @param buf
- * DOCUMENT ME!
- * @param length
- * DOCUMENT ME!
- *
- * @return DOCUMENT ME!
- *
- * @throws IOException
- * DOCUMENT ME!
- */
- protected static int readFully(Reader reader, char[] buf, int length)
- throws IOException {
- int numCharsRead = 0;
- while (numCharsRead < length) {
- int count = reader.read(buf, numCharsRead, length - numCharsRead);
- if (count < 0) {
- break;
- }
- numCharsRead += count;
- }
- return numCharsRead;
- }
- /**
- * Does the batch (if any) contain "plain" statements added by
- * Statement.addBatch(String)?
- *
- * If so, we can't re-write it to use multi-value or multi-queries.
- */
- protected boolean batchHasPlainStatements = false;
- private java.sql.DatabaseMetaData dbmd = null;
- /**
- * What is the first character of the prepared statement (used to check for
- * SELECT vs. INSERT/UPDATE/DELETE)
- */
- protected char firstCharOfStmt = 0;
- /** Does the SQL for this statement contain a 'limit' clause? */
- protected boolean hasLimitClause = false;
-
- /** Is this query a LOAD DATA query? */
- protected boolean isLoadDataQuery = false;
- private boolean[] isNull = null;
- private boolean[] isStream = null;
- protected int numberOfExecutions = 0;
- /** The SQL that was passed in to 'prepare' */
- protected String originalSql = null;
- /** The number of parameters in this PreparedStatement */
- protected int parameterCount;
- protected MysqlParameterMetadata parameterMetaData;
- private InputStream[] parameterStreams = null;
- private byte[][] parameterValues = null;
- /**
- * Only used by statement interceptors at the moment to
- * provide introspection of bound values
- */
- protected int[] parameterTypes = null;
-
- protected ParseInfo parseInfo;
- private java.sql.ResultSetMetaData pstmtResultMetaData;
- private byte[][] staticSqlStrings = null;
- private byte[] streamConvertBuf = new byte[4096];
- private int[] streamLengths = null;
- private SimpleDateFormat tsdf = null;
- /**
- * Are we using a version of MySQL where we can use 'true' boolean values?
- */
- protected boolean useTrueBoolean = false;
- protected boolean usingAnsiMode;
- protected String batchedValuesClause;
- private boolean doPingInstead;
- private SimpleDateFormat ddf;
- private SimpleDateFormat tdf;
-
- private boolean compensateForOnDuplicateKeyUpdate = false;
-
- /** Charset encoder used to escape if needed, such as Yen sign in SJIS */
- private CharsetEncoder charsetEncoder;
-
- /** Command index of currently executing batch command. */
- private int batchCommandIndex = -1;
-
- /**
- * Creates a prepared statement instance -- We need to provide factory-style
- * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
- * otherwise the class verifier complains when it tries to load JDBC4-only
- * interface classes that are present in JDBC4 method signatures.
- */
- protected static PreparedStatement getInstance(MySQLConnection conn,
- String catalog) throws SQLException {
- if (!Util.isJdbc4()) {
- return new PreparedStatement(conn, catalog);
- }
- return (PreparedStatement) Util.handleNewInstance(
- JDBC_4_PSTMT_2_ARG_CTOR, new Object[] { conn, catalog }, conn.getExceptionInterceptor());
- }
- /**
- * Creates a prepared statement instance -- We need to provide factory-style
- * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
- * otherwise the class verifier complains when it tries to load JDBC4-only
- * interface classes that are present in JDBC4 method signatures.
- */
- protected static PreparedStatement getInstance(MySQLConnection conn, String sql,
- String catalog) throws SQLException {
- if (!Util.isJdbc4()) {
- return new PreparedStatement(conn, sql, catalog);
- }
- return (PreparedStatement) Util.handleNewInstance(
- JDBC_4_PSTMT_3_ARG_CTOR, new Object[] { conn, sql, catalog }, conn.getExceptionInterceptor());
- }
- /**
- * Creates a prepared statement instance -- We need to provide factory-style
- * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
- * otherwise the class verifier complains when it tries to load JDBC4-only
- * interface classes that are present in JDBC4 method signatures.
- */
- protected static PreparedStatement getInstance(MySQLConnection conn, String sql,
- String catalog, ParseInfo cachedParseInfo) throws SQLException {
- if (!Util.isJdbc4()) {
- return new PreparedStatement(conn, sql, catalog, cachedParseInfo);
- }
- return (PreparedStatement) Util.handleNewInstance(
- JDBC_4_PSTMT_4_ARG_CTOR, new Object[] { conn, sql, catalog,
- cachedParseInfo }, conn.getExceptionInterceptor());
- }
-
- /**
- * Constructor used by server-side prepared statements
- *
- * @param conn
- * the connection that created us
- * @param catalog
- * the catalog in use when we were created
- *
- * @throws SQLException
- * if an error occurs
- */
- public PreparedStatement(MySQLConnection conn, String catalog)
- throws SQLException {
- super(conn, catalog);
-
- this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
- }
- /**
- * Constructor for the PreparedStatement class.
- *
- * @param conn
- * the connection creating this statement
- * @param sql
- * the SQL for this statement
- * @param catalog
- * the catalog/database this statement should be issued against
- *
- * @throws SQLException
- * if a database error occurs.
- */
- public PreparedStatement(MySQLConnection conn, String sql, String catalog)
- throws SQLException {
- super(conn, catalog);
- if (sql == null) {
- throw SQLError.createSQLException(Messages.getString("PreparedStatement.0"), //$NON-NLS-1$
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
- }
- this.originalSql = sql;
- if (this.originalSql.startsWith(PING_MARKER)) {
- this.doPingInstead = true;
- } else {
- this.doPingInstead = false;
- }
-
- this.dbmd = this.connection.getMetaData();
- this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
- this.parseInfo = new ParseInfo(sql, this.connection, this.dbmd,
- this.charEncoding, this.charConverter);
- initializeFromParseInfo();
-
- this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
-
- if (conn.getRequiresEscapingEncoder())
- charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder();
- }
- /**
- * Creates a new PreparedStatement object.
- *
- * @param conn
- * the connection creating this statement
- * @param sql
- * the SQL for this statement
- * @param catalog
- * the catalog/database this statement should be issued against
- * @param cachedParseInfo
- * already created parseInfo.
- *
- * @throws SQLException
- * DOCUMENT ME!
- */
- public PreparedStatement(MySQLConnection conn, String sql, String catalog,
- ParseInfo cachedParseInfo) throws SQLException {
- super(conn, catalog);
- if (sql == null) {
- throw SQLError.createSQLException(Messages.getString("PreparedStatement.1"), //$NON-NLS-1$
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
- }
- this.originalSql = sql;
- this.dbmd = this.connection.getMetaData();
- this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
- this.parseInfo = cachedParseInfo;
- this.usingAnsiMode = !this.connection.useAnsiQuotedIdentifiers();
- initializeFromParseInfo();
-
- this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
- if (conn.getRequiresEscapingEncoder())
- charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder();
- }
- /**
- * JDBC 2.0 Add a set of parameters to the batch.
- *
- * @exception SQLException
- * if a database-access error occurs.
- *
- * @see StatementImpl#addBatch
- */
- public void addBatch() throws SQLException {
- if (this.batchedArgs == null) {
- this.batchedArgs = new ArrayList();
- }
- for (int i = 0; i < this.parameterValues.length; i++) {
- checkAllParametersSet(this.parameterValues[i],
- this.parameterStreams[i], i);
- }
-
- this.batchedArgs.add(new BatchParams(this.parameterValues,
- this.parameterStreams, this.isStream, this.streamLengths,
- this.isNull));
- }
- public synchronized void addBatch(String sql) throws SQLException {
- this.batchHasPlainStatements = true;
- super.addBatch(sql);
- }
- protected String asSql() throws SQLException {
- return asSql(false);
- }
- protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
- if (this.isClosed) {
- return "statement has been closed, no further internal information available";
- }
-
- StringBuffer buf = new StringBuffer();
- try {
- int realParameterCount = this.parameterCount + getParameterIndexOffset();
- Object batchArg = null;
- if (batchCommandIndex != -1)
- batchArg = batchedArgs.get(batchCommandIndex);
-
- for (int i = 0; i < realParameterCount; ++i) {
- if (this.charEncoding != null) {
- buf.append(new String(this.staticSqlStrings[i],
- this.charEncoding));
- } else {
- buf.append(new String(this.staticSqlStrings[i]));
- }
- byte val[] = null;
- if (batchArg != null && batchArg instanceof String) {
- buf.append((String)batchArg);
- continue;
- }
- if (batchCommandIndex == -1)
- val = parameterValues[i];
- else
- val = ((BatchParams)batchArg).parameterStrings[i];
-
- boolean isStreamParam = false;
- if (batchCommandIndex == -1)
- isStreamParam = isStream[i];
- else
- isStreamParam = ((BatchParams)batchArg).isStream[i];
- if ((val == null) && !isStreamParam) {
- if (quoteStreamsAndUnknowns) {
- buf.append("'");
- }
- buf.append("** NOT SPECIFIED **"); //$NON-NLS-1$
- if (quoteStreamsAndUnknowns) {
- buf.append("'");
- }
- } else if (isStreamParam) {
- if (quoteStreamsAndUnknowns) {
- buf.append("'");
- }
- buf.append("** STREAM DATA **"); //$NON-NLS-1$
- if (quoteStreamsAndUnknowns) {
- buf.append("'");
- }
- } else {
- if (this.charConverter != null) {
- buf.append(this.charConverter.toString(val));
- } else {
- if (this.charEncoding != null) {
- buf.append(new String(val, this.charEncoding));
- } else {
- buf.append(StringUtils.toAsciiString(val));
- }
- }
- }
- }
- if (this.charEncoding != null) {
- buf.append(new String(
- this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()],
- this.charEncoding));
- } else {
- buf
- .append(StringUtils
- .toAsciiString(this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()]));
- }
- } catch (UnsupportedEncodingException uue) {
- throw new RuntimeException(Messages
- .getString("PreparedStatement.32") //$NON-NLS-1$
- + this.charEncoding
- + Messages.getString("PreparedStatement.33")); //$NON-NLS-1$
- }
- return buf.toString();
- }
- public synchronized void clearBatch() throws SQLException {
- this.batchHasPlainStatements = false;
- super.clearBatch();
- }
- /**
- * In general, parameter values remain in force for repeated used of a
- * Statement. Setting a parameter value automatically clears its previous
- * value. However, in some cases, it is useful to immediately release the
- * resources used by the current parameter values; this can be done by
- * calling clearParameters
- *
- * @exception SQLException
- * if a database access error occurs
- */
- public synchronized void clearParameters() throws SQLException {
- checkClosed();
-
- for (int i = 0; i < this.parameterValues.length; i++) {
- this.parameterValues[i] = null;
- this.parameterStreams[i] = null;
- this.isStream[i] = false;
- this.isNull[i] = false;
- this.parameterTypes[i] = Types.NULL;
- }
- }
- /**
- * Closes this prepared statement and releases all resources.
- *
- * @throws SQLException
- * if database error occurs.
- */
- public synchronized void close() throws SQLException {
- realClose(true, true);
- }
- private final void escapeblockFast(byte[] buf, Buffer packet, int size)
- throws SQLException {
- int lastwritten = 0;
- for (int i = 0; i < size; i++) {
- byte b = buf[i];
- if (b == '\0') {
- // write stuff not yet written
- if (i > lastwritten) {
- packet.writeBytesNoNull(buf, lastwritten, i - lastwritten);
- }
- // write escape
- packet.writeByte((byte) '\\');
- packet.writeByte((byte) '0');
- lastwritten = i + 1;
- } else {
- if ((b == '\\') || (b == '\'')
- || (!this.usingAnsiMode && b == '"')) {
- // write stuff not yet written
- if (i > lastwritten) {
- packet.writeBytesNoNull(buf, lastwritten, i
- - lastwritten);
- }
- // write escape
- packet.writeByte((byte) '\\');
- lastwritten = i; // not i+1 as b wasn't written.
- }
- }
- }
- // write out remaining stuff from buffer
- if (lastwritten < size) {
- packet.writeBytesNoNull(buf, lastwritten, size - lastwritten);
- }
- }
- private final void escapeblockFast(byte[] buf,
- ByteArrayOutputStream bytesOut, int size) {
- int lastwritten = 0;
- for (int i = 0; i < size; i++) {
- byte b = buf[i];
- if (b == '\0') {
- // write stuff not yet written
- if (i > lastwritten) {
- bytesOut.write(buf, lastwritten, i - lastwritten);
- }
- // write escape
- bytesOut.write('\\');
- bytesOut.write('0');
- lastwritten = i + 1;
- } else {
- if ((b == '\\') || (b == '\'')
- || (!this.usingAnsiMode && b == '"')) {
- // write stuff not yet written
- if (i > lastwritten) {
- bytesOut.write(buf, lastwritten, i - lastwritten);
- }
- // write escape
- bytesOut.write('\\');
- lastwritten = i; // not i+1 as b wasn't written.
- }
- }
- }
- // write out remaining stuff from buffer
- if (lastwritten < size) {
- bytesOut.write(buf, lastwritten, size - lastwritten);
- }
- }
-
- /**
- * Check to see if the statement is safe for read-only slaves after failover.
- *
- * @return true if safe for read-only.
- * @throws SQLException
- */
- protected boolean checkReadOnlySafeStatement() throws SQLException {
- return ((!this.connection.isReadOnly()) || (this.firstCharOfStmt == 'S'));
- }
- /**
- * Some prepared statements return multiple results; the execute method
- * handles these complex statements as well as the simpler form of
- * statements handled by executeQuery and executeUpdate
- *
- * @return true if the next result is a ResultSet; false if it is an update
- * count or there are no more results
- *
- * @exception SQLException
- * if a database access error occurs
- */
- public boolean execute() throws SQLException {
- checkClosed();
-
- MySQLConnection locallyScopedConn = this.connection;
-
- if(!checkReadOnlySafeStatement()) {
- throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") //$NON-NLS-1$
- + Messages.getString("PreparedStatement.21"), //$NON-NLS-1$
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
- }
-
- ResultSetInternalMethods rs = null;
- CachedResultSetMetaData cachedMetadata = null;
- synchronized (locallyScopedConn.getMutex()) {
- lastQueryIsOnDupKeyUpdate = false;
- if (retrieveGeneratedKeys)
- lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL();
- boolean doStreaming = createStreamingResultSet();
-
- clearWarnings();
- // Adjust net_write_timeout to a higher value if we're
- // streaming result sets. More often than not, someone runs into
- // an issue where they blow net_write_timeout when using this
- // feature, and if they're willing to hold a result set open
- // for 30 seconds or more, one more round-trip isn't going to hurt
- //
- // This is reset by RowDataDynamic.close().
-
- if (doStreaming
- && this.connection.getNetTimeoutForStreamingResults() > 0) {
- executeSimpleNonQuery(locallyScopedConn,
- "SET net_write_timeout="
- + this.connection
- .getNetTimeoutForStreamingResults());
- }
-
- this.batchedGeneratedKeys = null;
- Buffer sendPacket = fillSendPacket();
- String oldCatalog = null;
- if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
- oldCatalog = locallyScopedConn.getCatalog();
- locallyScopedConn.setCatalog(this.currentCatalog);
- }
- //
- // Check if we have cached metadata for this query...
- //
- if (locallyScopedConn.getCacheResultSetMetadata()) {
- cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
- }
- Field[] metadataFromCache = null;
-
- if (cachedMetadata != null) {
- metadataFromCache = cachedMetadata.fields;
- }
-
- boolean oldInfoMsgState = false;
- if (this.retrieveGeneratedKeys) {
- oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
- locallyScopedConn.setReadInfoMsgEnabled(true);
- }
- // If there isn't a limit clause in the SQL
- // then limit the number of rows to return in
- // an efficient manner. Only do this if
- // setMaxRows() hasn't been used on any Statements
- // generated from the current Connection (saves
- // a query, and network traffic).
- //
- // Only apply max_rows to selects
- //
- if (locallyScopedConn.useMaxRows()) {
- int rowLimit = -1;
- if (this.firstCharOfStmt == 'S') {
- if (this.hasLimitClause) {
- rowLimit = this.maxRows;
- } else {
- if (this.maxRows <= 0) {
- executeSimpleNonQuery(locallyScopedConn,
- "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
- } else {
- executeSimpleNonQuery(locallyScopedConn,
- "SET OPTION SQL_SELECT_LIMIT="
- + this.maxRows);
- }
- }
- } else {
- executeSimpleNonQuery(locallyScopedConn,
- "SET OPTION SQL_SELECT_LIMIT=DEFAULT");
- }
- // Finally, execute the query
- rs = executeInternal(rowLimit, sendPacket,
- doStreaming,
- (this.firstCharOfStmt == 'S'), metadataFromCache, false);
- } else {
- rs = executeInternal(-1, sendPacket,
- doStreaming,
- (this.firstCharOfStmt == 'S'), metadataFromCache, false);
- }
- if (cachedMetadata != null) {
- locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
- cachedMetadata, this.results);
- } else {
- if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) {
- locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
- null /* will be created */, rs);
- }
- }
-
- if (this.retrieveGeneratedKeys) {
- locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
- rs.setFirstCharOfQuery(this.firstCharOfStmt);
- }
- if (oldCatalog != null) {
- locallyScopedConn.setCatalog(oldCatalog);
- }
- if (rs != null) {
- this.lastInsertId = rs.getUpdateID();
-
- this.results = rs;
- }
- }
- return ((rs != null) && rs.reallyResult());
- }
- /**
- * JDBC 2.0 Submit a batch of commands to the database for execution. This
- * method is optional.
- *
- * @return an array of update counts containing one element for each command
- * in the batch. The array is ordered according to the order in
- * which commands were inserted into the batch
- *
- * @exception SQLException
- * if a database-access error occurs, or the driver does not
- * support batch statements
- * @throws java.sql.BatchUpdateException
- * DOCUMENT ME!
- */
- public int[] executeBatch() throws SQLException {
- checkClosed();
-
- if (this.connection.isReadOnly()) {
- throw new SQLException(Messages.getString("PreparedStatement.25") //$NON-NLS-1$
- + Messages.getString("PreparedStatement.26"), //$NON-NLS-1$
- SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
- }
- synchronized (this.connection.getMutex()) {
- if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
- return new int[0];
- }
- // we timeout the entire batch, not individual statements
- int batchTimeout = this.timeoutInMillis;
- this.timeoutInMillis = 0;
-
- resetCancelledState();
-
- try {
- clearWarnings();
- if (!this.batchHasPlainStatements
- && this.connection.getRewriteBatchedStatements()) {
-
-
- if (canRewriteAsMultiValueInsertAtSqlLevel()) {
- return executeBatchedInserts(batchTimeout);
- }
-
- if (this.connection.versionMeetsMinimum(4, 1, 0)
- && !this.batchHasPlainStatements
- && this.batchedArgs != null
- && this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) {
- return executePreparedBatchAsMultiStatement(batchTimeout);
- }
- }
- return executeBatchSerially(batchTimeout);
- } finally {
- clearBatch();
- }
- }
- }
- public boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException {
- return this.parseInfo.canRewriteAsMultiValueInsert;
- }
-
- protected int getLocationOfOnDuplicateKeyUpdate() {
- return this.parseInfo.locationOfOnDuplicateKeyUpdate;
- }
- /**
- * Rewrites the already prepared statement into a multi-statement
- * query of 'statementsPerBatch' values and executes the entire batch
- * using this new statement.
- *
- * @return update counts in the same fashion as executeBatch()
- *
- * @throws SQLException
- */
-
- protected int[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException {
- synchronized (this.connection.getMutex()) {
- // This is kind of an abuse, but it gets the job done
- if (this.batchedValuesClause == null) {
- this.batchedValuesClause = this.originalSql + ";";
- }
-
- MySQLConnection locallyScopedConn = this.connection;
-
- boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
- CancelTask timeoutTask = null;
-
- try {
- clearWarnings();
-
- int numBatchedArgs = this.batchedArgs.size();
-
- if (this.retrieveGeneratedKeys) {
- this.batchedGeneratedKeys = new ArrayList(numBatchedArgs);
- }
- int numValuesPerBatch = computeBatchSize(numBatchedArgs);
- if (numBatchedArgs < numValuesPerBatch) {
- numValuesPerBatch = numBatchedArgs;
- }
- java.sql.PreparedStatement batchedStatement = null;
- int batchedParamIndex = 1;
- int numberToExecuteAsMultiValue = 0;
- int batchCounter = 0;
- int updateCountCounter = 0;
- int[] updateCounts = new int[numBatchedArgs];
- SQLException sqlEx = null;
-
- try {
- if (!multiQueriesEnabled) {
- locallyScopedConn.getIO().enableMultiQueries();
- }
-
- if (this.retrieveGeneratedKeys) {
- batchedStatement = locallyScopedConn.prepareStatement(
- generateMultiStatementForBatch(numValuesPerBatch),
- RETURN_GENERATED_KEYS);
- } else {
- batchedStatement = locallyScopedConn
- .prepareStatement(generateMultiStatementForBatch(numValuesPerBatch));
- }
- if (locallyScopedConn.getEnableQueryTimeouts() &&
- batchTimeout != 0
- && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
- timeoutTask = new CancelTask((StatementImpl)batchedStatement);
- locallyScopedConn.getCancelTimer().schedule(timeoutTask,
- batchTimeout);
- }
-
- if (numBatchedArgs < numValuesPerBatch) {
- numberToExecuteAsMultiValue = numBatchedArgs;
- } else {
- numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch;
- }
-
- int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch;
-
- for (int i = 0; i < numberArgsToExecute; i++) {
- if (i != 0 && i % numValuesPerBatch == 0) {
- try {
- batchedStatement.execute();
- } catch (SQLException ex) {
- sqlEx = handleExceptionForBatch(batchCounter, numValuesPerBatch,
- updateCounts, ex);
- }
-
- updateCountCounter = processMultiCountsAndKeys(
- (StatementImpl)batchedStatement, updateCountCounter,
- updateCounts);
-
- batchedStatement.clearParameters();
- batchedParamIndex = 1;
- }
-
- batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
- batchedParamIndex, this.batchedArgs
- .get(batchCounter++));
- }
-
- try {
- batchedStatement.execute();
- } catch (SQLException ex) {
- sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch,
- updateCounts, ex);
- }
-
- updateCountCounter = processMultiCountsAndKeys(
- (StatementImpl)batchedStatement, updateCountCounter,
- updateCounts);
-
- batchedStatement.clearParameters();
-
- numValuesPerBatch = numBatchedArgs - batchCounter;
- } finally {
- if (batchedStatement != null) {
- batchedStatement.close();
- }
- }
-
- try {
- if (numValuesPerBatch > 0) {
-
- if (this.retrieveGeneratedKeys) {
- batchedStatement = locallyScopedConn.prepareStatement(
- generateMultiStatementForBatch(numValuesPerBatch),
- RETURN_GENERATED_KEYS);
- } else {
- batchedStatement = locallyScopedConn.prepareStatement(
- generateMultiStatementForBatch(numValuesPerBatch));
- }
-
- if (timeoutTask != null) {
- timeoutTask.toCancel = (StatementImpl)batchedStatement;
- }
-
- batchedParamIndex = 1;
-
- while (batchCounter < numBatchedArgs) {
- batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
- batchedParamIndex, this.batchedArgs
- .get(batchCounter++));
- }
-
- try {
- batchedStatement.execute();
- } catch (SQLException ex) {
- sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch,
- updateCounts, ex);
- }
-
- updateCountCounter = processMultiCountsAndKeys(
- (StatementImpl)batchedStatement, updateCountCounter,
- updateCounts);
-
- batchedStatement.clearParameters();
- }
-
- if (timeoutTask != null) {
- if (timeoutTask.caughtWhileCancelling != null) {
- throw timeoutTask.caughtWhileCancelling;
- }
- timeoutTask.cancel();
-
- locallyScopedConn.getCancelTimer().purge();
-
- timeoutTask = null;
- }
-
- if (sqlEx != null) {
- throw new java.sql.BatchUpdateException(sqlEx
- .getMessage(), sqlEx.getSQLState(), sqlEx
- .getErrorCode(), updateCounts);
- }
-
- return updateCounts;
- } finally {
- if (batchedStatement != null) {
- batchedStatement.close();
- }
- }
- } finally {
- if (timeoutTask != null) {
- timeoutTask.cancel();
- locallyScopedConn.getCancelTimer().purge();
- }
-
- resetCancelledState();
-
- if (!multiQueriesEnabled) {
- locallyScopedConn.getIO().disableMultiQueries();
- }
-
- clearBatch();
- }
- }
- }
-
- private String generateMultiStatementForBatch(int numBatches) {
- StringBuffer newStatementSql = new StringBuffer((this.originalSql
- .length() + 1) * numBatches);
-
- newStatementSql.append(this.originalSql);
- for (int i = 0; i < numBatches - 1; i++) {
- newStatementSql.append(';');
- newStatementSql.append(this.originalSql);
- }
- return newStatementSql.toString();
- }
-
- /**
- * Rewrites the already prepared statement into a multi-value insert
- * statement of 'statementsPerBatch' values and executes the entire batch
- * using this new statement.
- *
- * @return update counts in the same fashion as executeBatch()
- *
- * @throws SQLException
- */
- protected int[] executeBatchedInserts(int batchTimeout) throws SQLException {
- String valuesClause = getValuesClause();
- MySQLConnection locallyScopedConn = this.connection;
- if (valuesClause == null) {
- return executeBatchSerially(batchTimeout);
- }
- int numBatchedArgs = this.batchedArgs.size();
- if (this.retrieveGeneratedKeys) {
- this.batchedGeneratedKeys = new ArrayLis…
Large files files are truncated, but you can click here to view the full file