org.mariadb.jdbc.MariaDbConnection Maven / Gradle / Ivy
Show all versions of mariadb-java-client Show documentation
/*
*
* MariaDB Client for Java
*
* Copyright (c) 2012-2014 Monty Program Ab.
* Copyright (c) 2015-2020 MariaDB Corporation 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 java.net.SocketException;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.CallableStatementCache;
import org.mariadb.jdbc.internal.util.ConnectionState;
import org.mariadb.jdbc.internal.util.Utils;
import org.mariadb.jdbc.internal.util.dao.CallableStatementCacheKey;
import org.mariadb.jdbc.internal.util.dao.CloneableCallableStatement;
import org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory;
import org.mariadb.jdbc.internal.util.pool.GlobalStateInfo;
import org.mariadb.jdbc.internal.util.pool.Pools;
import org.mariadb.jdbc.util.Options;
@SuppressWarnings("Annotator")
public class MariaDbConnection implements Connection {
private static final Logger logger = LoggerFactory.getLogger(MariaDbConnection.class);
/**
* Pattern to check the correctness of callable statement query string Legal queries, as
* documented in JDK have the form: {[?=]call[(arg1,..,,argn)]}
*/
private static final Pattern CALLABLE_STATEMENT_PATTERN =
Pattern.compile(
"^(\\s*\\{)?\\s*((\\?\\s*=)?(\\s*\\/\\*([^\\*]|\\*[^\\/])*\\*\\/)*\\s*"
+ "call(\\s*\\/\\*([^\\*]|\\*[^\\/])*\\*\\/)*\\s*((((`[^`]+`)|([^`\\}]+))\\.)?"
+ "((`[^`]+`)|([^`\\}\\(]+)))\\s*(\\(.*\\))?(\\s*\\/\\*([^\\*]|\\*[^\\/])*\\*\\/)*"
+ "\\s*(#.*)?)\\s*(\\}\\s*)?$",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
/** Check that query can be executed with PREPARE. */
private static final Pattern PREPARABLE_STATEMENT_PATTERN =
Pattern.compile(
"^(\\s*\\/\\*([^\\*]|\\*[^\\/])*\\*\\/)*\\s*(SELECT|UPDATE|INSERT|DELETE|REPLACE|DO|CALL)",
Pattern.CASE_INSENSITIVE);
public final ReentrantLock lock;
/** the protocol to communicate with. */
private final Protocol protocol;
/** the properties for the client. */
private final Options options;
public MariaDbPooledConnection pooledConnection;
protected boolean nullCatalogMeansCurrent;
private CallableStatementCache callableStatementCache;
private volatile int lowercaseTableNames = -1;
private boolean canUseServerTimeout;
private boolean sessionStateAware;
private int stateFlag = 0;
private int defaultTransactionIsolation = 0;
private ExceptionFactory exceptionFactory;
private boolean warningsCleared;
/**
* Creates a new connection with a given protocol and query factory.
*
* @param protocol the protocol to use.
*/
public MariaDbConnection(Protocol protocol) {
this.protocol = protocol;
options = protocol.getOptions();
canUseServerTimeout = protocol.versionGreaterOrEqual(10, 1, 2);
sessionStateAware = protocol.sessionStateAware();
nullCatalogMeansCurrent = options.nullCatalogMeansCurrent;
if (options.cacheCallableStmts) {
callableStatementCache = CallableStatementCache.newInstance(options.callableStmtCacheSize);
}
this.lock = protocol.getLock();
this.exceptionFactory = ExceptionFactory.of(this.getServerThreadId(), this.options);
}
/**
* Create new connection Object.
*
* @param urlParser parser
* @param globalInfo global info
* @return connection object
* @throws SQLException if any connection error occur
*/
public static MariaDbConnection newConnection(UrlParser urlParser, GlobalStateInfo globalInfo)
throws SQLException {
if (urlParser.getOptions().pool) {
return Pools.retrievePool(urlParser).getConnection();
}
Protocol protocol = Utils.retrieveProxy(urlParser, globalInfo);
return new MariaDbConnection(protocol);
}
public static String quoteIdentifier(String string) {
return "`" + string.replaceAll("`", "``") + "`";
}
/**
* UnQuote string.
*
* @param string value
* @return unquote string
* @deprecated since 1.3.0
*/
@Deprecated
public static String unquoteIdentifier(String string) {
if (string != null && string.startsWith("`") && string.endsWith("`") && string.length() >= 2) {
return string.substring(1, string.length() - 1).replace("``", "`");
}
return string;
}
protected Protocol getProtocol() {
return protocol;
}
/**
* creates a new statement.
*
* @return a statement
* @throws SQLException if we cannot create the statement.
*/
public Statement createStatement() throws SQLException {
checkConnection();
return new MariaDbStatement(
this, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, exceptionFactory);
}
/**
* Creates a Statement
object that will generate ResultSet
objects with
* the given type and concurrency. This method is the same as the createStatement
* method above, but it allows the default result set type and concurrency to be overridden. The
* holdability of the created result sets can be determined by calling {@link #getHoldability}.
*
* @param resultSetType a result set type; one of ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency a concurrency type; one of ResultSet.CONCUR_READ_ONLY
* or ResultSet.CONCUR_UPDATABLE
* @return a new Statement
object that will generate ResultSet
objects
* with the given type and concurrency
*/
public Statement createStatement(final int resultSetType, final int resultSetConcurrency) {
return new MariaDbStatement(this, resultSetType, resultSetConcurrency, exceptionFactory);
}
/**
* Creates a Statement
object that will generate ResultSet
objects with
* the given type, concurrency, and holdability. This method is the same as the
* createStatement
method above, but it allows the default result set type, concurrency,
* and holdability to be overridden.
*
* @param resultSetType one of the following ResultSet
constants:
* ResultSet.TYPE_FORWARD_ONLY
, ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency one of the following ResultSet
constants:
* ResultSet.CONCUR_READ_ONLY
or ResultSet.CONCUR_UPDATABLE
* @param resultSetHoldability one of the following ResultSet
constants:
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or ResultSet.CLOSE_CURSORS_AT_COMMIT
* @return a new Statement
object that will generate ResultSet
objects
* with the given type, concurrency, and holdability
* @see ResultSet
*/
public Statement createStatement(
final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) {
return new MariaDbStatement(this, resultSetType, resultSetConcurrency, exceptionFactory);
}
private void checkConnection() throws SQLException {
if (protocol.isExplicitClosed()) {
throw exceptionFactory.create("createStatement() is called on closed connection", "08000");
}
if (protocol.isClosed() && protocol.getProxy() != null) {
lock.lock();
try {
protocol.getProxy().reconnect();
} finally {
lock.unlock();
}
}
}
/**
* Create a new client prepared statement.
*
* @param sql the query.
* @return a client prepared statement.
* @throws SQLException if there is a problem preparing the statement.
*/
public ClientSidePreparedStatement clientPrepareStatement(final String sql) throws SQLException {
return new ClientSidePreparedStatement(
this,
sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY,
Statement.RETURN_GENERATED_KEYS,
exceptionFactory);
}
/**
* Create a new server prepared statement.
*
* @param sql the query.
* @return a server prepared statement.
* @throws SQLException if there is a problem preparing the statement.
*/
public ServerSidePreparedStatement serverPrepareStatement(final String sql) throws SQLException {
return new ServerSidePreparedStatement(
this,
sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY,
Statement.RETURN_GENERATED_KEYS,
exceptionFactory);
}
/**
* creates a new prepared statement.
*
* @param sql the query.
* @return a prepared statement.
* @throws SQLException if there is a problem preparing the statement.
*/
public PreparedStatement prepareStatement(final String sql) throws SQLException {
return internalPrepareStatement(
sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, Statement.NO_GENERATED_KEYS);
}
/**
* Creates a PreparedStatement
object that will generate ResultSet
* objects with the given type and concurrency. This method is the same as the
* prepareStatement
method above, but it allows the default result set type and concurrency
* to be overridden. The holdability of the created result sets can be determined by calling
* {@link #getHoldability}.
*
* @param sql a String
object that is the SQL statement to be sent to the database;
* may contain one or more '?' IN parameters
* @param resultSetType a result set type; one of ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency a concurrency type; one of ResultSet.CONCUR_READ_ONLY
* or ResultSet.CONCUR_UPDATABLE
* @return a new PreparedStatement object containing the pre-compiled SQL statement that will
* produce ResultSet
objects with the given type and concurrency
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameters are notResultSet
constants indicating type
* and concurrency
*/
public PreparedStatement prepareStatement(
final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
return internalPrepareStatement(
sql, resultSetType, resultSetConcurrency, Statement.NO_GENERATED_KEYS);
}
/**
* Creates a PreparedStatement
object that will generate ResultSet
* objects with the given type, concurrency, and holdability.
*
* This method is the same as the prepareStatement
method above, but it allows the
* default result set type, concurrency, and holdability to be overridden.
*
* @param sql a String
object that is the SQL statement to be sent to the database;
* may contain one or more '?' IN parameters
* @param resultSetType one of the following ResultSet
constants:
* ResultSet.TYPE_FORWARD_ONLY
, ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency one of the following ResultSet
constants:
* ResultSet.CONCUR_READ_ONLY
or ResultSet.CONCUR_UPDATABLE
* @param resultSetHoldability one of the following ResultSet
constants:
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or ResultSet.CLOSE_CURSORS_AT_COMMIT
* @return a new PreparedStatement
object, containing the pre-compiled SQL statement,
* that will generate ResultSet
objects with the given type, concurrency, and
* holdability
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameters are not ResultSet
constants indicating
* type, concurrency, and holdability
* @see ResultSet
*/
public PreparedStatement prepareStatement(
final String sql,
final int resultSetType,
final int resultSetConcurrency,
final int resultSetHoldability)
throws SQLException {
return internalPrepareStatement(
sql, resultSetType, resultSetConcurrency, Statement.NO_GENERATED_KEYS);
}
/**
* Creates a default PreparedStatement
object that has the capability to retrieve
* auto-generated keys. The given constant tells the driver whether it should make auto-generated
* keys available for retrieval. This parameter is ignored 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).
*
*
Note: This method is optimized for handling parametric SQL statements that benefit
* from precompilation. If the driver supports precompilation, the method prepareStatement
*
will send the statement to the database for precompilation. Some drivers may not
* support precompilation. In this case, the statement may not be sent to the database until the
* PreparedStatement
object is executed. This has no direct effect on users; however,
* it does affect which methods throw certain SQLExceptions.
*
*
Result sets created using the returned PreparedStatement
object will by default
* be type TYPE_FORWARD_ONLY
and have a concurrency level of CONCUR_READ_ONLY
*
. The holdability of the created result sets can be determined by calling {@link
* #getHoldability}.
*
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
* @param autoGeneratedKeys a flag indicating whether auto-generated keys should be returned; one
* of Statement.RETURN_GENERATED_KEYS
or Statement.NO_GENERATED_KEYS
* @return a new PreparedStatement
object, containing the pre-compiled SQL statement,
* that will have the capability of returning auto-generated keys
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameter is not a Statement
constant indicating
* whether auto-generated keys should be returned
*/
public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys)
throws SQLException {
return internalPrepareStatement(
sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, autoGeneratedKeys);
}
/**
* Creates a default PreparedStatement
object capable of returning the auto-generated
* keys designated by the given array. 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).
*
*
An SQL statement with or without IN parameters can be pre-compiled and stored in a
* PreparedStatement
object. This object can then be used to efficiently execute this
* statement multiple times.
*
*
Note: This method is optimized for handling parametric SQL statements that benefit
* from precompilation. If the driver supports precompilation, the method prepareStatement
*
will send the statement to the database for precompilation. Some drivers may not
* support precompilation. In this case, the statement may not be sent to the database until the
* PreparedStatement
object is executed. This has no direct effect on users; however,
* it does affect which methods throw certain SQLExceptions.
*
*
Result sets created using the returned PreparedStatement
object will by default
* be type TYPE_FORWARD_ONLY
and have a concurrency level of CONCUR_READ_ONLY
*
. The holdability of the created result sets can be determined by calling {@link
* #getHoldability}.
*
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
* @param columnIndexes an array of column indexes indicating the columns that should be returned
* from the inserted row or rows
* @return a new PreparedStatement
object, containing the pre-compiled statement,
* that is capable of returning the auto-generated keys designated by the given array of
* column indexes
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
*/
public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes)
throws SQLException {
return prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
}
/**
* Creates a default PreparedStatement
object capable of returning the auto-generated
* keys designated by the given array. This array contains the names of the columns in the target
* table that contain the auto-generated keys that should be returned. 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).
*
*
An SQL statement with or without IN parameters can be pre-compiled and stored in a
* PreparedStatement
object. This object can then be used to efficiently execute this
* statement multiple times.
*
*
Note: This method is optimized for handling parametric SQL statements that benefit
* from precompilation. If the driver supports precompilation, the method prepareStatement
*
will send the statement to the database for precompilation. Some drivers may not
* support precompilation. In this case, the statement may not be sent to the database until the
* PreparedStatement
object is executed. This has no direct effect on users; however,
* it does affect which methods throw certain SQLExceptions.
*
*
Result sets created using the returned PreparedStatement
object will by default
* be type TYPE_FORWARD_ONLY
and have a concurrency level of CONCUR_READ_ONLY
*
. The holdability of the created result sets can be determined by calling {@link
* #getHoldability}.
*
* @param sql an SQL statement that may contain one or more '?' IN parameter placeholders
* @param columnNames an array of column names indicating the columns that should be returned from
* the inserted row or rows
* @return a new PreparedStatement
object, containing the pre-compiled statement,
* that is capable of returning the auto-generated keys designated by the given array of
* column names
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
*/
public PreparedStatement prepareStatement(final String sql, final String[] columnNames)
throws SQLException {
return prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
}
/**
* Send ServerPrepareStatement or ClientPrepareStatement depending on SQL query and options If
* server side and PREPARE can be delayed, a facade will be return, to have a fallback on client
* prepareStatement.
*
* @param sql sql query
* @param resultSetScrollType one of the following ResultSet
constants:
* ResultSet.TYPE_FORWARD_ONLY
, ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency a concurrency type; one of ResultSet.CONCUR_READ_ONLY
* or ResultSet.CONCUR_UPDATABLE
* @param autoGeneratedKeys a flag indicating whether auto-generated keys should be returned; one
* of Statement.RETURN_GENERATED_KEYS
or Statement.NO_GENERATED_KEYS
* @return PrepareStatement
* @throws SQLException if a connection error occur during the server preparation.
*/
private PreparedStatement internalPrepareStatement(
final String sql,
final int resultSetScrollType,
final int resultSetConcurrency,
final int autoGeneratedKeys)
throws SQLException {
if (sql != null) {
String sqlQuery = Utils.nativeSql(sql, protocol);
if (options.useServerPrepStmts && PREPARABLE_STATEMENT_PATTERN.matcher(sqlQuery).find()) {
// prepare isn't delayed -> if prepare fail, fallback to client preparedStatement?
checkConnection();
try {
return new ServerSidePreparedStatement(
this,
sqlQuery,
resultSetScrollType,
resultSetConcurrency,
autoGeneratedKeys,
exceptionFactory);
} catch (SQLNonTransientConnectionException e) {
throw e;
} catch (SQLException e) {
// on some specific case, server cannot prepared data (CONJ-238)
// will use clientPreparedStatement
}
}
return new ClientSidePreparedStatement(
this,
sqlQuery,
resultSetScrollType,
resultSetConcurrency,
autoGeneratedKeys,
exceptionFactory);
} else {
throw new SQLException("SQL value can not be NULL");
}
}
public CallableParameterMetaData getInternalParameterMetaData(
String procedureName, String databaseName, boolean isFunction) throws SQLException {
PreparedStatement prep =
new ClientSidePreparedStatement(
this,
"SELECT * from information_schema.PARAMETERS "
+ "WHERE SPECIFIC_NAME = ? "
+ "AND SPECIFIC_SCHEMA = "
+ (databaseName != null ? "?" : "DATABASE()")
+ " ORDER BY ORDINAL_POSITION",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY,
Statement.NO_GENERATED_KEYS,
exceptionFactory);
prep.setString(1, procedureName);
if (databaseName != null) prep.setString(2, databaseName);
ResultSet rs = prep.executeQuery();
return new CallableParameterMetaData(rs, isFunction);
}
/**
* Creates a CallableStatement
object for calling database stored procedures. The
* CallableStatement
object provides methods for setting up its IN and OUT
* parameters, and methods for executing the call to a stored procedure. example : {?= call
* <procedure-name>[(<arg1>,<arg2>, ...)]} or {call
* <procedure-name>[(<arg1>,<arg2>, ...)]}
*
*
Note: This method is optimized for handling stored procedure call statements.
*
* @param sql an SQL statement that may contain one or more '?' parameter placeholders. Typically
* this statement is specified using JDBC call escape syntax.
* @return a new default CallableStatement
object containing the pre-compiled SQL
* statement
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
*/
public CallableStatement prepareCall(final String sql) throws SQLException {
return prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
}
/**
* Creates a CallableStatement
object that will generate ResultSet
* objects with the given type and concurrency. This method is the same as the prepareCall
*
method above, but it allows the default result set type and concurrency to be
* overridden. The holdability of the created result sets can be determined by calling {@link
* #getHoldability}.
*
* @param sql a String
object that is the SQL statement to be sent to the database;
* may contain on or more '?' parameters
* @param resultSetType a result set type; one of ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency a concurrency type; one of ResultSet.CONCUR_READ_ONLY
* or ResultSet.CONCUR_UPDATABLE
* @return a new CallableStatement
object containing the pre-compiled SQL statement
* that will produce ResultSet
objects with the given type and concurrency
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameters are not ResultSet
constants indicating type
* and concurrency
*/
public CallableStatement prepareCall(
final String sql, final int resultSetType, final int resultSetConcurrency)
throws SQLException {
checkConnection();
Matcher matcher = CALLABLE_STATEMENT_PATTERN.matcher(sql);
if (!matcher.matches()) {
throw new SQLSyntaxErrorException(
"invalid callable syntax. must be like {[?=]call [(?,?, ...)]}\n but was : "
+ sql);
}
String query = Utils.nativeSql(matcher.group(2), protocol);
boolean isFunction = (matcher.group(3) != null);
String databaseAndProcedure = matcher.group(8);
String database = matcher.group(10);
String procedureName = matcher.group(13);
String arguments = matcher.group(16);
if (database == null && sessionStateAware) {
database = protocol.getDatabase();
}
if (database != null && options.cacheCallableStmts) {
if (callableStatementCache.containsKey(new CallableStatementCacheKey(database, query))) {
try {
CallableStatement callableStatement =
callableStatementCache.get(new CallableStatementCacheKey(database, query));
if (callableStatement != null) {
// Clone to avoid side effect like having some open resultSet.
return ((CloneableCallableStatement) callableStatement).clone(this);
}
} catch (CloneNotSupportedException cloneNotSupportedException) {
cloneNotSupportedException.printStackTrace();
}
}
CallableStatement callableStatement =
createNewCallableStatement(
query,
procedureName,
isFunction,
databaseAndProcedure,
database,
arguments,
resultSetType,
resultSetConcurrency,
exceptionFactory);
callableStatementCache.put(new CallableStatementCacheKey(database, query), callableStatement);
return callableStatement;
}
return createNewCallableStatement(
query,
procedureName,
isFunction,
databaseAndProcedure,
database,
arguments,
resultSetType,
resultSetConcurrency,
exceptionFactory);
}
/**
* Creates a CallableStatement
object that will generate ResultSet
* objects with the given type and concurrency. This method is the same as the prepareCall
*
method above, but it allows the default result set type, result set concurrency type
* and holdability to be overridden.
*
* @param sql a String
object that is the SQL statement to be sent to the database;
* may contain on or more '?' parameters
* @param resultSetType one of the following ResultSet
constants:
* ResultSet.TYPE_FORWARD_ONLY
, ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @param resultSetConcurrency one of the following ResultSet
constants:
* ResultSet.CONCUR_READ_ONLY
or ResultSet.CONCUR_UPDATABLE
* @param resultSetHoldability one of the following ResultSet
constants:
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or ResultSet.CLOSE_CURSORS_AT_COMMIT
* @return a new CallableStatement
object, containing the pre-compiled SQL statement,
* that will generate ResultSet
objects with the given type, concurrency, and
* holdability
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameters are not ResultSet
constants indicating
* type, concurrency, and holdability
* @see ResultSet
*/
public CallableStatement prepareCall(
final String sql,
final int resultSetType,
final int resultSetConcurrency,
final int resultSetHoldability)
throws SQLException {
return prepareCall(sql);
}
private CallableStatement createNewCallableStatement(
String query,
String procedureName,
boolean isFunction,
String databaseAndProcedure,
String database,
String arguments,
int resultSetType,
final int resultSetConcurrency,
ExceptionFactory exceptionFactory)
throws SQLException {
if (isFunction) {
return new MariaDbFunctionStatement(
this,
database,
databaseAndProcedure,
(arguments == null) ? "()" : arguments,
resultSetType,
resultSetConcurrency,
exceptionFactory);
} else {
return new MariaDbProcedureStatement(
query,
this,
procedureName,
database,
resultSetType,
resultSetConcurrency,
exceptionFactory);
}
}
@Override
public String nativeSQL(final String sql) throws SQLException {
return Utils.nativeSql(sql, protocol);
}
/**
* returns true if statements on this connection are auto committed.
*
* @return true if auto commit is on.
* @throws SQLException if there is an error
*/
public boolean getAutoCommit() throws SQLException {
return protocol.getAutocommit();
}
/**
* Sets whether this connection is auto committed.
*
* @param autoCommit if it should be auto committed.
* @throws SQLException if something goes wrong talking to the server.
*/
public void setAutoCommit(boolean autoCommit) throws SQLException {
if (autoCommit == getAutoCommit()) {
return;
}
try (Statement stmt = createStatement()) {
stateFlag |= ConnectionState.STATE_AUTOCOMMIT;
stmt.executeUpdate(autoCommit ? "set autocommit=1" : "set autocommit=0");
}
}
/**
* Sends commit to the server.
*
* @throws SQLException if there is an error committing.
*/
public void commit() throws SQLException {
lock.lock();
try {
if (protocol.inTransaction()) {
try (Statement st = createStatement()) {
st.execute("COMMIT");
}
}
} finally {
lock.unlock();
}
}
/**
* Rolls back a transaction.
*
* @throws SQLException if there is an error rolling back.
*/
public void rollback() throws SQLException {
lock.lock();
try {
if (protocol.inTransaction()) {
try (Statement st = createStatement()) {
st.execute("ROLLBACK");
}
}
} finally {
lock.unlock();
}
}
/**
* Undoes all changes made after the given Savepoint
object was set.
*
* This method should be used only when auto-commit has been disabled.
*
* @param savepoint the Savepoint
object to roll back to
* @throws SQLException if a database access error occurs, this method is called while
* participating in a distributed transaction, this method is called on a closed connection,
* the Savepoint
object is no longer valid, or this Connection
* object is currently in auto-commit mode
* @see Savepoint
* @see #rollback
*/
public void rollback(final Savepoint savepoint) throws SQLException {
try (Statement st = createStatement()) {
st.execute("ROLLBACK TO SAVEPOINT `" + savepoint.getSavepointName() + "`");
}
}
/**
* close the connection.
*
* @throws SQLException if there is a problem talking to the server.
*/
public void close() throws SQLException {
if (pooledConnection != null) {
rollback();
pooledConnection.fireConnectionClosed();
return;
}
protocol.closeExplicit();
}
/**
* checks if the connection is closed.
*
* @return true if the connection is closed
*/
public boolean isClosed() {
return protocol.isClosed();
}
/**
* returns the meta data about the database.
*
* @return meta data about the db.
*/
public DatabaseMetaData getMetaData() {
return new MariaDbDatabaseMetaData(this, protocol.getUrlParser());
}
/**
* Retrieves whether this Connection
object is in read-only mode.
*
* @return true
if this Connection
object is read-only; false
*
otherwise
* @throws SQLException SQLException if a database access error occurs or this method is called on
* a closed connection
*/
public boolean isReadOnly() throws SQLException {
return protocol.getReadonly();
}
/**
* Sets whether this connection is read only.
*
* @param readOnly true if it should be read only.
* @throws SQLException if there is a problem
*/
public void setReadOnly(final boolean readOnly) throws SQLException {
try {
logger.debug(
"conn={}({}) - set read-only to value {} {}",
protocol.getServerThreadId(),
protocol.isMasterConnection() ? "M" : "S",
readOnly);
stateFlag |= ConnectionState.STATE_READ_ONLY;
protocol.setReadonly(readOnly);
} catch (SQLException e) {
throw exceptionFactory.create(e);
}
}
/**
* Retrieves this Connection
object's current catalog name.
*
* @return the current catalog name or null
if there is none
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
* @see #setCatalog
*/
public String getCatalog() throws SQLException {
return protocol.getCatalog();
}
/**
* Sets the given catalog name in order to select a subspace of this Connection
* object's database in which to work.
*
*
If the driver does not support catalogs, it will silently ignore this request. MariaDB
* treats catalogs and databases as equivalent
*
* @param catalog the name of a catalog (subspace in this Connection
object's
* database) in which to work
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
* @see #getCatalog
*/
public void setCatalog(final String catalog) throws SQLException {
if (catalog == null) {
throw new SQLException("The catalog name may not be null", "XAE05");
}
try {
stateFlag |= ConnectionState.STATE_DATABASE;
protocol.setCatalog(catalog);
} catch (SQLException e) {
throw exceptionFactory.create(e);
}
}
public boolean isServerMariaDb() throws SQLException {
return protocol.isServerMariaDb();
}
public boolean versionGreaterOrEqual(int major, int minor, int patch) {
return protocol.versionGreaterOrEqual(major, minor, patch);
}
/**
* Retrieves this Connection
object's current transaction isolation level.
*
* @return the current transaction isolation level, which will be one of the following constants:
* Connection.TRANSACTION_READ_UNCOMMITTED
,
* Connection.TRANSACTION_READ_COMMITTED
, Connection.TRANSACTION_REPEATABLE_READ
*
, Connection.TRANSACTION_SERIALIZABLE
, or
* Connection.TRANSACTION_NONE
.
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
* @see #setTransactionIsolation
*/
public int getTransactionIsolation() throws SQLException {
Statement stmt = createStatement();
String sql = "SELECT @@tx_isolation";
if (!protocol.isServerMariaDb()) {
if ((protocol.getMajorServerVersion() >= 8 && protocol.versionGreaterOrEqual(8, 0, 3))
|| (protocol.getMajorServerVersion() < 8 && protocol.versionGreaterOrEqual(5, 7, 20))) {
sql = "SELECT @@transaction_isolation";
}
}
ResultSet rs = stmt.executeQuery(sql);
if (rs.next()) {
final String response = rs.getString(1);
switch (response) {
case "REPEATABLE-READ":
return Connection.TRANSACTION_REPEATABLE_READ;
case "READ-UNCOMMITTED":
return Connection.TRANSACTION_READ_UNCOMMITTED;
case "READ-COMMITTED":
return Connection.TRANSACTION_READ_COMMITTED;
case "SERIALIZABLE":
return Connection.TRANSACTION_SERIALIZABLE;
default:
throw exceptionFactory.create(
String.format(
"Could not get transaction isolation level: Invalid value \"%s\"", response));
}
}
throw exceptionFactory.create("Failed to retrieve transaction isolation");
}
/**
* Attempts to change the transaction isolation level for this Connection
object to
* the one given. The constants defined in the interface Connection
are the possible
* transaction isolation levels.
*
*
Note: If this method is called during a transaction, the result is
* implementation-defined.
*
* @param level one of the following Connection
constants:
* Connection.TRANSACTION_READ_UNCOMMITTED
, Connection.TRANSACTION_READ_COMMITTED
*
, Connection.TRANSACTION_REPEATABLE_READ
, or
* Connection.TRANSACTION_SERIALIZABLE
. (Note that Connection.TRANSACTION_NONE
*
cannot be used because it specifies that transactions are not supported.)
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameter is not one of the Connection
constants
* @see DatabaseMetaData#supportsTransactionIsolationLevel
* @see #getTransactionIsolation
*/
public void setTransactionIsolation(final int level) throws SQLException {
try {
stateFlag |= ConnectionState.STATE_TRANSACTION_ISOLATION;
protocol.setTransactionIsolation(level);
} catch (SQLException e) {
throw exceptionFactory.create(e);
}
}
/**
* Retrieves the first warning reported by calls on this Connection
object. If there
* is more than one warning, subsequent warnings will be chained to the first one and can be
* retrieved by calling the method SQLWarning.getNextWarning
on the warning that was
* retrieved previously.
*
*
This method may not be called on a closed connection; doing so will cause an
* SQLException
to be thrown.
*
*
Note: Subsequent warnings will be chained to this SQLWarning.
*
* @return the first SQLWarning
object or null
if there are none
* @throws SQLException if a database access error occurs or this method is called on a closed
* connection
* @see SQLWarning
*/
public SQLWarning getWarnings() throws SQLException {
if (warningsCleared || isClosed() || !protocol.hasWarnings()) {
return null;
}
SQLWarning last = null;
SQLWarning first = null;
try (Statement st = this.createStatement()) {
try (ResultSet rs = st.executeQuery("show warnings")) {
// returned result set has 'level', 'code' and 'message' columns, in this order.
while (rs.next()) {
int code = rs.getInt(2);
String message = rs.getString(3);
SQLWarning warning = new SQLWarning(message, null, code);
if (first == null) {
first = warning;
last = warning;
} else {
last.setNextWarning(warning);
last = warning;
}
}
}
}
return first;
}
/**
* Clears all warnings reported for this Connection
object. After a call to this
* method, the method getWarnings
returns null
until a new warning is
* reported for this Connection
object.
*
* @throws SQLException SQLException if a database access error occurs or this method is called on
* a closed connection
*/
public void clearWarnings() throws SQLException {
if (this.isClosed()) {
throw exceptionFactory.create(
"Connection.clearWarnings cannot be called on a closed connection");
}
warningsCleared = true;
}
/** Re-enable warnings, when next statement is executed. */
public void reenableWarnings() {
warningsCleared = false;
}
/**
* Retrieves the Map
object associated with this Connection
object.
* Unless the application has added an entry, the type map returned will be empty.
*
* @return the java.util.Map
object associated with this Connection
* object
* @see #setTypeMap
* @since 1.2
*/
public Map> getTypeMap() {
return new HashMap<>();
}
/**
* Installs the given TypeMap
object as the type map for this Connection
* object. The type map will be used for the custom mapping of SQL structured types and distinct
* types.
*
* @param map the java.util.Map
object to install as the replacement for this
* Connection
object's default type map
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given parameter is not a java.util.Map
object
* @see #getTypeMap
*/
public void setTypeMap(final Map> map) throws SQLException {
throw exceptionFactory.notSupported("TypeMap are not supported");
}
/**
* Retrieves the current holdability of ResultSet
objects created using this
* Connection
object.
*
* @return the holdability, one of ResultSet.HOLD_CURSORS_OVER_COMMIT
or
* ResultSet.CLOSE_CURSORS_AT_COMMIT
* @see #setHoldability
* @see DatabaseMetaData#getResultSetHoldability
* @see ResultSet
* @since 1.4
*/
public int getHoldability() {
return ResultSet.HOLD_CURSORS_OVER_COMMIT;
}
/**
* Changes the default holdability of ResultSet
objects created using this
* Connection
object to the given holdability. The default holdability of ResultSet
*
objects can be be determined by invoking {@link
* DatabaseMetaData#getResultSetHoldability}.
*
* @param holdability a ResultSet
holdability constant; one of
* ResultSet.HOLD_CURSORS_OVER_COMMIT
or ResultSet.CLOSE_CURSORS_AT_COMMIT
* @see #getHoldability
* @see DatabaseMetaData#getResultSetHoldability
* @see ResultSet
*/
@Override
public void setHoldability(final int holdability) {
// not handled
}
/**
* Creates an unnamed savepoint in the current transaction and returns the new Savepoint
* *
object that represents it.
*
* if setSavepoint is invoked outside of an active transaction, a transaction will be started
* at this newly created savepoint.
*
* @return the new Savepoint
object
* @throws SQLException if a database access error occurs, this method is called while
* participating in a distributed transaction, this method is called on a closed connection or
* this Connection
object is currently in auto-commit mode
* @see Savepoint
* @since 1.4
*/
public Savepoint setSavepoint() throws SQLException {
String randomName = UUID.randomUUID().toString();
return setSavepoint(randomName);
}
/**
* Creates a savepoint with the given name in the current transaction and returns the new
* Savepoint
object that represents it. if setSavepoint is invoked outside of an active
* transaction, a transaction will be started at this newly created savepoint.
*
* @param name a String
containing the name of the savepoint
* @return the new Savepoint
object
* @throws SQLException if a database access error occurs, this method is called while
* participating in a distributed transaction, this method is called on a closed connection or
* this Connection
object is currently in auto-commit mode
* @see Savepoint
* @since 1.4
*/
public Savepoint setSavepoint(final String name) throws SQLException {
Savepoint savepoint = new MariaDbSavepoint(name);
try (Statement st = createStatement()) {
st.execute("SAVEPOINT `" + savepoint.getSavepointName() + "`");
}
return savepoint;
}
/**
* Removes the specified Savepoint
and subsequent Savepoint
objects from
* the current transaction. Any reference to the savepoint after it have been removed will cause
* an SQLException
to be thrown.
*
* @param savepoint the Savepoint
object to be removed
* @throws SQLException if a database access error occurs, this method is called on a closed
* connection or the given Savepoint
object is not a valid savepoint in the
* current transaction
*/
public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
try (Statement st = createStatement()) {
st.execute("RELEASE SAVEPOINT `" + savepoint.getSavepointName() + "`");
}
}
/**
* Constructs an object that implements the Clob
interface. The object returned
* initially contains no data. The setAsciiStream
, setCharacterStream
* and setString
methods of the Clob
interface may be used to add data
* to the Clob
.
*
* @return An object that implements the Clob
interface
*/
public Clob createClob() {
return new MariaDbClob();
}
/**
* Constructs an object that implements the Blob
interface. The object returned
* initially contains no data. The setBinaryStream
and setBytes
methods
* of the Blob
interface may be used to add data to the Blob
.
*
* @return An object that implements the Blob
interface
*/
public Blob createBlob() {
return new MariaDbBlob();
}
/**
* Constructs an object that implements the NClob
interface. The object returned
* initially contains no data. The setAsciiStream
, setCharacterStream
* and setString
methods of the NClob
interface may be used to add data
* to the NClob
.
*
* @return An object that implements the NClob
interface
*/
public NClob createNClob() {
return new MariaDbClob();
}
/**
* Constructs an object that implements the SQLXML
interface. The object returned
* initially contains no data. The createXmlStreamWriter
object and setString
*
method of the SQLXML
interface may be used to add data to the SQLXML
*
object.
*
* @return An object that implements the SQLXML
interface
* @throws SQLException if an object that implements the SQLXML
interface can not be
* constructed, this method is called on a closed connection or a database access error
* occurs.
*/
@Override
public SQLXML createSQLXML() throws SQLException {
throw exceptionFactory.notSupported("SQLXML type is not supported");
}
/**
* Returns true if the connection has not been closed and is still valid. The driver shall submit
* a query on the connection or use some other mechanism that positively verifies the connection
* is still valid when this method is called.
*
*
The query submitted by the driver to validate the connection shall be executed in the
* context of the current transaction.
*
* @param timeout - The time in seconds to wait for the database operation used to validate the
* connection to complete. If the timeout period expires before the operation completes, this
* method returns false. A value of 0 indicates a timeout is not applied to the database
* operation.
* @return true if the connection is valid, false otherwise
* @throws SQLException if the value supplied for timeout
is less then 0
* @see DatabaseMetaData#getClientInfoProperties
* @since 1.6
*/
public boolean isValid(final int timeout) throws SQLException {
if (timeout < 0) {
throw new SQLException("the value supplied for timeout is negative");
}
if (isClosed()) {
return false;
}
try {
return protocol.isValid(timeout * 1000);
} catch (SQLException e) {
if (pooledConnection != null) {
MariaDbPooledConnection poolConnection = this.pooledConnection;
poolConnection.fireConnectionErrorOccurred(e);
poolConnection.close();
}
return false;
}
}
private void checkClientClose(final String name) throws SQLClientInfoException {
if (protocol.isExplicitClosed()) {
Map failures = new HashMap<>();
failures.put(name, ClientInfoStatus.REASON_UNKNOWN);
throw new SQLClientInfoException("setClientInfo() is called on closed connection", failures);
}
}
private void checkClientReconnect(final String name) throws SQLClientInfoException {
if (protocol.isClosed() && protocol.getProxy() != null) {
lock.lock();
try {
protocol.getProxy().reconnect();
} catch (SQLException sqle) {
Map failures = new HashMap<>();
failures.put(name, ClientInfoStatus.REASON_UNKNOWN);
throw new SQLClientInfoException("Connection closed", failures, sqle);
} finally {
lock.unlock();
}
}
}
private void checkClientValidProperty(final String name) throws SQLClientInfoException {
if (name == null
|| (!"ApplicationName".equals(name)
&& !"ClientUser".equals(name)
&& !"ClientHostname".equals(name))) {
Map failures = new HashMap<>();
failures.put(name, ClientInfoStatus.REASON_UNKNOWN_PROPERTY);
throw new SQLClientInfoException(
"setClientInfo() parameters can only be \"ApplicationName\",\"ClientUser\" or \"ClientHostname\", "
+ "but was : "
+ name,
failures);
}
}
private String buildClientQuery(final String name, final String value) {
StringBuilder escapeQuery = new StringBuilder("SET @").append(name).append("=");
if (value == null) {
escapeQuery.append("null");
} else {
escapeQuery.append("'");
int charsOffset = 0;
int charsLength = value.length();
char charValue;
if (protocol.noBackslashEscapes()) {
while (charsOffset < charsLength) {
charValue = value.charAt(charsOffset);
if (charValue == '\'') {
escapeQuery.append('\''); // add a single escape quote
}
escapeQuery.append(charValue);
charsOffset++;
}
} else {
while (charsOffset < charsLength) {
charValue = value.charAt(charsOffset);
if (charValue == '\'' || charValue == '\\' || charValue == '"' || charValue == 0) {
escapeQuery.append('\\'); // add escape slash
}
escapeQuery.append(charValue);
charsOffset++;
}
}
escapeQuery.append("'");
}
return escapeQuery.toString();
}
/**
* Sets the value of the client info property specified by name to the value specified by value.
*
* Applications may use the DatabaseMetaData.getClientInfoProperties
method to
* determine the client info properties supported by the driver and the maximum length that may be
* specified for each property.
*
*
The driver stores the value specified in a suitable location in the database. For example in
* a special register, session parameter, or system table column. For efficiency the driver may
* defer setting the value in the database until the next time a statement is executed or
* prepared. Other than storing the client information in the appropriate place in the database,
* these methods shall not alter the behavior of the connection in anyway. The values supplied to
* these methods are used for accounting, diagnostics and debugging purposes only.
*
*
The driver shall generate a warning if the client info name specified is not recognized by
* the driver.
*
*
If the value specified to this method is greater than the maximum length for the property
* the driver may either truncate the value and generate a warning or generate a
* SQLClientInfoException
. If the driver generates a SQLClientInfoException
,
* the value specified was not set on the connection.
*
*
The following are standard client info properties. Drivers are not required to support these
* properties however if the driver supports a client info property that can be described by one
* of the standard properties, the standard property name should be used.
*
*
* - ApplicationName - The name of the application currently utilizing the connection
*
- ClientUser - The name of the user that the application using the connection is performing
* work for. This may not be the same as the user name that was used in establishing the
* connection.
*
- ClientHostname - The hostname of the computer the application using the connection is
* running on.
*
*
* @param name The name of the client info property to set
* @param value The value to set the client info property to. If the value is null, the current
* value of the specified property is cleared.
* @throws SQLClientInfoException if the database server returns an error while setting the client
* info value on the database server or this method is called on a closed connection
* @since 1.6
*/
public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
checkClientClose(name);
checkClientReconnect(name);
checkClientValidProperty(name);
try {
Statement statement = createStatement();
statement.execute(buildClientQuery(name, value));
} catch (SQLException sqle) {
Map failures = new HashMap<>();
failures.put(name, ClientInfoStatus.REASON_UNKNOWN);
throw new SQLClientInfoException("unexpected error during setClientInfo", failures, sqle);
}
}
/**
* Returns a list containing the name and current value of each client info property supported by
* the driver. The value of a client info property may be null if the property has not been set
* and does not have a default value.
*
* @return A Properties
object that contains the name and current value of each of
* the client info properties supported by the driver.
* @throws SQLException if the database server returns an error when fetching the client info
* values from the database or this method is called on a closed connection
*/
public Properties getClientInfo() throws SQLException {
checkConnection();
Properties properties = new Properties();
try (Statement statement = createStatement()) {
try (ResultSet rs =
statement.executeQuery("SELECT @ApplicationName, @ClientUser, @ClientHostname")) {
if (rs.next()) {
if (rs.getString(1) != null) {
properties.setProperty("ApplicationName", rs.getString(1));
}
if (rs.getString(2) != null) {
properties.setProperty("ClientUser", rs.getString(2));
}
if (rs.getString(3) != null) {
properties.setProperty("ClientHostname", rs.getString(3));
}
return properties;
}
}
}
properties.setProperty("ApplicationName", null);
properties.setProperty("ClientUser", null);
properties.setProperty("ClientHostname", null);
return properties;
}
/**
* Sets the value of the connection's client info properties. The Properties
object
* contains the names and values of the client info properties to be set. The set of client info
* properties contained in the properties list replaces the current set of client info properties
* on the connection. If a property that is currently set on the connection is not present in the
* properties list, that property is cleared. Specifying an empty properties list will clear all
* of the properties on the connection. See setClientInfo (String, String)
for more
* information.
*
* If an error occurs in setting any of the client info properties, a
* SQLClientInfoException
is thrown. The SQLClientInfoException
contains
* information indicating which client info properties were not set. The state of the client
* information is unknown because some databases do not allow multiple client info properties to
* be set atomically. For those databases, one or more properties may have been set before the
* error occurred.
*
* @param properties the list of client info properties to set
* @throws SQLClientInfoException if the database server returns an error while setting the
* clientInfo values on the database server or this method is called on a closed connection
* @see Connection#setClientInfo(String, String) setClientInfo(String, String)
* @since 1.6
*/
public void setClientInfo(final Properties properties) throws SQLClientInfoException {
Map propertiesExceptions = new HashMap<>();
for (String name : new String[] {"ApplicationName", "ClientUser", "ClientHostname"}) {
try {
setClientInfo(name, properties.getProperty(name));
} catch (SQLClientInfoException e) {
propertiesExceptions.putAll(e.getFailedProperties());
}
}
if (!propertiesExceptions.isEmpty()) {
String errorMsg =
"setClientInfo errors : the following properties where not set : "
+ propertiesExceptions.keySet();
throw new SQLClientInfoException(errorMsg, propertiesExceptions);
}
}
/**
* Returns the value of the client info property specified by name. This method may return null if
* the specified client info property has not been set and does not have a default value. This
* method will also return null if the specified client info property name is not supported by the
* driver. Applications may use the DatabaseMetaData.getClientInfoProperties
method
* to determine the client info properties supported by the driver.
*
* @param name The name of the client info property to retrieve
* @return The value of the client info property specified
* @throws SQLException if the database server returns an error when fetching the client info
* value from the database or this method is called on a closed connection
* @see DatabaseMetaData#getClientInfoProperties
* @since 1.6
*/
public String getClientInfo(final String name) throws SQLException {
checkConnection();
if (!"ApplicationName".equals(name)
&& !"ClientUser".equals(name)
&& !"ClientHostname".equals(name)) {
throw new SQLException(
"name must be \"ApplicationName\", \"ClientUser\" or \"ClientHostname\", but was \""
+ name
+ "\"");
}
try (Statement statement = createStatement()) {
try (ResultSet rs = statement.executeQuery("SELECT @" + name)) {
if (rs.next()) {
return rs.getString(1);
}
}
}
return null;
}
/**
* Factory method for creating Array objects. Note: When createArrayOf
is used
* to create an array object that maps to a primitive data type, then it is implementation-defined
* whether the Array
object is an array of that primitive data type or an array of
* Object
. Note: The JDBC driver is responsible for mapping the elements
* Object
array to the default JDBC SQL type defined in java.sql.Types for the given
* class of Object
. The default mapping is specified in Appendix B of the JDBC
* specification. If the resulting JDBC type is not the appropriate type for the given typeName
* then it is implementation defined whether an SQLException
is thrown or the driver
* supports the resulting conversion.
*
* @param typeName the SQL name of the type the elements of the array map to. The typeName is a
* database-specific name which may be the name of a built-in type, a user-defined type or a
* standard SQL type supported by this database. This is the value returned by
* Array.getBaseTypeName
* @param elements the elements that populate the returned object
* @return an Array object whose elements map to the specified SQL type
* @throws SQLException if a database error occurs, the JDBC type is not appropriate for the
* typeName and the conversion is not supported, the typeName is null or this method is called
* on a closed connection
*/
public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
throw exceptionFactory.notSupported("Array type is not supported");
}
/**
* Factory method for creating Struct objects.
*
* @param typeName the SQL type name of the SQL structured type that this Struct
* object maps to. The typeName is the name of a user-defined type that has been defined for
* this database. It is the value returned by Struct.getSQLTypeName
.
* @param attributes the attributes that populate the returned object
* @return a Struct object that maps to the given SQL type and is populated with the given
* attributes
* @throws SQLException if a database error occurs, the typeName is null or this method is called
* on a closed connection
*/
public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
throw exceptionFactory.notSupported("Struct type is not supported");
}
/**
* 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
*/
public T unwrap(final Class iface) throws SQLException {
try {
if (isWrapperFor(iface)) {
return iface.cast(this);
} else {
throw new SQLException("The receiver is not a wrapper for " + iface.getName());
}
} 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 iface a Class defining an interface.
* @return true if this implements the interface or directly or indirectly wraps an object that
* does.
* @since 1.6
*/
public boolean isWrapperFor(final Class> iface) {
return iface.isInstance(this);
}
/**
* returns the username for the connection.
*
* @return the username.
*/
@Deprecated
public String getUsername() {
return protocol.getUsername();
}
/**
* returns the hostname for the connection.
*
* @return the hostname.
*/
@Deprecated
public String getHostname() {
return protocol.getHost();
}
/**
* returns the port for the connection.
*
* @return the port
*/
@Deprecated
public int getPort() {
return protocol.getPort();
}
protected boolean getPinGlobalTxToPhysicalConnection() {
return protocol.getPinGlobalTxToPhysicalConnection();
}
/** If failover is not activated, will close connection when a connection error occur. */
public void setHostFailed() {
if (protocol.getProxy() == null) {
protocol.setHostFailedWithoutProxy();
}
}
/**
* Are table case sensitive or not . Default Value: 0 (Unix), 1 (Windows), 2 (Mac OS X). If set to
* 0 (the default on Unix-based systems), table names and aliases and database names are compared
* in a case-sensitive manner. If set to 1 (the default on Windows), names are stored in lowercase
* and not compared in a case-sensitive manner. If set to 2 (the default on Mac OS X), names are
* stored as declared, but compared in lowercase.
*
* @return int value.
* @throws SQLException if a connection error occur
*/
public int getLowercaseTableNames() throws SQLException {
if (lowercaseTableNames == -1) {
try (Statement st = createStatement()) {
try (ResultSet rs = st.executeQuery("select @@lower_case_table_names")) {
rs.next();
lowercaseTableNames = rs.getInt(1);
}
}
}
return lowercaseTableNames;
}
/**
* Abort connection.
*
* @param executor executor
* @throws SQLException if security manager doesn't permit it.
*/
public void abort(Executor executor) throws SQLException {
if (this.isClosed()) {
return;
}
SQLPermission sqlPermission = new SQLPermission("callAbort");
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(sqlPermission);
}
if (executor == null) {
throw exceptionFactory.create("Cannot abort the connection: null executor passed");
}
executor.execute(protocol::abort);
}
/**
* Get network timeout.
*
* @return timeout
* @throws SQLException if database socket error occur
*/
public int getNetworkTimeout() throws SQLException {
return this.protocol.getTimeout();
}
public String getSchema() {
// We support only catalog
return null;
}
public void setSchema(String arg0) {
// We support only catalog, and JDBC indicate "If the driver does not support schemas, it will
// silently ignore this request."
}
/**
* Change network timeout.
*
* @param executor executor (can be null)
* @param milliseconds network timeout in milliseconds.
* @throws SQLException if security manager doesn't permit it.
*/
public void setNetworkTimeout(Executor executor, final int milliseconds) throws SQLException {
if (this.isClosed()) {
throw exceptionFactory.create(
"Connection.setNetworkTimeout cannot be called on a closed connection");
}
if (milliseconds < 0) {
throw exceptionFactory.create(
"Connection.setNetworkTimeout cannot be called with a negative timeout");
}
SQLPermission sqlPermission = new SQLPermission("setNetworkTimeout");
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(sqlPermission);
}
try {
stateFlag |= ConnectionState.STATE_NETWORK_TIMEOUT;
protocol.setTimeout(milliseconds);
} catch (SocketException se) {
throw exceptionFactory.create("Cannot set the network timeout", se);
}
}
public long getServerThreadId() {
return protocol.getServerThreadId();
}
public boolean canUseServerTimeout() {
return canUseServerTimeout;
}
public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
this.defaultTransactionIsolation = defaultTransactionIsolation;
}
/**
* Reset connection set has it was after creating a "fresh" new connection.
* defaultTransactionIsolation must have been initialized.
*
* BUT : - session variable state are reset only if option useResetConnection is set and - if
* using the option "useServerPrepStmts", PREPARE statement are still prepared
*
* @throws SQLException if resetting operation failed
*/
public void reset() throws SQLException {
// COM_RESET_CONNECTION exist since mysql 5.7.3 and mariadb 10.2.4
// but not possible to use it with mysql waiting for https://bugs.mysql.com/bug.php?id=97633
// correction.
// and mariadb only since https://jira.mariadb.org/browse/MDEV-18281
boolean useComReset =
options.useResetConnection
&& protocol.isServerMariaDb()
&& (protocol.versionGreaterOrEqual(10, 3, 13)
|| (protocol.getMajorServerVersion() == 10
&& protocol.getMinorServerVersion() == 2
&& protocol.versionGreaterOrEqual(10, 2, 22)));
if (useComReset) {
protocol.reset();
}
if (stateFlag != 0) {
try {
if ((stateFlag & ConnectionState.STATE_NETWORK_TIMEOUT) != 0) {
setNetworkTimeout(null, options.socketTimeout);
}
if ((stateFlag & ConnectionState.STATE_AUTOCOMMIT) != 0) {
setAutoCommit(options.autocommit);
}
if ((stateFlag & ConnectionState.STATE_DATABASE) != 0) {
protocol.resetDatabase();
}
if ((stateFlag & ConnectionState.STATE_READ_ONLY) != 0) {
setReadOnly(false); // default to master connection
}
// COM_RESET_CONNECTION reset transaction isolation
if (!useComReset && (stateFlag & ConnectionState.STATE_TRANSACTION_ISOLATION) != 0) {
setTransactionIsolation(defaultTransactionIsolation);
}
stateFlag = 0;
} catch (SQLException sqle) {
throw exceptionFactory.create("error resetting connection");
}
}
warningsCleared = true;
}
public boolean includeDeadLockInfo() {
return options.includeInnodbStatusInDeadlockExceptions;
}
public boolean includeThreadsTraces() {
return options.includeThreadDumpInDeadlockExceptions;
}
}