All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mariadb.jdbc.MariaDbStatement Maven / Gradle / Ivy

The newest version!
/*
 *
 * 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.
     * 
    *
  1. clear previous query state
  2. *
  3. launch timeout timer if needed
  4. *
* * @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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy