org.mariadb.jdbc.MariaDbStatement Maven / Gradle / Ivy
Show all versions of mariadb-java-client-jre7 Show documentation
/*
*
* MariaDB Client for Java
*
* Copyright (c) 2012-2014 Monty Program Ab.
* Copyright (c) 2015-2017 MariaDB Ab.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to Monty Program Ab [email protected].
*
* This particular MariaDB Client for Java file is work
* derived from a Drizzle-JDBC. Drizzle-JDBC file which is covered by subject to
* the following copyright and notice provisions:
*
* Copyright (c) 2009-2011, Marcus Eriksson
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* Neither the name of the driver nor the names of its contributors may not be
* used to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
*/
package org.mariadb.jdbc;
import org.mariadb.jdbc.internal.com.read.dao.CmdInformation;
import org.mariadb.jdbc.internal.com.read.dao.Results;
import org.mariadb.jdbc.internal.com.read.resultset.SelectResultSet;
import org.mariadb.jdbc.internal.logging.Logger;
import org.mariadb.jdbc.internal.logging.LoggerFactory;
import org.mariadb.jdbc.internal.protocol.Protocol;
import org.mariadb.jdbc.internal.util.Options;
import org.mariadb.jdbc.internal.util.Utils;
import org.mariadb.jdbc.internal.util.dao.ClientPrepareResult;
import org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper;
import org.mariadb.jdbc.internal.util.scheduler.SchedulerServiceProviderHolder;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class MariaDbStatement implements Statement, Cloneable {
//timeout scheduler
private static final ScheduledExecutorService timeoutScheduler = SchedulerServiceProviderHolder.getTimeoutScheduler();
protected static Logger logger = LoggerFactory.getLogger(MariaDbStatement.class);
protected final ReentrantLock lock;
/**
* the protocol used to talk to the server.
*/
protected Protocol protocol;
/**
* the Connection object.
*/
protected MariaDbConnection connection;
protected Future> timerTaskFuture;
protected volatile boolean closed = false;
protected List batchQueries;
protected int queryTimeout;
protected long maxRows;
protected Results results;
protected int resultSetScrollType;
protected boolean mustCloseOnCompletion = false;
protected Options options;
//are warnings cleared?
protected boolean warningsCleared;
protected int fetchSize;
boolean isTimedout;
volatile boolean executing;
protected boolean canUseServerTimeout;
private int maxFieldSize;
/**
* Creates a new Statement.
*
* @param connection the connection to return in getConnection.
* @param resultSetScrollType one of the following ResultSet
constants: ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or ResultSet.TYPE_SCROLL_SENSITIVE
*/
public MariaDbStatement(MariaDbConnection connection, int resultSetScrollType) {
this.protocol = connection.getProtocol();
this.connection = connection;
this.canUseServerTimeout = connection.canUseServerTimeout();
this.resultSetScrollType = resultSetScrollType;
this.lock = this.connection.lock;
this.options = this.protocol.getOptions();
this.results = new Results(this, protocol.getAutoIncrementIncrement());
}
/**
* Provide a "cleanup" method that can be called after unloading driver, to fix Tomcat's obscure classpath handling.
*/
public static void unloadDriver() {
// nothing to do here, as scheduler is already daemon thread
}
/**
* Clone statement.
*
* @param connection connection
* @return Clone statement.
* @throws CloneNotSupportedException if any error occur.
*/
public MariaDbStatement clone(MariaDbConnection connection) throws CloneNotSupportedException {
MariaDbStatement clone = (MariaDbStatement) super.clone();
clone.connection = connection;
clone.protocol = connection.getProtocol();
clone.timerTaskFuture = null;
clone.batchQueries = new ArrayList<>();
clone.results = new Results(clone, clone.protocol.getAutoIncrementIncrement());
clone.closed = false;
clone.warningsCleared = true;
clone.fetchSize = 0;
clone.maxRows = 0;
return clone;
}
/**
* returns the protocol.
*
* @return the protocol used.
*/
public Protocol getProtocol() {
return protocol;
}
// Part of query prolog - setup timeout timer
protected void setTimerTask() {
assert (timerTaskFuture == null);
timerTaskFuture = timeoutScheduler.schedule(new Runnable() {
@Override
public void run() {
try {
isTimedout = true;
protocol.cancelCurrentQuery();
} catch (Throwable e) {
}
}
}, queryTimeout, TimeUnit.SECONDS);
}
/**
* Command prolog.
*
* - clear previous query state
* - launch timeout timer if needed
*
*
* @param forceUseOfTimer even if query timeout if possible on server using max_statement_time, force using timer
* (for batch)
* @throws SQLException if statement is closed
*/
protected void executeQueryPrologue(boolean forceUseOfTimer) throws SQLException {
executing = true;
if (closed) {
throw new SQLException("execute() is called on closed statement");
}
protocol.prolog(maxRows, protocol.getProxy() != null, connection, this);
if (queryTimeout != 0 && (!canUseServerTimeout || forceUseOfTimer)) {
setTimerTask();
}
}
protected void stopTimeoutTask() {
if (timerTaskFuture != null) {
if (!timerTaskFuture.cancel(true)) {
// could not cancel, task either started or already finished
// we must now wait for task to finish to ensure state modifications are done
try {
timerTaskFuture.get();
} catch (InterruptedException e) {
// reset interrupt status
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
// ignore error, likely due to interrupting during cancel
}
// we don't catch the exception if already canceled, that would indicate we tried
// to cancel in parallel (which this code currently is not designed for)
}
timerTaskFuture = null;
}
}
/**
* Reset timeout after query, re-throw SQL exception
*
* @param sqle current exception
* @return SQLException exception with new message in case of timer timeout.
*/
protected SQLException executeExceptionEpilogue(SQLException sqle) {
//if has a failover, closing the statement
if (sqle.getSQLState() != null && sqle.getSQLState().startsWith("08")) {
try {
close();
} catch (SQLException sqlee) {
//eat exception
}
}
if (isTimedout) {
return new SQLTimeoutException("(conn:" + getServerThreadId() + ") Query timed out", "JZ0002", 1317, sqle);
}
SQLException sqlException = ExceptionMapper.getException(sqle, connection, this, queryTimeout != 0);
logger.error("error executing query", sqlException);
return sqlException;
}
protected void executeEpilogue() {
stopTimeoutTask();
isTimedout = false;
executing = false;
}
protected void executeBatchEpilogue() {
executing = false;
stopTimeoutTask();
isTimedout = false;
clearBatch();
}
protected SQLException handleFailoverAndTimeout(SQLException sqle) {
//if has a failover, closing the statement
if (sqle.getSQLState() != null && sqle.getSQLState().startsWith("08")) {
try {
close();
} catch (SQLException sqlee) {
//eat exception
}
}
if (isTimedout) {
sqle = new SQLTimeoutException("(conn:" + getServerThreadId() + ") Query timed out", "JZ0002", 1317, sqle);
}
return sqle;
}
protected BatchUpdateException executeBatchExceptionEpilogue(SQLException sqle, CmdInformation cmdInformation, int size, boolean rewritten) {
sqle = handleFailoverAndTimeout(sqle);
int[] ret;
if (cmdInformation == null) {
ret = new int[size];
Arrays.fill(ret, Statement.EXECUTE_FAILED);
} else if (rewritten) {
ret = cmdInformation.getRewriteUpdateCounts();
} else {
ret = cmdInformation.getUpdateCounts();
}
sqle = ExceptionMapper.getException(sqle, connection, this, queryTimeout != 0);
logger.error("error executing query", sqle);
return new BatchUpdateException(sqle.getMessage(), sqle.getSQLState(), sqle.getErrorCode(), ret, sqle);
}
/**
* executes a query.
*
* @param sql the query
* @param fetchSize fetch size
* @return true if there was a result set, false otherwise.
* @throws SQLException the error description
*/
protected boolean executeInternal(String sql, int fetchSize) throws SQLException {
lock.lock();
try {
executeQueryPrologue(false);
results.reset(fetchSize, false, 1, false, resultSetScrollType);
protocol.executeQuery(protocol.isMasterConnection(), results,
getTimeoutSql(Utils.nativeSql(sql, connection.noBackslashEscapes)));
results.commandEnd();
return results.getResultSet() != null;
} catch (SQLException exception) {
throw executeExceptionEpilogue(exception);
} finally {
executeEpilogue();
lock.unlock();
}
}
private String getTimeoutSql(String sql) {
if (queryTimeout != 0 && canUseServerTimeout) {
return "SET STATEMENT max_statement_time=" + queryTimeout + " FOR " + sql;
}
return sql;
}
/**
* ! This method is for test only !
* This permit sending query using specific charset.
*
* @param sql sql
* @param charset charset
* @return boolean if execution went well
* @throws SQLException if any exception occur
*/
public boolean testExecute(String sql, Charset charset) throws SQLException {
lock.lock();
try {
executeQueryPrologue(false);
results.reset(fetchSize, false, 1, false, resultSetScrollType);
protocol.executeQuery(protocol.isMasterConnection(), results,
getTimeoutSql(Utils.nativeSql(sql, connection.noBackslashEscapes)), charset);
results.commandEnd();
return results.getResultSet() != null;
} catch (SQLException exception) {
throw executeExceptionEpilogue(exception);
} finally {
executeEpilogue();
lock.unlock();
}
}
/**
* executes a query.
*
* @param sql the query
* @return true if there was a result set, false otherwise.
* @throws SQLException if the query could not be sent to server
*/
public boolean execute(String sql) throws SQLException {
return executeInternal(sql, fetchSize);
}
/**
* Executes the given SQL statement, which may return multiple results, and signals the driver that any
* auto-generated keys should be made available for retrieval. The driver will ignore this signal if the SQL
* statement is not an INSERT
statement, or an SQL statement able to return auto-generated keys
* (the list of such statements is vendor-specific).
* In some (uncommon) situations, a single SQL statement may return multiple result sets and/or update counts.
* Normally you can ignore this unless you are (1) executing a stored procedure
* that you know may return multiple results or (2) you are dynamically executing an unknown SQL string.
* The execute
method
* executes an SQL statement and indicates the form of the first result. You must then use the methods
* getResultSet
or
* getUpdateCount
to retrieve the result, and getInternalMoreResults
to move to any
* subsequent result(s).
*
* @param sql any SQL statement
* @param autoGeneratedKeys a constant indicating whether auto-generated keys should be made available for retrieval
* using the methodgetGeneratedKeys
; one of the following constants:
* Statement.RETURN_GENERATED_KEYS
or
* Statement.NO_GENERATED_KEYS
* @return true
if the first result is a ResultSet
object; false
if it is an
* update count or there are no results
* @throws SQLException if a database access error occurs, this method is called on a closed
* Statement
or the second parameter supplied to this method is
* not Statement.RETURN_GENERATED_KEYS
or
* Statement.NO_GENERATED_KEYS
.
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method with a constant of
* Statement.RETURN_GENERATED_KEYS
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
* @see #getGeneratedKeys
*/
public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
return executeInternal(sql, fetchSize);
}
/**
* Executes the given SQL statement, which may return multiple results, and signals the driver that the
* auto-generated keys indicated in the given array should be made available for retrieval. This array contains
* the indexes of the columns in the target table that contain the auto-generated keys that should be made
* available. The driver will ignore the array if the SQL statement is not an INSERT
* statement, or an SQL statement able to return auto-generated keys (the list of such statements is
* vendor-specific).
* Under some (uncommon) situations, a single SQL statement may return multiple result sets and/or update counts.
* Normally you can ignore this unless you are
* (1) executing a stored procedure that you know may return multiple results or
* (2) you are dynamically executing an unknown SQL string.
* The execute
method executes an SQL statement and indicates the form of the first result.
* You must then use the methods getResultSet
or getUpdateCount
to retrieve the result,
* and getInternalMoreResults
to move to any subsequent result(s).
*
* @param sql any SQL statement
* @param columnIndexes an array of the indexes of the columns in the inserted row that should be made available
* for retrieval by a call to the
* method getGeneratedKeys
* @return true
if the first result is a ResultSet
object; false
if it is an
* update count or there are no results
* @throws SQLException if a database access error occurs, this method is called on a closed
* Statement
or the elements in
* the int
array passed to this method are not valid column
* indexes
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
* @since 1.4
*/
public boolean execute(final String sql, final int[] columnIndexes) throws SQLException {
return executeInternal(sql, fetchSize);
}
/**
* Executes the given SQL statement, which may return multiple results, and signals the driver that the
* auto-generated keys indicated in the given array should be made available for retrieval. This array contains
* the names of the columns in the target table that contain the auto-generated keys that should be made available.
* The driver will ignore the array if the SQL statement is not an INSERT
* statement, or an SQL statement able to return auto-generated keys (the list of such statements is
* vendor-specific).
* In some (uncommon) situations, a single SQL statement may return multiple result sets and/or update counts.
* Normally you can ignore this unless you are (1) executing a stored procedure that you know may return multiple
* results or (2) you are dynamically executing an unknown SQL string.
* The execute
method executes an SQL statement and indicates the form of the first result.
* You must then use the methods getResultSet
or getUpdateCount
to retrieve the result,
* and getInternalMoreResults
to move to any subsequent result(s).
*
* @param sql any SQL statement
* @param columnNames an array of the names of the columns in the inserted row that should be made available
* for retrieval by a call to the method getGeneratedKeys
* @return true
if the next result is a ResultSet
object; false
if it is an
* update count or there are no more results
* @throws SQLException if a database access error occurs, this method is called on a closed
* Statement
or the elements of
* the String
array passed to this method are not valid column
* names
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
* @see #getGeneratedKeys
* @since 1.4
*/
public boolean execute(final String sql, final String[] columnNames) throws SQLException {
return executeInternal(sql, fetchSize);
}
/**
* executes a select query.
*
* @param sql the query to send to the server
* @return a result set
* @throws SQLException if something went wrong
*/
public ResultSet executeQuery(String sql) throws SQLException {
if (executeInternal(sql, fetchSize)) {
return results.getResultSet();
}
//throw new SQLException("executeQuery() with query '" + query +"' did not return a result set");
return SelectResultSet.createEmptyResultSet();
}
/**
* Executes an update.
*
* @param sql the update query.
* @return update count
* @throws SQLException if the query could not be sent to server.
*/
public int executeUpdate(String sql) throws SQLException {
if (executeInternal(sql, fetchSize)) {
return 0;
}
return getUpdateCount();
}
/**
* Executes the given SQL statement and signals the driver with the given flag about whether the auto-generated keys
* produced by this Statement
object should be made available for retrieval. The driver will ignore
* the flag if the SQL statement is not an
* INSERT
statement, or an SQL statement able to return auto-generated keys (the list of such
* statements is vendor-specific).
*
* @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT
,
* UPDATE
or DELETE
; or an
* SQL statement that returns nothing, such as a DDL statement.
* @param autoGeneratedKeys a flag indicating whether auto-generated keys should be made available for retrieval;
* one of the following constants:
* Statement.RETURN_GENERATED_KEYS
Statement.NO_GENERATED_KEYS
* @return either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements
* that return nothing
* @throws SQLException if a database access error occurs, this method is called on a closed
* Statement
, the given SQL
* statement returns a ResultSet
object, or the given constant
* is not one of those allowed
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method with a constant of
* Statement.RETURN_GENERATED_KEYS
* @since 1.4
*/
public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
if (executeInternal(sql, fetchSize)) {
return 0;
}
return getUpdateCount();
}
/**
* Executes the given SQL statement and signals the driver that the auto-generated keys indicated in the given array
* should be made available for retrieval. This array contains the indexes of the columns in the target table that
* contain the auto-generated keys that should be made available. The driver will ignore the array if the SQL
* statement is not an INSERT
statement, or an SQL statement able to return auto-generated keys (the
* list of such statements is vendor-specific).
*
* @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT
,
* UPDATE
or DELETE
; or an SQL statement that returns nothing, such
* as a DDL statement.
* @param columnIndexes an array of column indexes indicating the columns that should be returned from the
* inserted row
* @return either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements
* that return nothing
* @throws SQLException if a database access error occurs, this method is called on a closed
* Statement
, the SQL statement returns a ResultSet
* object, or the second argument supplied to this method is not an int
* array whose elements are valid column indexes
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @since 1.4
*/
public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
return executeUpdate(sql);
}
/**
* Executes the given SQL statement and signals the driver that the auto-generated keys indicated in the given
* array should be made available for retrieval. This array contains the names of the columns in the target table
* that contain the auto-generated keys that should be made available. The driver will ignore the array if the SQL
* statement is not an INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT
,
* UPDATE
or DELETE
; or an
* SQL statement that returns nothing, such as a DDL statement.
* @param columnNames an array of the names of the columns that should be returned from the inserted row
* @return either the row count for INSERT
, UPDATE
, or DELETE
statements,
* or 0 for SQL statements that return nothing
* @throws SQLException if a database access error occurs, this method is called on a closed
* Statement
, the SQL statement returns a ResultSet
* object, or the second argument supplied to this method is not a String
* array whose elements are valid column names
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @since 1.4
*/
public int executeUpdate(final String sql, final String[] columnNames) throws SQLException {
return executeUpdate(sql);
}
/**
* Executes the given SQL statement, which may be an INSERT, UPDATE, or DELETE statement or an SQL statement that returns nothing,
* such as an SQL DDL statement.
* This method should be used when the returned row count may exceed Integer.MAX_VALUE.
*
* @param sql sql command
* @return update counts
* @throws SQLException if any error occur during execution
*/
public long executeLargeUpdate(String sql) throws SQLException {
if (executeInternal(sql, fetchSize)) {
return 0;
}
return getLargeUpdateCount();
}
/**
* Identical to executeLargeUpdate(String sql), with a flag that indicate that autoGeneratedKeys (primary key fields with "auto_increment")
* generated id's must be retrieved.
*
* Those id's will be available using getGeneratedKeys() method.
*
* @param sql sql command
* @param autoGeneratedKeys a flag indicating whether auto-generated keys should be made available for retrieval;
* one of the following constants:
* Statement.RETURN_GENERATED_KEYS
* Statement.NO_GENERATED_KEYS
* @return update counts
* @throws SQLException if any error occur during execution
*/
public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
//driver always get generated keys.
return executeLargeUpdate(sql);
}
/**
* Identical to executeLargeUpdate(String sql, int autoGeneratedKeys) with autoGeneratedKeys = Statement.RETURN_GENERATED_KEYS set.
*
* @param sql sql command
* @param columnIndexes column Indexes
* @return update counts
* @throws SQLException if any error occur during execution
*/
public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
//driver always get generated keys. no need for columnIndexes indication
return executeLargeUpdate(sql);
}
/**
* Identical to executeLargeUpdate(String sql, int autoGeneratedKeys) with autoGeneratedKeys = Statement.RETURN_GENERATED_KEYS set.
*
* @param sql sql command
* @param columnNames columns names
* @return update counts
* @throws SQLException if any error occur during execution
*/
public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
//driver always get generated keys. no need for columnNames indication
return executeLargeUpdate(sql);
}
/**
* Releases this Statement
object's database and JDBC resources immediately instead of waiting for this to happen when it is
* automatically closed. It is generally good practice to release resources as soon as you are finished with them to avoid tying up database
* resources. Calling the method close
on a Statement
object that is already closed has no effect. Note:When a
* Statement
object is closed, its current ResultSet
object, if one exists, is also closed.
*
* @throws SQLException if a database access error occurs
*/
public void close() throws SQLException {
lock.lock();
try {
closed = true;
if (results.getFetchSize() != 0) {
if (options.killFetchStmtOnClose) {
try {
protocol.cancelCurrentQuery();
skipMoreResults();
} catch (SQLException sqle) {
//eat exception
} catch (IOException sqle) {
//eat exception
}
} else skipMoreResults();
}
results.close();
protocol = null;
if (connection == null || connection.pooledConnection == null
|| connection.pooledConnection.statementEventListeners.isEmpty()) {
return;
}
connection.pooledConnection.fireStatementClosed(this);
} finally {
lock.unlock();
}
}
/**
* Retrieves the maximum number of bytes that can be returned for character and binary column values in a ResultSet
object produced
* by this Statement
object.
* This limit applies only to
* BINARY
,
* VARBINARY
,
* LONGVARBINARY
,
* CHAR
,
* VARCHAR
,
* NCHAR
,
* NVARCHAR
,
* LONGNVARCHAR
and
* LONGVARCHAR
* columns. If the limit is exceeded, the excess data is silently discarded.
*
* @return the current column size limit for columns storing character and binary values; zero means there is no limit
* @see #setMaxFieldSize
*/
public int getMaxFieldSize() {
return maxFieldSize;
}
/**
* Sets the limit for the maximum number of bytes that can be returned for character and binary column values in a ResultSet
object
* produced by this Statement
object. This limit applies only to BINARY
, VARBINARY
,
* LONGVARBINARY
, CHAR
, VARCHAR
, NCHAR
, NVARCHAR
, LONGNVARCHAR
and
* LONGVARCHAR
fields. If the limit is exceeded, the excess data is silently discarded. For maximum portability, use values greater
* than 256.
*
* @param max the new column size limit in bytes; zero means there is no limit
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
or the condition max
* >= 0 is not satisfied
* @see #getMaxFieldSize
*/
public void setMaxFieldSize(final int max) throws SQLException {
maxFieldSize = max;
}
/**
* Retrieves the maximum number of rows that a ResultSet
object produced by this Statement
object can contain. If this
* limit is exceeded, the excess rows are silently dropped.
*
* @return the current maximum number of rows for a ResultSet
object produced by this Statement
object; zero means there
* is no limit
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @see #setMaxRows
*/
public int getMaxRows() throws SQLException {
return (int) maxRows;
}
/**
* Sets the limit for the maximum number of rows that any ResultSet
object generated by this Statement
object can
* contain to the given number. If the limit is exceeded, the excess rows are silently dropped.
*
* @param max the new max rows limit; zero means there is no limit
* @throws SQLException if the condition max >= 0 is not satisfied
* @see #getMaxRows
*/
public void setMaxRows(final int max) throws SQLException {
if (max < 0) {
throw new SQLException("max rows cannot be negative : asked for " + max);
}
maxRows = max;
}
/**
* Retrieves the maximum number of rows that a ResultSet object produced by this Statement object can contain.
* If this limit is exceeded, the excess rows are silently dropped.
*
* @return the current maximum number of rows for a ResultSet object produced by this Statement object; zero means there is no limit
*/
public long getLargeMaxRows() {
return maxRows;
}
/**
* Sets the limit for the maximum number of rows that any ResultSet object generated by this Statement object can contain to the given number.
* If the limit is exceeded, the excess rows are silently dropped.
*
* @param max the new max rows limit; zero means there is no limit
* @throws SQLException if the condition max >= 0 is not satisfied
*/
public void setLargeMaxRows(long max) throws SQLException {
if (max < 0) {
throw new SQLException("max rows cannot be negative : setLargeMaxRows value is " + max);
}
maxRows = max;
}
/**
* Sets escape processing on or off. If escape scanning is on (the default), the driver will do escape substitution before sending the SQL
* statement to the database. Note: Since prepared statements have usually been parsed prior to making this call, disabling escape processing for
* PreparedStatements
objects will have no effect.
*
* @param enable true
to enable escape processing; false
to disable it
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
*/
public void setEscapeProcessing(final boolean enable) throws SQLException {
}
/**
* Retrieves the number of seconds the driver will wait for a Statement
object to execute. If the limit is exceeded, a
* SQLException
is thrown.
*
* @return the current query timeout limit in seconds; zero means there is no limit
* @see #setQueryTimeout
*/
public int getQueryTimeout() {
return queryTimeout;
}
/**
* Sets the number of seconds the driver will wait for a Statement
object to execute to the given number of seconds. If the limit is
* exceeded, an SQLException
is thrown. A JDBC driver must apply this limit to the execute
,
* executeQuery
and executeUpdate
methods.
*
* @param seconds the new query timeout limit in seconds; zero means there is no limit
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
or the condition
* seconds >= 0 is not satisfied
* @see #getQueryTimeout
*/
public void setQueryTimeout(final int seconds) throws SQLException {
if (seconds < 0) {
throw new SQLException("Query timeout rows cannot be negative : asked for " + seconds);
}
this.queryTimeout = seconds;
}
/**
* Sets the inputStream that will be used for the next execute that uses "LOAD DATA LOCAL INFILE". The name specified as local file/URL will be
* ignored.
*
* @param inputStream inputStream instance, that will be used to send data to server
* @throws SQLException if statement is closed
*/
public void setLocalInfileInputStream(InputStream inputStream) throws SQLException {
checkClose();
protocol.setLocalInfileInputStream(inputStream);
}
/**
* Cancels this Statement
object if both the DBMS and driver support aborting an SQL statement. This method can be used by one thread
* to cancel a statement that is being executed by another thread.
*
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
*/
public void cancel() throws SQLException {
checkClose();
try {
if (!executing) return;
protocol.cancelCurrentQuery();
} catch (SQLException e) {
logger.error("error cancelling query", e);
ExceptionMapper.throwException(e, connection, this);
} catch (IOException e) {
// connection gone, query is definitely canceled
}
}
/**
* Retrieves the first warning reported by calls on this Statement
object. Subsequent Statement
object warnings will be
* chained to this SQLWarning
object.
The warning chain is automatically cleared each time a statement is (re)executed. This
* method may not be called on a closed Statement
object; doing so will cause an SQLException
to be thrown.
* Note: If you are processing a ResultSet
object, any warnings associated with reads on that ResultSet
object
* will be chained on it rather than on the Statement
object that produced it.
*
* @return the first SQLWarning
object or null
if there are no warnings
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
*/
public SQLWarning getWarnings() throws SQLException {
checkClose();
if (!warningsCleared) {
return this.connection.getWarnings();
}
return null;
}
/**
* Clears all the warnings reported on this Statement
object. After a call to this method, the method getWarnings
will
* return null
until a new warning is reported for this Statement
object.
*
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
*/
public void clearWarnings() throws SQLException {
warningsCleared = true;
}
/**
* Sets the SQL cursor name to the given String
, which will be used by subsequent Statement
object execute
* methods. This name can then be used in SQL positioned update or delete statements to identify the current row in the ResultSet
* object generated by this statement. If the database does not support positioned update/delete, this method is a noop. To insure that a cursor
* has the proper isolation level to support updates, the cursor's SELECT
statement should have the form SELECT FOR
* UPDATE
. If FOR UPDATE
is not present, positioned updates may fail. Note: By definition, the execution of
* positioned updates and deletes must be done by a different Statement
object than the one that generated the ResultSet
* object being used for positioning. Also, cursor names must be unique within a connection.
*
* @param name the new cursor name, which must be unique within a connection
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
*/
public void setCursorName(final String name) throws SQLException {
throw ExceptionMapper.getFeatureNotSupportedException("Cursors are not supported");
}
/**
* Gets the connection that created this statement.
*
* @return the connection
* @throws SQLException if connection is invalid
*/
public MariaDbConnection getConnection() throws SQLException {
return this.connection;
}
/**
* Retrieves any auto-generated keys created as a result of executing this Statement
object. If this Statement
object
* did not generate any keys, an empty ResultSet
object is returned. Note:If the columns which represent the auto-generated
* keys were not specified, the JDBC driver implementation will determine the columns which best represent the auto-generated keys.
*
* @return a ResultSet
object containing the auto-generated key(s) generated by the execution of this Statement
object
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @since 1.4
*/
public ResultSet getGeneratedKeys() throws SQLException {
if (results != null) {
return results.getGeneratedKeys(protocol);
}
return SelectResultSet.createEmptyResultSet();
}
/**
* Retrieves the result set holdability for ResultSet
objects generated by this Statement
object.
*
* @return either ResultSet.HOLD_CURSORS_OVER_COMMIT
or ResultSet.CLOSE_CURSORS_AT_COMMIT
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @since 1.4
*/
public int getResultSetHoldability() throws SQLException {
return ResultSet.HOLD_CURSORS_OVER_COMMIT;
}
/**
* Retrieves whether this Statement
object has been closed. A Statement
is closed if the method close has been called on
* it, or if it is automatically closed.
*
* @return true if this Statement
object is closed; false if it is still open
* @throws SQLException if a database access error occurs
* @since 1.6
*/
public boolean isClosed() throws SQLException {
return closed;
}
/**
* Returns a value indicating whether the Statement
is poolable or not.
*
* @return true
if the Statement
is poolable; false
otherwise
* @throws SQLException if this method is called on a closed Statement
* @see Statement#setPoolable(boolean) setPoolable(boolean)
* @since 1.6
*/
@Override
public boolean isPoolable() throws SQLException {
return false;
}
/**
* Requests that a Statement
be pooled or not pooled. The value specified is a hint to the statement pool implementation
* indicating whether the applicaiton wants the statement to be pooled. It is up to the statement pool manager as to whether the hint is
* used.
The poolable value of a statement is applicable to both internal statement caches implemented by the driver and external
* statement caches implemented by application servers and other applications.
By default, a Statement
is not poolable when
* created, and a PreparedStatement
and CallableStatement
are poolable when created.
*
* @param poolable requests that the statement be pooled if true and that the statement not be pooled if false
* @throws SQLException if this method is called on a closed Statement
* @since 1.6
*/
@Override
public void setPoolable(final boolean poolable) throws SQLException {
}
/**
* Retrieves the current result as a ResultSet object. This method should be called only once per result.
*
* @return the current result as a ResultSet object or null if the result is an update count or there are no more results
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
*/
public ResultSet getResultSet() throws SQLException {
checkClose();
return results.getResultSet();
}
/**
* Retrieves the current result as an update count; if the result is a ResultSet object or there are no more
* results, -1 is returned. This method should be called only once per result.
*
* @return the current result as an update count; -1 if the current result is a ResultSet object or there are no
* more results
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
*/
public int getUpdateCount() throws SQLException {
if (results != null && results.getCmdInformation() != null && !results.isBatch()) {
return results.getCmdInformation().getUpdateCount();
}
return -1;
}
/**
* Retrieves the current result as an update count; if the result is a ResultSet object or there are no more results, -1 is returned.
*
* @return last update count
*/
public long getLargeUpdateCount() {
if (results.getCmdInformation() != null) {
return results.getCmdInformation().getLargeUpdateCount();
}
return -1;
}
protected void skipMoreResults() throws SQLException {
try {
protocol.skip();
warningsCleared = false;
connection.reenableWarnings();
} catch (SQLException e) {
logger.debug("error skipMoreResults", e);
ExceptionMapper.throwException(e, connection, this);
}
}
/**
* Moves to this Statement
object's next result, returns true
if it is a
* ResultSet
object, and implicitly closes any current ResultSet
object(s) obtained with
* the method getResultSet
.
* There are no more results when the following is true: // stmt is a Statement object
* ((stmt.getInternalMoreResults() == false) && (stmt.getUpdateCount() == -1))
*
* @return true
if the next result is a ResultSet
object; false
if it is an
* update count or there are no more results
* @throws SQLException if a database access error occurs or this method is called on a closed
* Statement
* @see #execute
*/
public boolean getMoreResults() throws SQLException {
return getMoreResults(Statement.CLOSE_CURRENT_RESULT);
}
/**
* Moves to this Statement
object's next result, deals with any current ResultSet
* object(s) according to the instructions specified by the given flag, and returns true
if the next
* result is a ResultSet
object.
* There are no more results when the following is true: // stmt is a Statement object
* ((stmt.getInternalMoreResults(current) == false) && (stmt.getUpdateCount() == -1))
*
* @param current one of the following Statement
constants indicating what should happen to current
* ResultSet
objects obtained using the method getResultSet
:
* Statement.CLOSE_CURRENT_RESULT
, Statement.KEEP_CURRENT_RESULT
,
* or Statement.CLOSE_ALL_RESULTS
* @return true
if the next result is a ResultSet
object; false
if it is an
* update count or there are no more results
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
* or the argument supplied is not one of the following: Statement.CLOSE_CURRENT_RESULT
,
* Statement.KEEP_CURRENT_RESULT
or Statement.CLOSE_ALL_RESULTS
* @throws SQLFeatureNotSupportedException if DatabaseMetaData.supportsMultipleOpenResults
returns
* false
and either Statement.KEEP_CURRENT_RESULT
* or Statement.CLOSE_ALL_RESULTS
are supplied as the argument.
* @see #execute
* @since 1.4
*/
public boolean getMoreResults(final int current) throws SQLException {
//if fetch size is set to read fully, other resultSet are put in cache
checkClose();
return results.getMoreResults(current, protocol);
}
/**
* Retrieves the direction for fetching rows from database tables that is the default for result sets generated from this Statement
* object. If this Statement
object has not set a fetch direction by calling the method setFetchDirection
, the return
* value is implementation-specific.
*
* @return the default fetch direction for result sets generated from this Statement
object
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @see #setFetchDirection
* @since 1.2
*/
public int getFetchDirection() throws SQLException {
return ResultSet.FETCH_FORWARD;
}
/**
* Gives the driver a hint as to the direction in which rows will be processed in ResultSet
objects created using this
* Statement
object. The default value is ResultSet.FETCH_FORWARD
.
* Note that this method sets the default fetch
* direction for result sets generated by this Statement
object. Each result set has its own methods for getting and setting its own
* fetch direction.
*
* @param direction the initial direction for processing rows
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
* or the given direction is not one of
* ResultSet.FETCH_FORWARD
,
* ResultSet.FETCH_REVERSE
,
* or ResultSet.FETCH_UNKNOWN
* @see #getFetchDirection
* @since 1.2
*/
public void setFetchDirection(final int direction) throws SQLException {
//not implemented
}
/**
* Retrieves the number of result set rows that is the default fetch size for ResultSet
objects generated from this
* Statement
object. If this Statement
object has not set a fetch size by calling the method setFetchSize
,
* the return value is implementation-specific.
*
* @return the default fetch size for result sets generated from this Statement
object
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @see #setFetchSize
* @since 1.2
*/
public int getFetchSize() throws SQLException {
return this.fetchSize;
}
/**
* Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for
* ResultSet
objects genrated by this Statement
. If the value specified is zero, then the hint is ignored. The default
* value is zero.
*
* @param rows the number of rows to fetch
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
or the condition
* rows >= 0
is not satisfied.
* @see #getFetchSize
* @since 1.2
*/
public void setFetchSize(final int rows) throws SQLException {
if (rows < 0 && rows != Integer.MIN_VALUE) {
throw new SQLException("invalid fetch size");
} else if (rows == Integer.MIN_VALUE) {
//for compatibility Integer.MIN_VALUE is transform to 0 => streaming
this.fetchSize = 1;
return;
}
this.fetchSize = rows;
}
/**
* Retrieves the result set concurrency for ResultSet
objects generated by this Statement
object.
*
* @return either ResultSet.CONCUR_READ_ONLY
or ResultSet.CONCUR_UPDATABLE
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @since 1.2
*/
public int getResultSetConcurrency() throws SQLException {
return ResultSet.CONCUR_READ_ONLY;
}
/**
* Retrieves the result set type for ResultSet
objects generated by this Statement
object.
*
* @return one of ResultSet.TYPE_FORWARD_ONLY
, ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @throws SQLException if a database access error occurs or this method is called on a closed Statement
* @since 1.2
*/
public int getResultSetType() throws SQLException {
return resultSetScrollType;
}
/**
* Adds the given SQL command to the current list of commmands for this Statement
object. The send in this list can be executed
* as a batch by calling the method executeBatch
.
*
* @param sql typically this is a SQL INSERT
or UPDATE
statement
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
or the driver does
* not support batch updates
* @see #executeBatch
* @see DatabaseMetaData#supportsBatchUpdates
* @since 1.2
*/
public void addBatch(final String sql) throws SQLException {
if (batchQueries == null) batchQueries = new ArrayList<>();
if (sql == null) throw ExceptionMapper.getSqlException("null cannot be set to addBatch( String sql)");
batchQueries.add(sql);
}
/**
* Empties this Statement
object's current list of SQL send.
*
* @see #addBatch
* @see DatabaseMetaData#supportsBatchUpdates
* @since 1.2
*/
public void clearBatch() {
if (batchQueries != null) batchQueries.clear();
}
/**
* Execute statements. depending on option, queries mays be rewritten :
*
* those queries will be rewritten if possible to
* INSERT INTO ... VALUES (...) ; INSERT INTO ... VALUES (...);
*
* if option rewriteBatchedStatements is set to true, rewritten to
* INSERT INTO ... VALUES (...), (...);
*
* @return an array of update counts containing one element for each command in the batch. The elements of the
* array are ordered according to the order in which send were added to the batch.
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
* or the driver does not support batch statements. Throws {@link BatchUpdateException}
* (a subclass of SQLException
) if one of the send
* sent to the database fails to execute properly or attempts to return a result set.
* @see #addBatch
* @see DatabaseMetaData#supportsBatchUpdates
* @since 1.3
*/
public int[] executeBatch() throws SQLException {
checkClose();
int size;
if (batchQueries == null || (size = batchQueries.size()) == 0) return new int[0];
lock.lock();
try {
internalBatchExecution(size);
return results.getCmdInformation().getUpdateCounts();
} catch (SQLException initialSqlEx) {
throw executeBatchExceptionEpilogue(initialSqlEx, results.getCmdInformation(), size, false);
} finally {
executeBatchEpilogue();
lock.unlock();
}
}
/**
* Internal batch execution.
*
* @param size expected result-set size
* @throws SQLException throw exception if batch error occur
*/
protected void internalBatchExecution(int size) throws SQLException {
executeQueryPrologue(true);
results.reset(0, true, size, false, resultSetScrollType);
if (this.options.rewriteBatchedStatements) {
//check that queries are rewritable
boolean batchQueryMultiRewritable = true;
for (String query : batchQueries) {
if (!ClientPrepareResult.isRewritableBatch(query, connection.noBackslashEscapes)) {
batchQueryMultiRewritable = false;
break;
}
}
if (protocol.isInterrupted()) {
//interrupted by timeout, must throw an exception manually
throw new SQLTimeoutException("Timeout during batch execution");
}
if (batchQueryMultiRewritable) {
protocol.executeBatchMultiple(protocol.isMasterConnection(), results, batchQueries);
} else {
protocol.executeBatch(protocol.isMasterConnection(), results, batchQueries);
}
} else {
protocol.executeBatch(protocol.isMasterConnection(), results, batchQueries);
}
results.commandEnd();
}
/**
*
Returns an object that implements the given interface to allow access to non-standard methods, or standard methods not exposed by the
* proxy.
* If the receiver implements the interface then the result is the receiver or a proxy for the receiver. If the receiver is a wrapper and
* the wrapped object implements the interface then the result is the wrapped object or a proxy for the wrapped object. Otherwise return the the
* result of calling unwrap
recursively on the wrapped object or a proxy for that result. If the receiver is not a wrapper and does
* not implement the interface, then an SQLException
is thrown.
*
* @param iface A Class defining an interface that the result must implement.
* @return an object that implements the interface. May be a proxy for the actual implementing object.
* @throws SQLException If no object found that implements the interface
* @since 1.6
*/
@SuppressWarnings("unchecked")
public T unwrap(final Class iface) throws SQLException {
try {
if (isWrapperFor(iface)) {
return (T) this;
} else {
throw new SQLException("The receiver is not a wrapper and does not implement the interface");
}
} catch (Exception e) {
throw new SQLException("The receiver is not a wrapper and does not implement the interface");
}
}
/**
* Returns true if this either implements the interface argument or is directly or indirectly a wrapper for an object that does. Returns false
* otherwise. If this implements the interface then return true, else if this is a wrapper then return the result of recursively calling
* isWrapperFor
on the wrapped object. If this does not implement the interface and is not a wrapper, return false. This method
* should be implemented as a low-cost operation compared to unwrap
so that callers can use this method to avoid expensive
* unwrap
calls that may fail. If this method returns true then calling unwrap
with the same argument should succeed.
*
* @param interfaceOrWrapper a Class defining an interface.
* @return true if this implements the interface or directly or indirectly wraps an object that does.
* @throws SQLException if an error occurs while determining whether this is a wrapper for an object with the given interface.
* @since 1.6
*/
public boolean isWrapperFor(final Class> interfaceOrWrapper) throws SQLException {
return interfaceOrWrapper.isInstance(this);
}
public void closeOnCompletion() throws SQLException {
mustCloseOnCompletion = true;
}
public boolean isCloseOnCompletion() throws SQLException {
return mustCloseOnCompletion;
}
/**
* Check that close on completion is asked, and close if so.
*
* @param resultSet resultSet
* @throws SQLException if close has error
*/
public void checkCloseOnCompletion(ResultSet resultSet) throws SQLException {
if (mustCloseOnCompletion
&& !closed
&& resultSet.equals(results.getResultSet())) {
close();
}
}
/**
* Check if statement is closed, and throw exception if so.
*
* @throws SQLException if statement close
*/
protected void checkClose() throws SQLException {
if (closed) {
throw new SQLException("Cannot do an operation on a closed statement");
}
}
/**
* Permit to retrieve current connection thread id, or -1 if unknown.
*
* @return current connection thread id.
*/
public long getServerThreadId() {
return (protocol != null) ? protocol.getServerThreadId() : -1;
}
}