Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.mysql.cj.jdbc.StatementImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2002, 2020, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
* Free Software Foundation.
*
* This program is also distributed with certain software (including but not
* limited to OpenSSL) that is licensed under separate terms, as designated in a
* particular file or component or in included license documentation. The
* authors of MySQL hereby grant you an additional permission to link the
* program and your derivative works with the separately licensed software that
* they have included with MySQL.
*
* Without limiting anything contained in the foregoing, this file, which is
* part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at
* http://oss.oracle.com/licenses/universal-foss-exception.
*
* 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, version 2.0,
* 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.cj.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.sql.BatchUpdateException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import com.mysql.cj.CancelQueryTask;
import com.mysql.cj.CharsetMapping;
import com.mysql.cj.Messages;
import com.mysql.cj.MysqlType;
import com.mysql.cj.NativeSession;
import com.mysql.cj.ParseInfo;
import com.mysql.cj.PingTarget;
import com.mysql.cj.Query;
import com.mysql.cj.Session;
import com.mysql.cj.SimpleQuery;
import com.mysql.cj.TransactionEventHandler;
import com.mysql.cj.conf.HostInfo;
import com.mysql.cj.conf.PropertyDefinitions;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.exceptions.AssertionFailedException;
import com.mysql.cj.exceptions.CJException;
import com.mysql.cj.exceptions.CJOperationNotSupportedException;
import com.mysql.cj.exceptions.CJTimeoutException;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.ExceptionInterceptor;
import com.mysql.cj.exceptions.MysqlErrorNumbers;
import com.mysql.cj.exceptions.OperationCancelledException;
import com.mysql.cj.exceptions.StatementIsClosedException;
import com.mysql.cj.jdbc.exceptions.MySQLStatementCancelledException;
import com.mysql.cj.jdbc.exceptions.MySQLTimeoutException;
import com.mysql.cj.jdbc.exceptions.SQLError;
import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping;
import com.mysql.cj.jdbc.result.CachedResultSetMetaData;
import com.mysql.cj.jdbc.result.ResultSetFactory;
import com.mysql.cj.jdbc.result.ResultSetImpl;
import com.mysql.cj.jdbc.result.ResultSetInternalMethods;
import com.mysql.cj.log.ProfilerEvent;
import com.mysql.cj.protocol.Message;
import com.mysql.cj.protocol.ProtocolEntityFactory;
import com.mysql.cj.protocol.Resultset;
import com.mysql.cj.protocol.Resultset.Type;
import com.mysql.cj.protocol.a.NativeConstants;
import com.mysql.cj.protocol.a.NativeMessageBuilder;
import com.mysql.cj.protocol.a.result.ByteArrayRow;
import com.mysql.cj.protocol.a.result.ResultsetRowsStatic;
import com.mysql.cj.result.DefaultColumnDefinition;
import com.mysql.cj.result.Field;
import com.mysql.cj.result.Row;
import com.mysql.cj.util.StringUtils;
import com.mysql.cj.util.Util;
/**
* A Statement object is used for executing a static SQL statement and obtaining
* the results produced by it.
*
* Only one ResultSet per Statement can be open at any point in time. Therefore, if the reading of one ResultSet is interleaved with the reading of another,
* each must have been generated by different Statements. All statement execute methods implicitly close a statement's current ResultSet if an open one exists.
*/
public class StatementImpl implements JdbcStatement {
protected static final String PING_MARKER = "/* ping */";
protected NativeMessageBuilder commandBuilder = new NativeMessageBuilder(); // TODO use shared builder
public final static byte USES_VARIABLES_FALSE = 0;
public final static byte USES_VARIABLES_TRUE = 1;
public final static byte USES_VARIABLES_UNKNOWN = -1;
/** The character encoding to use (if available) */
protected String charEncoding = null;
/** The connection that created us */
protected volatile JdbcConnection connection = null;
/** Should we process escape codes? */
protected boolean doEscapeProcessing = true;
/** Has this statement been closed? */
protected boolean isClosed = false;
/** The auto_increment value for the last insert */
protected long lastInsertId = -1;
/** The max field size for this statement */
protected int maxFieldSize = (Integer) PropertyDefinitions.getPropertyDefinition(PropertyKey.maxAllowedPacket).getDefaultValue();
/**
* The maximum number of rows to return for this statement (-1 means _all_
* rows)
*/
public int maxRows = -1;
/** Set of currently-open ResultSets */
protected Set openResults = new HashSet<>();
/** Are we in pedantic mode? */
protected boolean pedantic = false;
/** Should we profile? */
protected boolean profileSQL = false;
/** The current results */
protected ResultSetInternalMethods results = null;
protected ResultSetInternalMethods generatedKeysResults = null;
/** The concurrency for this result set (updatable or not) */
protected int resultSetConcurrency = 0;
/** The update count for this statement */
protected long updateCount = -1;
/** Should we use the usage advisor? */
protected boolean useUsageAdvisor = false;
/** The warnings chain. */
protected SQLWarning warningChain = null;
/**
* Should this statement hold results open over .close() irregardless of
* connection's setting?
*/
protected boolean holdResultsOpenOverClose = false;
protected ArrayList batchedGeneratedKeys = null;
protected boolean retrieveGeneratedKeys = false;
protected boolean continueBatchOnError = false;
protected PingTarget pingTarget = null;
protected ExceptionInterceptor exceptionInterceptor;
/** Whether or not the last query was of the form ON DUPLICATE KEY UPDATE */
protected boolean lastQueryIsOnDupKeyUpdate = false;
/** Are we currently closing results implicitly (internally)? */
private boolean isImplicitlyClosingResults = false;
protected RuntimeProperty dontTrackOpenResources;
protected RuntimeProperty dumpQueriesOnException;
protected boolean logSlowQueries = false;
protected RuntimeProperty rewriteBatchedStatements;
protected RuntimeProperty maxAllowedPacket;
protected boolean dontCheckOnDuplicateKeyUpdateInSQL;
protected RuntimeProperty sendFractionalSeconds;
protected ResultSetFactory resultSetFactory;
protected Query query;
protected NativeSession session = null;
/**
* Constructor for a Statement.
*
* @param c
* the Connection instance that creates us
* @param db
* the database name in use when we were created
*
* @throws SQLException
* if an error occurs.
*/
public StatementImpl(JdbcConnection c, String db) throws SQLException {
if ((c == null) || c.isClosed()) {
throw SQLError.createSQLException(Messages.getString("Statement.0"), MysqlErrorNumbers.SQL_STATE_CONNECTION_NOT_OPEN, null);
}
this.connection = c;
this.session = (NativeSession) c.getSession();
this.exceptionInterceptor = c.getExceptionInterceptor();
try {
initQuery();
} catch (CJException e) {
throw SQLExceptionsMapping.translateException(e, getExceptionInterceptor());
}
this.query.setCurrentDatabase(db);
JdbcPropertySet pset = c.getPropertySet();
this.dontTrackOpenResources = pset.getBooleanProperty(PropertyKey.dontTrackOpenResources);
this.dumpQueriesOnException = pset.getBooleanProperty(PropertyKey.dumpQueriesOnException);
this.continueBatchOnError = pset.getBooleanProperty(PropertyKey.continueBatchOnError).getValue();
this.pedantic = pset.getBooleanProperty(PropertyKey.pedantic).getValue();
this.rewriteBatchedStatements = pset.getBooleanProperty(PropertyKey.rewriteBatchedStatements);
this.charEncoding = pset.getStringProperty(PropertyKey.characterEncoding).getValue();
this.profileSQL = pset.getBooleanProperty(PropertyKey.profileSQL).getValue();
this.useUsageAdvisor = pset.getBooleanProperty(PropertyKey.useUsageAdvisor).getValue();
this.logSlowQueries = pset.getBooleanProperty(PropertyKey.logSlowQueries).getValue();
this.maxAllowedPacket = pset.getIntegerProperty(PropertyKey.maxAllowedPacket);
this.dontCheckOnDuplicateKeyUpdateInSQL = pset.getBooleanProperty(PropertyKey.dontCheckOnDuplicateKeyUpdateInSQL).getValue();
this.sendFractionalSeconds = pset.getBooleanProperty(PropertyKey.sendFractionalSeconds);
this.doEscapeProcessing = pset.getBooleanProperty(PropertyKey.enableEscapeProcessing).getValue();
this.maxFieldSize = this.maxAllowedPacket.getValue();
if (!this.dontTrackOpenResources.getValue()) {
c.registerStatement(this);
}
int defaultFetchSize = pset.getIntegerProperty(PropertyKey.defaultFetchSize).getValue();
if (defaultFetchSize != 0) {
setFetchSize(defaultFetchSize);
}
int maxRowsConn = pset.getIntegerProperty(PropertyKey.maxRows).getValue();
if (maxRowsConn != -1) {
setMaxRows(maxRowsConn);
}
this.holdResultsOpenOverClose = pset.getBooleanProperty(PropertyKey.holdResultsOpenOverStatementClose).getValue();
this.resultSetFactory = new ResultSetFactory(this.connection, this);
}
protected void initQuery() {
this.query = new SimpleQuery(this.session);
}
@Override
public void addBatch(String sql) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (sql != null) {
this.query.addBatch(sql);
}
}
}
@Override
public void addBatch(Object batch) {
this.query.addBatch(batch);
}
@Override
public List getBatchedArgs() {
return this.query.getBatchedArgs();
}
@Override
public void cancel() throws SQLException {
if (!this.query.getStatementExecuting().get()) {
return;
}
if (!this.isClosed && this.connection != null) {
JdbcConnection cancelConn = null;
java.sql.Statement cancelStmt = null;
try {
HostInfo hostInfo = this.session.getHostInfo();
String database = hostInfo.getDatabase();
String user = StringUtils.isNullOrEmpty(hostInfo.getUser()) ? "" : hostInfo.getUser();
String password = StringUtils.isNullOrEmpty(hostInfo.getPassword()) ? "" : hostInfo.getPassword();
NativeSession newSession = new NativeSession(this.session.getHostInfo(), this.session.getPropertySet());
newSession.connect(hostInfo, user, password, database, 30000, new TransactionEventHandler() {
@Override
public void transactionCompleted() {
}
@Override
public void transactionBegun() {
}
});
newSession.sendCommand(new NativeMessageBuilder().buildComQuery(newSession.getSharedSendPacket(), "KILL QUERY " + this.session.getThreadId()),
false, 0);
setCancelStatus(CancelStatus.CANCELED_BY_USER);
} catch (IOException e) {
throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor);
} finally {
if (cancelStmt != null) {
cancelStmt.close();
}
if (cancelConn != null) {
cancelConn.close();
}
}
}
}
// --------------------------JDBC 2.0-----------------------------
/**
* Checks if closed() has been called, and throws an exception if so
*
* @return connection
* @throws StatementIsClosedException
* if this statement has been closed
*/
protected JdbcConnection checkClosed() {
JdbcConnection c = this.connection;
if (c == null) {
throw ExceptionFactory.createException(StatementIsClosedException.class, Messages.getString("Statement.AlreadyClosed"), getExceptionInterceptor());
}
return c;
}
/**
* Checks if the given SQL query with the given first non-ws char is a DML
* statement. Throws an exception if it is.
*
* @param sql
* the SQL to check
* @param firstStatementChar
* the UC first non-ws char of the statement
*
* @throws SQLException
* if the statement contains DML
*/
protected void checkForDml(String sql, char firstStatementChar) throws SQLException {
if ((firstStatementChar == 'I') || (firstStatementChar == 'U') || (firstStatementChar == 'D') || (firstStatementChar == 'A')
|| (firstStatementChar == 'C') || (firstStatementChar == 'T') || (firstStatementChar == 'R')) {
String noCommentSql = StringUtils.stripComments(sql, "'\"", "'\"", true, false, true, true);
if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE")
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP")
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")
|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "TRUNCATE") || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "RENAME")) {
throw SQLError.createSQLException(Messages.getString("Statement.57"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
}
}
/**
* Method checkNullOrEmptyQuery.
*
* @param sql
* the SQL to check
*
* @throws SQLException
* if query is null or empty.
*/
protected void checkNullOrEmptyQuery(String sql) throws SQLException {
if (sql == null) {
throw SQLError.createSQLException(Messages.getString("Statement.59"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
if (sql.length() == 0) {
throw SQLError.createSQLException(Messages.getString("Statement.61"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
}
@Override
public void clearBatch() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
this.query.clearBatchedArgs();
}
}
@Override
public void clearBatchedArgs() {
this.query.clearBatchedArgs();
}
@Override
public void clearWarnings() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
setClearWarningsCalled(true);
this.warningChain = null;
// TODO souldn't we also clear warnings from _server_ ?
}
}
/**
* In many cases, it is desirable to immediately release a Statement's
* database and JDBC resources instead of waiting for this to happen when it
* is automatically closed. The close method provides this immediate
* release.
*
*
* Note: A Statement is automatically closed when it is garbage collected. When a Statement is closed, its current ResultSet, if one exists, is also
* closed.
*
*
* @exception SQLException
* if a database access error occurs
*/
@Override
public void close() throws SQLException {
realClose(true, true);
}
/**
* Close any open result sets that have been 'held open'
*
* @throws SQLException
* if an error occurs
*/
protected void closeAllOpenResults() throws SQLException {
JdbcConnection locallyScopedConn = this.connection;
if (locallyScopedConn == null) {
return; // already closed
}
synchronized (locallyScopedConn.getConnectionMutex()) {
if (this.openResults != null) {
for (ResultSetInternalMethods element : this.openResults) {
try {
element.realClose(false);
} catch (SQLException sqlEx) {
AssertionFailedException.shouldNotHappen(sqlEx);
}
}
this.openResults.clear();
}
}
}
/**
* Close all result sets in this statement. This includes multi-results
*
* @throws SQLException
* if a database access error occurs
*/
protected void implicitlyCloseAllOpenResults() throws SQLException {
this.isImplicitlyClosingResults = true;
try {
if (!(this.holdResultsOpenOverClose || this.dontTrackOpenResources.getValue())) {
if (this.results != null) {
this.results.realClose(false);
}
if (this.generatedKeysResults != null) {
this.generatedKeysResults.realClose(false);
}
closeAllOpenResults();
}
} finally {
this.isImplicitlyClosingResults = false;
}
}
@Override
public void removeOpenResultSet(ResultSetInternalMethods rs) {
try {
synchronized (checkClosed().getConnectionMutex()) {
if (this.openResults != null) {
this.openResults.remove(rs);
}
boolean hasMoreResults = rs.getNextResultset() != null;
// clear the current results or GGK results
if (this.results == rs && !hasMoreResults) {
this.results = null;
}
if (this.generatedKeysResults == rs) {
this.generatedKeysResults = null;
}
// trigger closeOnCompletion if:
// a) the result set removal wasn't triggered internally
// b) there are no additional results
if (!this.isImplicitlyClosingResults && !hasMoreResults) {
checkAndPerformCloseOnCompletionAction();
}
}
} catch (StatementIsClosedException e) {
// we can't break the interface, having this be no-op in case of error is ok
}
}
@Override
public int getOpenResultSetCount() {
try {
synchronized (checkClosed().getConnectionMutex()) {
if (this.openResults != null) {
return this.openResults.size();
}
return 0;
}
} catch (StatementIsClosedException e) {
// we can't break the interface, having this be no-op in case of error is ok
return 0;
}
}
/**
* Check if all ResultSets generated by this statement are closed. If so,
* close this statement.
*/
private void checkAndPerformCloseOnCompletionAction() {
try {
synchronized (checkClosed().getConnectionMutex()) {
if (isCloseOnCompletion() && !this.dontTrackOpenResources.getValue() && getOpenResultSetCount() == 0
&& (this.results == null || !this.results.hasRows() || this.results.isClosed())
&& (this.generatedKeysResults == null || !this.generatedKeysResults.hasRows() || this.generatedKeysResults.isClosed())) {
realClose(false, false);
}
}
} catch (SQLException e) {
}
}
/**
* @param sql
* query
* @return result set
* @throws SQLException
* if a database access error occurs or this method is called on a closed Statement
*/
private ResultSetInternalMethods createResultSetUsingServerFetch(String sql) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
java.sql.PreparedStatement pStmt = this.connection.prepareStatement(sql, this.query.getResultType().getIntValue(), this.resultSetConcurrency);
pStmt.setFetchSize(this.query.getResultFetchSize());
if (this.maxRows > -1) {
pStmt.setMaxRows(this.maxRows);
}
statementBegins();
pStmt.execute();
//
// Need to be able to get resultset irrespective if we issued DML or not to make this work.
//
ResultSetInternalMethods rs = ((StatementImpl) pStmt).getResultSetInternal();
rs.setStatementUsedForFetchingRows((ClientPreparedStatement) pStmt);
this.results = rs;
return rs;
}
}
/**
* We only stream result sets when they are forward-only, read-only, and the
* fetch size has been set to Integer.MIN_VALUE
*
* @return true if this result set should be streamed row at-a-time, rather
* than read all at once.
*/
protected boolean createStreamingResultSet() {
return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
&& (this.query.getResultFetchSize() == Integer.MIN_VALUE));
}
private Resultset.Type originalResultSetType = Type.FORWARD_ONLY;
private int originalFetchSize = 0;
@Override
public void enableStreamingResults() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
this.originalResultSetType = this.query.getResultType();
this.originalFetchSize = this.query.getResultFetchSize();
setFetchSize(Integer.MIN_VALUE);
setResultSetType(Type.FORWARD_ONLY);
}
}
@Override
public void disableStreamingResults() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.query.getResultFetchSize() == Integer.MIN_VALUE && this.query.getResultType() == Type.FORWARD_ONLY) {
setFetchSize(this.originalFetchSize);
setResultSetType(this.originalResultSetType);
}
}
}
/**
* 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().
*
* @param con
* created this statement
* @throws SQLException
* if a database error occurs
*/
protected void setupStreamingTimeout(JdbcConnection con) throws SQLException {
int netTimeoutForStreamingResults = this.session.getPropertySet().getIntegerProperty(PropertyKey.netTimeoutForStreamingResults).getValue();
if (createStreamingResultSet() && netTimeoutForStreamingResults > 0) {
executeSimpleNonQuery(con, "SET net_write_timeout=" + netTimeoutForStreamingResults);
}
}
@Override
public CancelQueryTask startQueryTimer(Query stmtToCancel, int timeout) {
return this.query.startQueryTimer(stmtToCancel, timeout);
}
@Override
public void stopQueryTimer(CancelQueryTask timeoutTask, boolean rethrowCancelReason, boolean checkCancelTimeout) {
this.query.stopQueryTimer(timeoutTask, rethrowCancelReason, checkCancelTimeout);
}
@Override
public boolean execute(String sql) throws SQLException {
return executeInternal(sql, false);
}
private boolean executeInternal(String sql, boolean returnGeneratedKeys) throws SQLException {
JdbcConnection locallyScopedConn = checkClosed();
synchronized (locallyScopedConn.getConnectionMutex()) {
checkClosed();
checkNullOrEmptyQuery(sql);
resetCancelledState();
implicitlyCloseAllOpenResults();
if (sql.charAt(0) == '/') {
if (sql.startsWith(PING_MARKER)) {
doPingInstead();
return true;
}
}
char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));
boolean maybeSelect = firstNonWsChar == 'S';
this.retrieveGeneratedKeys = returnGeneratedKeys;
this.lastQueryIsOnDupKeyUpdate = returnGeneratedKeys && firstNonWsChar == 'I' && containsOnDuplicateKeyInString(sql);
if (!maybeSelect && locallyScopedConn.isReadOnly()) {
throw SQLError.createSQLException(Messages.getString("Statement.27") + Messages.getString("Statement.28"),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
try {
setupStreamingTimeout(locallyScopedConn);
if (this.doEscapeProcessing) {
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.session.getServerSession().getServerTimeZone(),
this.session.getServerSession().getCapabilities().serverSupportsFracSecs(),
this.session.getServerSession().isServerTruncatesFracSecs(), getExceptionInterceptor());
sql = escapedSqlResult instanceof String ? (String) escapedSqlResult : ((EscapeProcessorResult) escapedSqlResult).escapedSql;
}
CachedResultSetMetaData cachedMetaData = null;
ResultSetInternalMethods rs = null;
this.batchedGeneratedKeys = null;
if (useServerFetch()) {
rs = createResultSetUsingServerFetch(sql);
} else {
CancelQueryTask timeoutTask = null;
String oldDb = null;
try {
timeoutTask = startQueryTimer(this, getTimeoutInMillis());
if (!locallyScopedConn.getDatabase().equals(getCurrentDatabase())) {
oldDb = locallyScopedConn.getDatabase();
locallyScopedConn.setDatabase(getCurrentDatabase());
}
// Check if we have cached metadata for this query...
if (locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue()) {
cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
}
// Only apply max_rows to selects
locallyScopedConn.setSessionMaxRows(maybeSelect ? this.maxRows : -1);
statementBegins();
rs = ((NativeSession) locallyScopedConn.getSession()).execSQL(this, sql, this.maxRows, null, createStreamingResultSet(),
getResultSetFactory(), cachedMetaData, false);
if (timeoutTask != null) {
stopQueryTimer(timeoutTask, true, true);
timeoutTask = null;
}
} catch (CJTimeoutException | OperationCancelledException e) {
throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor);
} finally {
stopQueryTimer(timeoutTask, false, false);
if (oldDb != null) {
locallyScopedConn.setDatabase(oldDb);
}
}
}
if (rs != null) {
this.lastInsertId = rs.getUpdateID();
this.results = rs;
rs.setFirstCharOfQuery(firstNonWsChar);
if (rs.hasRows()) {
if (cachedMetaData != null) {
locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, this.results);
} else if (this.session.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue()) {
locallyScopedConn.initializeResultsMetadataFromCache(sql, null /* will be created */, this.results);
}
}
}
return ((rs != null) && rs.hasRows());
} finally {
this.query.getStatementExecuting().set(false);
}
}
}
@Override
public void statementBegins() {
this.query.statementBegins();
}
@Override
public void resetCancelledState() {
synchronized (checkClosed().getConnectionMutex()) {
this.query.resetCancelledState();
}
}
@Override
public boolean execute(String sql, int returnGeneratedKeys) throws SQLException {
return executeInternal(sql, returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS);
}
@Override
public boolean execute(String sql, int[] generatedKeyIndices) throws SQLException {
return executeInternal(sql, generatedKeyIndices != null && generatedKeyIndices.length > 0);
}
@Override
public boolean execute(String sql, String[] generatedKeyNames) throws SQLException {
return executeInternal(sql, generatedKeyNames != null && generatedKeyNames.length > 0);
}
@Override
public int[] executeBatch() throws SQLException {
return Util.truncateAndConvertToInt(executeBatchInternal());
}
protected long[] executeBatchInternal() throws SQLException {
JdbcConnection locallyScopedConn = checkClosed();
synchronized (locallyScopedConn.getConnectionMutex()) {
if (locallyScopedConn.isReadOnly()) {
throw SQLError.createSQLException(Messages.getString("Statement.34") + Messages.getString("Statement.35"),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
implicitlyCloseAllOpenResults();
List batchedArgs = this.query.getBatchedArgs();
if (batchedArgs == null || batchedArgs.size() == 0) {
return new long[0];
}
// we timeout the entire batch, not individual statements
int individualStatementTimeout = getTimeoutInMillis();
setTimeoutInMillis(0);
CancelQueryTask timeoutTask = null;
try {
resetCancelledState();
statementBegins();
try {
this.retrieveGeneratedKeys = true; // The JDBC spec doesn't forbid this, but doesn't provide for it either...we do..
long[] updateCounts = null;
if (batchedArgs != null) {
int nbrCommands = batchedArgs.size();
this.batchedGeneratedKeys = new ArrayList<>(batchedArgs.size());
boolean multiQueriesEnabled = locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.allowMultiQueries).getValue();
if (multiQueriesEnabled || (locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.rewriteBatchedStatements).getValue()
&& nbrCommands > 4)) {
return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout);
}
timeoutTask = startQueryTimer(this, individualStatementTimeout);
updateCounts = new long[nbrCommands];
for (int i = 0; i < nbrCommands; i++) {
updateCounts[i] = -3;
}
SQLException sqlEx = null;
int commandIndex = 0;
for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
try {
String sql = (String) batchedArgs.get(commandIndex);
updateCounts[commandIndex] = executeUpdateInternal(sql, true, true);
if (timeoutTask != null) {
// we need to check the cancel state on each iteration to generate timeout exception if needed
checkCancelTimeout();
}
// limit one generated key per OnDuplicateKey statement
getBatchedGeneratedKeys(this.results.getFirstCharOfQuery() == 'I' && containsOnDuplicateKeyInString(sql) ? 1 : 0);
} catch (SQLException ex) {
updateCounts[commandIndex] = EXECUTE_FAILED;
if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException)
&& !hasDeadlockOrTimeoutRolledBackTx(ex)) {
sqlEx = ex;
} else {
long[] newUpdateCounts = new long[commandIndex];
if (hasDeadlockOrTimeoutRolledBackTx(ex)) {
for (int i = 0; i < newUpdateCounts.length; i++) {
newUpdateCounts[i] = java.sql.Statement.EXECUTE_FAILED;
}
} else {
System.arraycopy(updateCounts, 0, newUpdateCounts, 0, commandIndex);
}
sqlEx = ex;
break;
//throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor());
}
}
}
if (sqlEx != null) {
throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor());
}
}
if (timeoutTask != null) {
stopQueryTimer(timeoutTask, true, true);
timeoutTask = null;
}
return (updateCounts != null) ? updateCounts : new long[0];
} finally {
this.query.getStatementExecuting().set(false);
}
} finally {
stopQueryTimer(timeoutTask, false, false);
resetCancelledState();
setTimeoutInMillis(individualStatementTimeout);
clearBatch();
}
}
}
protected final boolean hasDeadlockOrTimeoutRolledBackTx(SQLException ex) {
int vendorCode = ex.getErrorCode();
switch (vendorCode) {
case MysqlErrorNumbers.ER_LOCK_DEADLOCK:
case MysqlErrorNumbers.ER_LOCK_TABLE_FULL:
return true;
case MysqlErrorNumbers.ER_LOCK_WAIT_TIMEOUT:
return false;
default:
return false;
}
}
/**
* Rewrites batch into a single query to send to the server. This method
* will constrain each batch to be shorter than max_allowed_packet on the
* server.
*
* @param multiQueriesEnabled
* is multi-queries syntax allowed?
* @param nbrCommands
* number of queries in a batch
* @param individualStatementTimeout
* timeout for a single query in a batch
*
* @return update counts in the same manner as executeBatch()
* @throws SQLException
* if a database access error occurs or this method is called on a closed PreparedStatement
*/
private long[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled, int nbrCommands, int individualStatementTimeout) throws SQLException {
JdbcConnection locallyScopedConn = checkClosed();
synchronized (locallyScopedConn.getConnectionMutex()) {
if (!multiQueriesEnabled) {
this.session.enableMultiQueries();
}
java.sql.Statement batchStmt = null;
CancelQueryTask timeoutTask = null;
try {
long[] updateCounts = new long[nbrCommands];
for (int i = 0; i < nbrCommands; i++) {
updateCounts[i] = JdbcStatement.EXECUTE_FAILED;
}
int commandIndex = 0;
StringBuilder queryBuf = new StringBuilder();
batchStmt = locallyScopedConn.createStatement();
timeoutTask = startQueryTimer((StatementImpl) batchStmt, individualStatementTimeout);
int counter = 0;
String connectionEncoding = locallyScopedConn.getPropertySet().getStringProperty(PropertyKey.characterEncoding).getValue();
int numberOfBytesPerChar = StringUtils.startsWithIgnoreCase(connectionEncoding, "utf") ? 3
: (CharsetMapping.isMultibyteCharset(connectionEncoding) ? 2 : 1);
int escapeAdjust = 1;
batchStmt.setEscapeProcessing(this.doEscapeProcessing);
if (this.doEscapeProcessing) {
escapeAdjust = 2; // We assume packet _could_ grow by this amount, as we're not sure how big statement will end up after escape processing
}
SQLException sqlEx = null;
int argumentSetsInBatchSoFar = 0;
for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
String nextQuery = (String) this.query.getBatchedArgs().get(commandIndex);
if (((((queryBuf.length() + nextQuery.length()) * numberOfBytesPerChar) + 1 /* for semicolon */
+ NativeConstants.HEADER_LENGTH) * escapeAdjust) + 32 > this.maxAllowedPacket.getValue()) {
try {
batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS);
} catch (SQLException ex) {
sqlEx = handleExceptionForBatch(commandIndex, argumentSetsInBatchSoFar, updateCounts, ex);
}
counter = processMultiCountsAndKeys((StatementImpl) batchStmt, counter, updateCounts);
queryBuf = new StringBuilder();
argumentSetsInBatchSoFar = 0;
}
queryBuf.append(nextQuery);
queryBuf.append(";");
argumentSetsInBatchSoFar++;
}
if (queryBuf.length() > 0) {
try {
batchStmt.execute(queryBuf.toString(), java.sql.Statement.RETURN_GENERATED_KEYS);
} catch (SQLException ex) {
sqlEx = handleExceptionForBatch(commandIndex - 1, argumentSetsInBatchSoFar, updateCounts, ex);
}
counter = processMultiCountsAndKeys((StatementImpl) batchStmt, counter, updateCounts);
}
if (timeoutTask != null) {
stopQueryTimer(timeoutTask, true, true);
timeoutTask = null;
}
if (sqlEx != null) {
throw SQLError.createBatchUpdateException(sqlEx, updateCounts, getExceptionInterceptor());
}
return (updateCounts != null) ? updateCounts : new long[0];
} finally {
stopQueryTimer(timeoutTask, false, false);
resetCancelledState();
try {
if (batchStmt != null) {
batchStmt.close();
}
} finally {
if (!multiQueriesEnabled) {
this.session.disableMultiQueries();
}
}
}
}
}
protected int processMultiCountsAndKeys(StatementImpl batchedStatement, int updateCountCounter, long[] updateCounts) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount();
boolean doGenKeys = this.batchedGeneratedKeys != null;
byte[][] row = null;
if (doGenKeys) {
long generatedKey = batchedStatement.getLastInsertID();
row = new byte[1][];
row[0] = StringUtils.getBytes(Long.toString(generatedKey));
this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
}
while (batchedStatement.getMoreResults() || batchedStatement.getLargeUpdateCount() != -1) {
updateCounts[updateCountCounter++] = batchedStatement.getLargeUpdateCount();
if (doGenKeys) {
long generatedKey = batchedStatement.getLastInsertID();
row = new byte[1][];
row[0] = StringUtils.getBytes(Long.toString(generatedKey));
this.batchedGeneratedKeys.add(new ByteArrayRow(row, getExceptionInterceptor()));
}
}
return updateCountCounter;
}
}
protected SQLException handleExceptionForBatch(int endOfBatchIndex, int numValuesPerBatch, long[] updateCounts, SQLException ex)
throws BatchUpdateException, SQLException {
for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) {
updateCounts[j] = EXECUTE_FAILED;
}
if (this.continueBatchOnError && !(ex instanceof MySQLTimeoutException) && !(ex instanceof MySQLStatementCancelledException)
&& !hasDeadlockOrTimeoutRolledBackTx(ex)) {
return ex;
} // else: throw the exception immediately
long[] newUpdateCounts = new long[endOfBatchIndex];
System.arraycopy(updateCounts, 0, newUpdateCounts, 0, endOfBatchIndex);
throw SQLError.createBatchUpdateException(ex, newUpdateCounts, getExceptionInterceptor());
}
@Override
public java.sql.ResultSet executeQuery(String sql) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
JdbcConnection locallyScopedConn = this.connection;
this.retrieveGeneratedKeys = false;
checkNullOrEmptyQuery(sql);
resetCancelledState();
implicitlyCloseAllOpenResults();
if (sql.charAt(0) == '/') {
if (sql.startsWith(PING_MARKER)) {
doPingInstead();
return this.results;
}
}
setupStreamingTimeout(locallyScopedConn);
if (this.doEscapeProcessing) {
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.session.getServerSession().getServerTimeZone(),
this.session.getServerSession().getCapabilities().serverSupportsFracSecs(), this.session.getServerSession().isServerTruncatesFracSecs(),
getExceptionInterceptor());
sql = escapedSqlResult instanceof String ? (String) escapedSqlResult : ((EscapeProcessorResult) escapedSqlResult).escapedSql;
}
char firstStatementChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));
checkForDml(sql, firstStatementChar);
CachedResultSetMetaData cachedMetaData = null;
if (useServerFetch()) {
this.results = createResultSetUsingServerFetch(sql);
return this.results;
}
CancelQueryTask timeoutTask = null;
String oldDb = null;
try {
timeoutTask = startQueryTimer(this, getTimeoutInMillis());
if (!locallyScopedConn.getDatabase().equals(getCurrentDatabase())) {
oldDb = locallyScopedConn.getDatabase();
locallyScopedConn.setDatabase(getCurrentDatabase());
}
//
// Check if we have cached metadata for this query...
//
if (locallyScopedConn.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue()) {
cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
}
locallyScopedConn.setSessionMaxRows(this.maxRows);
statementBegins();
this.results = ((NativeSession) locallyScopedConn.getSession()).execSQL(this, sql, this.maxRows, null, createStreamingResultSet(),
getResultSetFactory(), cachedMetaData, false);
if (timeoutTask != null) {
stopQueryTimer(timeoutTask, true, true);
timeoutTask = null;
}
} catch (CJTimeoutException | OperationCancelledException e) {
throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor);
} finally {
this.query.getStatementExecuting().set(false);
stopQueryTimer(timeoutTask, false, false);
if (oldDb != null) {
locallyScopedConn.setDatabase(oldDb);
}
}
this.lastInsertId = this.results.getUpdateID();
if (cachedMetaData != null) {
locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, this.results);
} else {
if (this.connection.getPropertySet().getBooleanProperty(PropertyKey.cacheResultSetMetadata).getValue()) {
locallyScopedConn.initializeResultsMetadataFromCache(sql, null /* will be created */, this.results);
}
}
return this.results;
}
}
protected void doPingInstead() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.pingTarget != null) {
try {
this.pingTarget.doPing();
} catch (SQLException e) {
throw e;
} catch (Exception e) {
throw SQLError.createSQLException(e.getMessage(), MysqlErrorNumbers.SQL_STATE_COMMUNICATION_LINK_FAILURE, e, getExceptionInterceptor());
}
} else {
this.connection.ping();
}
ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet();
this.results = fakeSelectOneResultSet;
}
}
protected ResultSetInternalMethods generatePingResultSet() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
String encoding = this.session.getServerSession().getCharacterSetMetadata();
int collationIndex = this.session.getServerSession().getMetadataCollationIndex();
Field[] fields = { new Field(null, "1", collationIndex, encoding, MysqlType.BIGINT, 1) };
ArrayList rows = new ArrayList<>();
byte[] colVal = new byte[] { (byte) '1' };
rows.add(new ByteArrayRow(new byte[][] { colVal }, getExceptionInterceptor()));
return this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE,
new ResultsetRowsStatic(rows, new DefaultColumnDefinition(fields)));
}
}
public void executeSimpleNonQuery(JdbcConnection c, String nonQuery) throws SQLException {
synchronized (c.getConnectionMutex()) {
((NativeSession) c.getSession()).execSQL(this, nonQuery, -1, null, false, getResultSetFactory(), null, false).close();
}
}
@Override
public int executeUpdate(String sql) throws SQLException {
return Util.truncateAndConvertToInt(executeLargeUpdate(sql));
}
protected long executeUpdateInternal(String sql, boolean isBatch, boolean returnGeneratedKeys) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
JdbcConnection locallyScopedConn = this.connection;
checkNullOrEmptyQuery(sql);
resetCancelledState();
char firstStatementChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));
this.retrieveGeneratedKeys = returnGeneratedKeys;
this.lastQueryIsOnDupKeyUpdate = returnGeneratedKeys && firstStatementChar == 'I' && containsOnDuplicateKeyInString(sql);
ResultSetInternalMethods rs = null;
if (this.doEscapeProcessing) {
Object escapedSqlResult = EscapeProcessor.escapeSQL(sql, this.session.getServerSession().getServerTimeZone(),
this.session.getServerSession().getCapabilities().serverSupportsFracSecs(), this.session.getServerSession().isServerTruncatesFracSecs(),
getExceptionInterceptor());
sql = escapedSqlResult instanceof String ? (String) escapedSqlResult : ((EscapeProcessorResult) escapedSqlResult).escapedSql;
}
if (locallyScopedConn.isReadOnly(false)) {
throw SQLError.createSQLException(Messages.getString("Statement.42") + Messages.getString("Statement.43"),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) {
throw SQLError.createSQLException(Messages.getString("Statement.46"), "01S03", getExceptionInterceptor());
}
implicitlyCloseAllOpenResults();
// The checking and changing of databases must happen in sequence, so synchronize on the same mutex that _conn is using
CancelQueryTask timeoutTask = null;
String oldDb = null;
try {
timeoutTask = startQueryTimer(this, getTimeoutInMillis());
if (!locallyScopedConn.getDatabase().equals(getCurrentDatabase())) {
oldDb = locallyScopedConn.getDatabase();
locallyScopedConn.setDatabase(getCurrentDatabase());
}
//
// Only apply max_rows to selects
//
locallyScopedConn.setSessionMaxRows(-1);
statementBegins();
// null database: force read of field info on DML
rs = ((NativeSession) locallyScopedConn.getSession()).execSQL(this, sql, -1, null, false, getResultSetFactory(), null, isBatch);
if (timeoutTask != null) {
stopQueryTimer(timeoutTask, true, true);
timeoutTask = null;
}
} catch (CJTimeoutException | OperationCancelledException e) {
throw SQLExceptionsMapping.translateException(e, this.exceptionInterceptor);
} finally {
stopQueryTimer(timeoutTask, false, false);
if (oldDb != null) {
locallyScopedConn.setDatabase(oldDb);
}
if (!isBatch) {
this.query.getStatementExecuting().set(false);
}
}
this.results = rs;
rs.setFirstCharOfQuery(firstStatementChar);
this.updateCount = rs.getUpdateCount();
this.lastInsertId = rs.getUpdateID();
return this.updateCount;
}
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return Util.truncateAndConvertToInt(executeLargeUpdate(sql, autoGeneratedKeys));
}
@Override
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
return Util.truncateAndConvertToInt(executeLargeUpdate(sql, columnIndexes));
}
@Override
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
return Util.truncateAndConvertToInt(executeLargeUpdate(sql, columnNames));
}
@Override
public java.sql.Connection getConnection() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.connection;
}
}
@Override
public int getFetchDirection() throws SQLException {
return java.sql.ResultSet.FETCH_FORWARD;
}
@Override
public int getFetchSize() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.query.getResultFetchSize();
}
}
@Override
public java.sql.ResultSet getGeneratedKeys() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (!this.retrieveGeneratedKeys) {
throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
if (this.batchedGeneratedKeys == null) {
if (this.lastQueryIsOnDupKeyUpdate) {
return this.generatedKeysResults = getGeneratedKeysInternal(1);
}
return this.generatedKeysResults = getGeneratedKeysInternal();
}
String encoding = this.session.getServerSession().getCharacterSetMetadata();
int collationIndex = this.session.getServerSession().getMetadataCollationIndex();
Field[] fields = new Field[1];
fields[0] = new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20);
this.generatedKeysResults = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE,
new ResultsetRowsStatic(this.batchedGeneratedKeys, new DefaultColumnDefinition(fields)));
return this.generatedKeysResults;
}
}
/*
* Needed because there's no concept of super.super to get to this
* implementation from ServerPreparedStatement when dealing with batched
* updates.
*/
protected ResultSetInternalMethods getGeneratedKeysInternal() throws SQLException {
long numKeys = getLargeUpdateCount();
return getGeneratedKeysInternal(numKeys);
}
protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
String encoding = this.session.getServerSession().getCharacterSetMetadata();
int collationIndex = this.session.getServerSession().getMetadataCollationIndex();
Field[] fields = new Field[1];
fields[0] = new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20);
ArrayList rowSet = new ArrayList<>();
long beginAt = getLastInsertID();
if (this.results != null) {
String serverInfo = this.results.getServerInfo();
//
// Only parse server info messages for 'REPLACE' queries
//
if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R') && (serverInfo != null) && (serverInfo.length() > 0)) {
numKeys = getRecordCountFromInfo(serverInfo);
}
if ((beginAt != 0 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) {
for (int i = 0; i < numKeys; i++) {
byte[][] row = new byte[1][];
if (beginAt > 0) {
row[0] = StringUtils.getBytes(Long.toString(beginAt));
} else {
byte[] asBytes = new byte[8];
asBytes[7] = (byte) (beginAt & 0xff);
asBytes[6] = (byte) (beginAt >>> 8);
asBytes[5] = (byte) (beginAt >>> 16);
asBytes[4] = (byte) (beginAt >>> 24);
asBytes[3] = (byte) (beginAt >>> 32);
asBytes[2] = (byte) (beginAt >>> 40);
asBytes[1] = (byte) (beginAt >>> 48);
asBytes[0] = (byte) (beginAt >>> 56);
BigInteger val = new BigInteger(1, asBytes);
row[0] = val.toString().getBytes();
}
rowSet.add(new ByteArrayRow(row, getExceptionInterceptor()));
beginAt += this.connection.getAutoIncrementIncrement();
}
}
}
ResultSetImpl gkRs = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE,
new ResultsetRowsStatic(rowSet, new DefaultColumnDefinition(fields)));
return gkRs;
}
}
/**
* getLastInsertID returns the value of the auto_incremented key after an
* executeQuery() or excute() call.
*
*
* This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()" which is tied to the Connection that created this Statement, and therefore could
* have had many INSERTS performed before one gets a chance to call "select LAST_INSERT_ID()".
*
*
* @return the last update ID.
*/
public long getLastInsertID() {
synchronized (checkClosed().getConnectionMutex()) {
return this.lastInsertId;
}
}
/**
* getLongUpdateCount returns the current result as an update count, if the
* result is a ResultSet or there are no more results, -1 is returned. It
* should only be called once per result.
*
*
* This method returns longs as MySQL server returns 64-bit values for update counts
*
*
* @return the current update count.
*/
public long getLongUpdateCount() {
synchronized (checkClosed().getConnectionMutex()) {
if (this.results == null) {
return -1;
}
if (this.results.hasRows()) {
return -1;
}
return this.updateCount;
}
}
@Override
public int getMaxFieldSize() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.maxFieldSize;
}
}
@Override
public int getMaxRows() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.maxRows <= 0) {
return 0;
}
return this.maxRows;
}
}
@Override
public boolean getMoreResults() throws SQLException {
return getMoreResults(CLOSE_CURRENT_RESULT);
}
@Override
public boolean getMoreResults(int current) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.results == null) {
return false;
}
boolean streamingMode = createStreamingResultSet();
if (streamingMode) {
if (this.results.hasRows()) {
while (this.results.next()) {
// need to drain remaining rows to get to server status which tells us whether more results actually exist or not
}
}
}
ResultSetInternalMethods nextResultSet = (ResultSetInternalMethods) this.results.getNextResultset();
switch (current) {
case java.sql.Statement.CLOSE_CURRENT_RESULT:
if (this.results != null) {
if (!(streamingMode || this.dontTrackOpenResources.getValue())) {
this.results.realClose(false);
}
this.results.clearNextResultset();
}
break;
case java.sql.Statement.CLOSE_ALL_RESULTS:
if (this.results != null) {
if (!(streamingMode || this.dontTrackOpenResources.getValue())) {
this.results.realClose(false);
}
this.results.clearNextResultset();
}
closeAllOpenResults();
break;
case java.sql.Statement.KEEP_CURRENT_RESULT:
if (!this.dontTrackOpenResources.getValue()) {
this.openResults.add(this.results);
}
this.results.clearNextResultset(); // nobody besides us should
// ever need this value...
break;
default:
throw SQLError.createSQLException(Messages.getString("Statement.19"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
this.results = nextResultSet;
if (this.results == null) {
this.updateCount = -1;
this.lastInsertId = -1;
} else if (this.results.hasRows()) {
this.updateCount = -1;
this.lastInsertId = -1;
} else {
this.updateCount = this.results.getUpdateCount();
this.lastInsertId = this.results.getUpdateID();
}
boolean moreResults = (this.results != null) && this.results.hasRows();
if (!moreResults) {
checkAndPerformCloseOnCompletionAction();
}
return moreResults;
}
}
@Override
public int getQueryTimeout() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return getTimeoutInMillis() / 1000;
}
}
/**
* Parses actual record count from 'info' message
*
* @param serverInfo
* server info message
* @return records count
*/
private long getRecordCountFromInfo(String serverInfo) {
StringBuilder recordsBuf = new StringBuilder();
long recordsCount = 0;
long duplicatesCount = 0;
char c = (char) 0;
int length = serverInfo.length();
int i = 0;
for (; i < length; i++) {
c = serverInfo.charAt(i);
if (Character.isDigit(c)) {
break;
}
}
recordsBuf.append(c);
i++;
for (; i < length; i++) {
c = serverInfo.charAt(i);
if (!Character.isDigit(c)) {
break;
}
recordsBuf.append(c);
}
recordsCount = Long.parseLong(recordsBuf.toString());
StringBuilder duplicatesBuf = new StringBuilder();
for (; i < length; i++) {
c = serverInfo.charAt(i);
if (Character.isDigit(c)) {
break;
}
}
duplicatesBuf.append(c);
i++;
for (; i < length; i++) {
c = serverInfo.charAt(i);
if (!Character.isDigit(c)) {
break;
}
duplicatesBuf.append(c);
}
duplicatesCount = Long.parseLong(duplicatesBuf.toString());
return recordsCount - duplicatesCount;
}
@Override
public java.sql.ResultSet getResultSet() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return ((this.results != null) && this.results.hasRows()) ? (java.sql.ResultSet) this.results : null;
}
}
@Override
public int getResultSetConcurrency() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.resultSetConcurrency;
}
}
@Override
public int getResultSetHoldability() throws SQLException {
return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
}
protected ResultSetInternalMethods getResultSetInternal() {
try {
synchronized (checkClosed().getConnectionMutex()) {
return this.results;
}
} catch (StatementIsClosedException e) {
return this.results; // you end up with the same thing as before, you'll get exception when actually trying to use it
}
}
@Override
public int getResultSetType() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.query.getResultType().getIntValue();
}
}
@Override
public int getUpdateCount() throws SQLException {
return Util.truncateAndConvertToInt(getLargeUpdateCount());
}
@Override
public java.sql.SQLWarning getWarnings() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (isClearWarningsCalled()) {
return null;
}
SQLWarning pendingWarningsFromServer = this.session.getProtocol().convertShowWarningsToSQLWarnings(0, false);
if (this.warningChain != null) {
this.warningChain.setNextWarning(pendingWarningsFromServer);
} else {
this.warningChain = pendingWarningsFromServer;
}
return this.warningChain;
}
}
/**
* Closes this statement, and frees resources.
*
* @param calledExplicitly
* was this called from close()?
* @param closeOpenResults
* should open result sets be closed?
*
* @throws SQLException
* if an error occurs
*/
protected void realClose(boolean calledExplicitly, boolean closeOpenResults) throws SQLException {
JdbcConnection locallyScopedConn = this.connection;
if (locallyScopedConn == null || this.isClosed) {
return; // already closed
}
// do it ASAP to reduce the chance of calling this method concurrently from ConnectionImpl.closeAllOpenStatements()
if (!this.dontTrackOpenResources.getValue()) {
locallyScopedConn.unregisterStatement(this);
}
if (this.useUsageAdvisor) {
if (!calledExplicitly) {
this.session.getProfilerEventHandler().processEvent(ProfilerEvent.TYPE_USAGE, this.session, this, null, 0, new Throwable(),
Messages.getString("Statement.63"));
}
}
if (closeOpenResults) {
closeOpenResults = !(this.holdResultsOpenOverClose || this.dontTrackOpenResources.getValue());
}
if (closeOpenResults) {
if (this.results != null) {
try {
this.results.close();
} catch (Exception ex) {
}
}
if (this.generatedKeysResults != null) {
try {
this.generatedKeysResults.close();
} catch (Exception ex) {
}
}
closeAllOpenResults();
}
this.isClosed = true;
closeQuery();
this.results = null;
this.generatedKeysResults = null;
this.connection = null;
this.session = null;
this.warningChain = null;
this.openResults = null;
this.batchedGeneratedKeys = null;
this.pingTarget = null;
this.resultSetFactory = null;
}
@Override
public void setCursorName(String name) throws SQLException {
// No-op
}
@Override
public void setEscapeProcessing(boolean enable) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
this.doEscapeProcessing = enable;
}
}
@Override
public void setFetchDirection(int direction) throws SQLException {
switch (direction) {
case java.sql.ResultSet.FETCH_FORWARD:
case java.sql.ResultSet.FETCH_REVERSE:
case java.sql.ResultSet.FETCH_UNKNOWN:
break;
default:
throw SQLError.createSQLException(Messages.getString("Statement.5"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
}
@Override
public void setFetchSize(int rows) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (((rows < 0) && (rows != Integer.MIN_VALUE)) || ((this.maxRows > 0) && (rows > this.getMaxRows()))) {
throw SQLError.createSQLException(Messages.getString("Statement.7"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
this.query.setResultFetchSize(rows);
}
}
@Override
public void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) {
try {
synchronized (checkClosed().getConnectionMutex()) {
this.holdResultsOpenOverClose = holdResultsOpenOverClose;
}
} catch (StatementIsClosedException e) {
// FIXME: can't break interface at this point
}
}
@Override
public void setMaxFieldSize(int max) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (max < 0) {
throw SQLError.createSQLException(Messages.getString("Statement.11"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
int maxBuf = this.maxAllowedPacket.getValue();
if (max > maxBuf) {
throw SQLError.createSQLException(Messages.getString("Statement.13", new Object[] { Long.valueOf(maxBuf) }),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
this.maxFieldSize = max;
}
}
@Override
public void setMaxRows(int max) throws SQLException {
setLargeMaxRows(max);
}
@Override
public void setQueryTimeout(int seconds) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (seconds < 0) {
throw SQLError.createSQLException(Messages.getString("Statement.21"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
setTimeoutInMillis(seconds * 1000);
}
}
/**
* Sets the concurrency for result sets generated by this statement
*
* @param concurrencyFlag
* concurrency flag
* @throws SQLException
* if a database access error occurs or this method is called on a closed PreparedStatement
*/
void setResultSetConcurrency(int concurrencyFlag) throws SQLException {
try {
synchronized (checkClosed().getConnectionMutex()) {
this.resultSetConcurrency = concurrencyFlag;
// updating resultset factory because concurrency is cached there
this.resultSetFactory = new ResultSetFactory(this.connection, this);
}
} catch (StatementIsClosedException e) {
// FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement...
}
}
/**
* Sets the result set type for result sets generated by this statement
*
* @param typeFlag
* {@link com.mysql.cj.protocol.Resultset.Type}
* @throws SQLException
* if a database access error occurs or this method is called on a closed PreparedStatement
*/
void setResultSetType(Resultset.Type typeFlag) throws SQLException {
try {
synchronized (checkClosed().getConnectionMutex()) {
this.query.setResultType(typeFlag);
// updating resultset factory because type is cached there
this.resultSetFactory = new ResultSetFactory(this.connection, this);
}
} catch (StatementIsClosedException e) {
// FIXME: Can't break interface atm, we'll get the exception later when you try and do something useful with a closed statement...
}
}
void setResultSetType(int typeFlag) throws SQLException {
this.query.setResultType(Type.fromValue(typeFlag, Type.FORWARD_ONLY));
}
protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.retrieveGeneratedKeys) {
java.sql.ResultSet rs = null;
try {
rs = batchedStatement.getGeneratedKeys();
while (rs.next()) {
this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
}
} finally {
if (rs != null) {
rs.close();
}
}
}
}
}
protected void getBatchedGeneratedKeys(int maxKeys) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.retrieveGeneratedKeys) {
java.sql.ResultSet rs = null;
try {
rs = maxKeys == 0 ? getGeneratedKeysInternal() : getGeneratedKeysInternal(maxKeys);
while (rs.next()) {
this.batchedGeneratedKeys.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
}
} finally {
this.isImplicitlyClosingResults = true;
try {
if (rs != null) {
rs.close();
}
} finally {
this.isImplicitlyClosingResults = false;
}
}
}
}
}
private boolean useServerFetch() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.session.getPropertySet().getBooleanProperty(PropertyKey.useCursorFetch).getValue() && this.query.getResultFetchSize() > 0
&& this.query.getResultType() == Type.FORWARD_ONLY;
}
}
@Override
public boolean isClosed() throws SQLException {
JdbcConnection locallyScopedConn = this.connection;
if (locallyScopedConn == null) {
return true;
}
synchronized (locallyScopedConn.getConnectionMutex()) {
return this.isClosed;
}
}
private boolean isPoolable = false;
@Override
public boolean isPoolable() throws SQLException {
checkClosed();
return this.isPoolable;
}
@Override
public void setPoolable(boolean poolable) throws SQLException {
checkClosed();
this.isPoolable = poolable;
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
checkClosed();
// This works for classes that aren't actually wrapping anything
return iface.isInstance(this);
}
@Override
public T unwrap(Class iface) throws SQLException {
try {
// This works for classes that aren't actually wrapping anything
return iface.cast(this);
} catch (ClassCastException cce) {
throw SQLError.createSQLException(Messages.getString("Common.UnableToUnwrap", new Object[] { iface.toString() }),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
}
protected static int findStartOfStatement(String sql) {
int statementStartPos = 0;
if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) {
statementStartPos = sql.indexOf("*/");
if (statementStartPos == -1) {
statementStartPos = 0;
} else {
statementStartPos += 2;
}
} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--") || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) {
statementStartPos = sql.indexOf('\n');
if (statementStartPos == -1) {
statementStartPos = sql.indexOf('\r');
if (statementStartPos == -1) {
statementStartPos = 0;
}
}
}
return statementStartPos;
}
@Override
public InputStream getLocalInfileInputStream() {
return this.session.getLocalInfileInputStream();
}
@Override
public void setLocalInfileInputStream(InputStream stream) {
this.session.setLocalInfileInputStream(stream);
}
@Override
public void setPingTarget(PingTarget pingTarget) {
this.pingTarget = pingTarget;
}
@Override
public ExceptionInterceptor getExceptionInterceptor() {
return this.exceptionInterceptor;
}
protected boolean containsOnDuplicateKeyInString(String sql) {
return ParseInfo.getOnDuplicateKeyLocation(sql, this.dontCheckOnDuplicateKeyUpdateInSQL, this.rewriteBatchedStatements.getValue(),
this.session.getServerSession().isNoBackslashEscapesSet()) != -1;
}
private boolean closeOnCompletion = false;
@Override
public void closeOnCompletion() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
this.closeOnCompletion = true;
}
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
return this.closeOnCompletion;
}
}
@Override
public long[] executeLargeBatch() throws SQLException {
return executeBatchInternal();
}
@Override
public long executeLargeUpdate(String sql) throws SQLException {
return executeUpdateInternal(sql, false, false);
}
@Override
public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return executeUpdateInternal(sql, false, autoGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS);
}
@Override
public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
return executeUpdateInternal(sql, false, columnIndexes != null && columnIndexes.length > 0);
}
@Override
public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
return executeUpdateInternal(sql, false, columnNames != null && columnNames.length > 0);
}
@Override
public long getLargeMaxRows() throws SQLException {
// Max rows is limited by MySQLDefs.MAX_ROWS anyway...
return getMaxRows();
}
@Override
public long getLargeUpdateCount() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (this.results == null) {
return -1;
}
if (this.results.hasRows()) {
return -1;
}
return this.results.getUpdateCount();
}
}
@Override
public void setLargeMaxRows(long max) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if ((max > MAX_ROWS) || (max < 0)) {
throw SQLError.createSQLException(Messages.getString("Statement.15") + max + " > " + MAX_ROWS + ".",
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
}
if (max == 0) {
max = -1;
}
this.maxRows = (int) max;
}
}
@Override
public String getCurrentDatabase() {
return this.query.getCurrentDatabase();
}
public long getServerStatementId() {
throw ExceptionFactory.createException(CJOperationNotSupportedException.class, Messages.getString("Statement.65"));
}
@Override
@SuppressWarnings("unchecked")
public ProtocolEntityFactory getResultSetFactory() {
return (ProtocolEntityFactory) this.resultSetFactory;
}
@Override
public int getId() {
return this.query.getId();
}
@Override
public void setCancelStatus(CancelStatus cs) {
this.query.setCancelStatus(cs);
}
@Override
public void checkCancelTimeout() {
this.query.checkCancelTimeout();
}
@Override
public Session getSession() {
return this.session;
}
@Override
public Object getCancelTimeoutMutex() {
return this.query.getCancelTimeoutMutex();
}
@Override
public void closeQuery() {
if (this.query != null) {
this.query.closeQuery();
}
}
@Override
public int getResultFetchSize() {
return this.query.getResultFetchSize();
}
@Override
public void setResultFetchSize(int fetchSize) {
this.query.setResultFetchSize(fetchSize);
}
@Override
public Resultset.Type getResultType() {
return this.query.getResultType();
}
@Override
public void setResultType(Resultset.Type resultSetType) {
this.query.setResultType(resultSetType);
}
@Override
public int getTimeoutInMillis() {
return this.query.getTimeoutInMillis();
}
@Override
public void setTimeoutInMillis(int timeoutInMillis) {
this.query.setTimeoutInMillis(timeoutInMillis);
}
@Override
public long getExecuteTime() {
return this.query.getExecuteTime();
}
@Override
public void setExecuteTime(long executeTime) {
this.query.setExecuteTime(executeTime);
}
@Override
public AtomicBoolean getStatementExecuting() {
return this.query.getStatementExecuting();
}
@Override
public void setCurrentDatabase(String currentDb) {
this.query.setCurrentDatabase(currentDb);
}
@Override
public boolean isClearWarningsCalled() {
return this.query.isClearWarningsCalled();
}
@Override
public void setClearWarningsCalled(boolean clearWarningsCalled) {
this.query.setClearWarningsCalled(clearWarningsCalled);
}
@Override
public Query getQuery() {
return this.query;
}
}