/*
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
*
* 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<
ResultSetInternalMethods>
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<
Row>
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<
Boolean>
dontTrackOpenResources;
protected
RuntimeProperty<
Boolean>
dumpQueriesOnException;
protected boolean
logSlowQueries = false;
protected
RuntimeProperty<
Boolean>
rewriteBatchedStatements;
protected
RuntimeProperty<
Integer>
maxAllowedPacket;
protected boolean
dontCheckOnDuplicateKeyUpdateInSQL;
protected
RuntimeProperty<
Boolean>
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<
Object>
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.
*
* <p>
* <B>Note:</B> A Statement is automatically closed when it is garbage collected. When a Statement is closed, its current ResultSet, if one exists, is also
* closed.
* </p>
*
* @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().
getDefaultTimeZone(),
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<
Object>
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().
getDefaultTimeZone(),
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<
Row>
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()).<
ResultSetImpl>
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().
getDefaultTimeZone(),
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<
Row>
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.
*
* <p>
* 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()".
* </p>
*
* @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.
*
* <p>
* This method returns longs as MySQL server returns 64-bit values for update counts
* </p>
*
* @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 {
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> T
unwrap(
Class<T>
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 <T extends
Resultset, M extends
Message>
ProtocolEntityFactory<T, M>
getResultSetFactory() {
return (
ProtocolEntityFactory<T, M>) 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
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;
}
}