diff --git a/src/main/java/com/aerospike/jdbc/AerospikeConnection.java b/src/main/java/com/aerospike/jdbc/AerospikeConnection.java index a49625c..16c29e0 100644 --- a/src/main/java/com/aerospike/jdbc/AerospikeConnection.java +++ b/src/main/java/com/aerospike/jdbc/AerospikeConnection.java @@ -10,6 +10,7 @@ import com.aerospike.jdbc.util.DatabaseMetadataBuilder; import java.sql.*; +import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -28,7 +29,7 @@ public class AerospikeConnection implements Connection, SimpleWrapper { private static final Logger logger = Logger.getLogger(AerospikeConnection.class.getName()); - private static final String NOT_TRANSACTIONAL_MESSAGE = "Connection is not transactional"; + private static final String SAVEPOINT_NOT_SUPPORTED_MESSAGE = "Savepoint is not supported"; private final String url; private final DriverConfiguration config; @@ -69,22 +70,26 @@ public CallableStatement prepareCall(String sql) throws SQLException { @Override public String nativeSQL(String sql) throws SQLException { - throw new SQLFeatureNotSupportedException("nativeSQL is not supported"); + checkClosed(); + return sql; } @Override - public boolean getAutoCommit() { + public boolean getAutoCommit() throws SQLException { + checkClosed(); return true; } @Override - public void setAutoCommit(boolean autoCommit) { - // do nothing + public void setAutoCommit(boolean autoCommit) throws SQLException { + checkClosed(); + // no-op } @Override - public void commit() { - // do nothing + public void commit() throws SQLException { + checkClosed(); + // no-op } @Override @@ -106,40 +111,44 @@ public boolean isClosed() { @Override public DatabaseMetaData getMetaData() throws SQLException { + checkClosed(); logger.fine(() -> "getMetaData request"); return metadataBuilder.build(url, this); } @Override - public boolean isReadOnly() { + public boolean isReadOnly() throws SQLException { + checkClosed(); return readOnly; } @Override public void setReadOnly(boolean readOnly) throws SQLException { - if (!isValid(1)) { - throw new SQLException("Cannot set read only mode on closed connection"); - } + checkClosed(); this.readOnly = readOnly; } @Override - public String getCatalog() { + public String getCatalog() throws SQLException { + checkClosed(); return catalog.get(); } @Override - public void setCatalog(String catalog) { + public void setCatalog(String catalog) throws SQLException { + checkClosed(); this.catalog.set(catalog); } @Override - public int getTransactionIsolation() { + public int getTransactionIsolation() throws SQLException { + checkClosed(); return TRANSACTION_NONE; } @Override public void setTransactionIsolation(int level) throws SQLException { + checkClosed(); if (level != TRANSACTION_NONE) { throw new SQLFeatureNotSupportedException(format("Aerospike does not support transactions," + " so the only valid value here is TRANSACTION_NONE=%d", TRANSACTION_NONE)); @@ -147,13 +156,15 @@ public void setTransactionIsolation(int level) throws SQLException { } @Override - public SQLWarning getWarnings() { + public SQLWarning getWarnings() throws SQLException { + checkClosed(); return null; } @Override - public void clearWarnings() { - // do nothing + public void clearWarnings() throws SQLException { + checkClosed(); + // no-op } @Override @@ -173,26 +184,27 @@ public CallableStatement prepareCall(String sql, int resultSetType, int resultSe } @Override - public Map> getTypeMap() { + public Map> getTypeMap() throws SQLException { + checkClosed(); return typeMap; } @Override - public void setTypeMap(Map> map) { + public void setTypeMap(Map> map) throws SQLException { + checkClosed(); typeMap = map; } @Override - public int getHoldability() { + public int getHoldability() throws SQLException { + checkClosed(); return holdability; } @Override public void setHoldability(int holdability) throws SQLException { - if (isClosed()) { - throw new SQLException("Cannot set holdability on closed connection"); - } - if (!(holdability == HOLD_CURSORS_OVER_COMMIT || holdability == CLOSE_CURSORS_AT_COMMIT)) { + checkClosed(); + if (holdability != HOLD_CURSORS_OVER_COMMIT && holdability != CLOSE_CURSORS_AT_COMMIT) { throw new SQLException(format( "Unsupported holdability %d. Must be either HOLD_CURSORS_OVER_COMMIT=%d or CLOSE_CURSORS_AT_COMMIT=%d", holdability, @@ -205,27 +217,28 @@ public void setHoldability(int holdability) throws SQLException { @Override public Savepoint setSavepoint() throws SQLException { - throw new SQLFeatureNotSupportedException(NOT_TRANSACTIONAL_MESSAGE); + throw new SQLFeatureNotSupportedException(SAVEPOINT_NOT_SUPPORTED_MESSAGE); } @Override public Savepoint setSavepoint(String name) throws SQLException { - throw new SQLFeatureNotSupportedException(NOT_TRANSACTIONAL_MESSAGE); + throw new SQLFeatureNotSupportedException(SAVEPOINT_NOT_SUPPORTED_MESSAGE); } @Override public void rollback(Savepoint savepoint) throws SQLException { - throw new SQLFeatureNotSupportedException(NOT_TRANSACTIONAL_MESSAGE); + throw new SQLFeatureNotSupportedException(SAVEPOINT_NOT_SUPPORTED_MESSAGE); } @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { - throw new SQLFeatureNotSupportedException(NOT_TRANSACTIONAL_MESSAGE); + throw new SQLFeatureNotSupportedException(SAVEPOINT_NOT_SUPPORTED_MESSAGE); } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + checkClosed(); validateResultSetParameters(resultSetType, resultSetConcurrency, resultSetHoldability); return new AerospikeStatement(client, this); } @@ -233,6 +246,7 @@ public Statement createStatement(int resultSetType, int resultSetConcurrency, in @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + checkClosed(); validateResultSetParameters(resultSetType, resultSetConcurrency, resultSetHoldability); return new AerospikePreparedStatement(client, this, sql); } @@ -245,7 +259,7 @@ private void validateResultSetParameters(int resultSetType, int resultSetConcurr if (resultSetConcurrency != CONCUR_READ_ONLY) { throw new SQLFeatureNotSupportedException("Updatable ResultSet is not supported yet"); } - if (!(resultSetHoldability == HOLD_CURSORS_OVER_COMMIT || resultSetHoldability == CLOSE_CURSORS_AT_COMMIT)) { + if (resultSetHoldability != HOLD_CURSORS_OVER_COMMIT && resultSetHoldability != CLOSE_CURSORS_AT_COMMIT) { throw new SQLException(format("Wrong value for the resultSetHoldability (%d). Supported values are: " + "HOLD_CURSORS_OVER_COMMIT=%d or CLOSE_CURSORS_AT_COMMIT=%d", resultSetHoldability, HOLD_CURSORS_OVER_COMMIT, CLOSE_CURSORS_AT_COMMIT)); @@ -274,17 +288,19 @@ public PreparedStatement prepareStatement(String sql, String[] columnNames) thro } @Override - public Clob createClob() { - return new StringClob(); + public Clob createClob() throws SQLException { + return createNClob(); } @Override - public Blob createBlob() { + public Blob createBlob() throws SQLException { + checkClosed(); return new ByteArrayBlob(); } @Override - public NClob createNClob() { + public NClob createNClob() throws SQLException { + checkClosed(); return new StringClob(); } @@ -295,27 +311,45 @@ public SQLXML createSQLXML() throws SQLException { @Override public boolean isValid(int timeout) { - return client.isConnected() && Objects.nonNull(client.getClusterStats()); + return !isClosed() && client.isConnected() && Objects.nonNull(client.getClusterStats()); } @Override - public void setClientInfo(String name, String value) { + public void setClientInfo(String name, String value) throws SQLClientInfoException { + try { + checkClosed(); + } catch (final SQLException cause) { + Map failedProperties = new HashMap<>(); + failedProperties.put(name, ClientInfoStatus.REASON_UNKNOWN); + throw new SQLClientInfoException(failedProperties, cause); + } logger.info(() -> format("Set client info: %s -> %s", name, value)); config.put(name, value); } @Override - public String getClientInfo(String name) { + public String getClientInfo(String name) throws SQLException { + checkClosed(); return config.getClientInfo().getProperty(name); } @Override - public Properties getClientInfo() { + public Properties getClientInfo() throws SQLException { + checkClosed(); return config.getClientInfo(); } @Override - public void setClientInfo(Properties properties) { + public void setClientInfo(Properties properties) throws SQLClientInfoException { + try { + checkClosed(); + } catch (final SQLException cause) { + Map failedProperties = new HashMap<>(); + for (Object key : properties.keySet()) { + failedProperties.put((String) key, ClientInfoStatus.REASON_UNKNOWN); + } + throw new SQLClientInfoException(failedProperties, cause); + } logger.info(() -> format("Set client info: %s", properties)); config.putAll(properties); } @@ -331,22 +365,33 @@ public Struct createStruct(String typeName, Object[] attributes) throws SQLExcep } @Override - public String getSchema() { + @SuppressWarnings("java:S4144") + public String getSchema() throws SQLException { + checkClosed(); return null; } @Override - public void setSchema(String schema) { - // do nothing + public void setSchema(String schema) throws SQLException { + checkClosed(); + // no-op } @Override - public void abort(Executor executor) { + public void abort(Executor executor) throws SQLException { + if (executor == null) { + throw new SQLException("executor is null"); + } executor.execute(this::close); } @Override - public void setNetworkTimeout(Executor executor, int milliseconds) { + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + checkClosed(); + if (milliseconds < 0) { + throw new SQLException("Timeout must be a non-negative value in milliseconds"); + } + stream(new Policy[]{ client.getReadPolicyDefault(), client.getWritePolicyDefault(), @@ -361,10 +406,17 @@ public void setNetworkTimeout(Executor executor, int milliseconds) { } @Override - public int getNetworkTimeout() { + public int getNetworkTimeout() throws SQLException { + checkClosed(); return client.getReadPolicyDefault().totalTimeout; } + protected void checkClosed() throws SQLException { + if (isClosed()) { + throw new SQLException("Connection is closed"); + } + } + public DriverConfiguration getConfiguration() { return config; } diff --git a/src/main/java/com/aerospike/jdbc/AerospikeDatabaseMetadata.java b/src/main/java/com/aerospike/jdbc/AerospikeDatabaseMetadata.java index bdd60a4..9416b60 100644 --- a/src/main/java/com/aerospike/jdbc/AerospikeDatabaseMetadata.java +++ b/src/main/java/com/aerospike/jdbc/AerospikeDatabaseMetadata.java @@ -114,7 +114,7 @@ public String getURL() { } @Override - public String getUserName() { + public String getUserName() throws SQLException { return connection.getClientInfo().getProperty("user"); } diff --git a/src/main/java/com/aerospike/jdbc/AerospikePreparedStatement.java b/src/main/java/com/aerospike/jdbc/AerospikePreparedStatement.java index e53c4ce..d21fb9c 100644 --- a/src/main/java/com/aerospike/jdbc/AerospikePreparedStatement.java +++ b/src/main/java/com/aerospike/jdbc/AerospikePreparedStatement.java @@ -35,7 +35,8 @@ public class AerospikePreparedStatement extends AerospikeStatement implements Pr private final String sqlStatement; private final Object[] sqlParameters; - public AerospikePreparedStatement(IAerospikeClient client, AerospikeConnection connection, String sqlStatement) { + public AerospikePreparedStatement(IAerospikeClient client, AerospikeConnection connection, + String sqlStatement) throws SQLException { super(client, connection); this.sqlStatement = sqlStatement; sqlParameters = buildSqlParameters(sqlStatement); diff --git a/src/main/java/com/aerospike/jdbc/AerospikeStatement.java b/src/main/java/com/aerospike/jdbc/AerospikeStatement.java index 3e84aa5..4b50e99 100644 --- a/src/main/java/com/aerospike/jdbc/AerospikeStatement.java +++ b/src/main/java/com/aerospike/jdbc/AerospikeStatement.java @@ -43,7 +43,7 @@ public class AerospikeStatement implements Statement, SimpleWrapper { private int maxRows = Integer.MAX_VALUE; private int queryTimeout; - public AerospikeStatement(IAerospikeClient client, AerospikeConnection connection) { + public AerospikeStatement(IAerospikeClient client, AerospikeConnection connection) throws SQLException { this.client = client; this.connection = connection; this.catalog = connection.getCatalog(); @@ -112,7 +112,7 @@ public void setMaxRows(int max) { @Override public void setEscapeProcessing(boolean enable) { - // do nothing + // no-op } @Override @@ -137,7 +137,7 @@ public SQLWarning getWarnings() { @Override public void clearWarnings() { - // do nothing + // no-op } @Override @@ -190,7 +190,7 @@ public int getFetchSize() { @Override public void setFetchSize(int rows) { - // do nothing supported size = 1. + // no-op; supported size = 1. } @Override @@ -305,7 +305,7 @@ public void setPoolable(boolean poolable) throws SQLException { @Override public void closeOnCompletion() { - // do nothing + // no-op } @Override diff --git a/src/main/java/com/aerospike/jdbc/sql/BaseResultSet.java b/src/main/java/com/aerospike/jdbc/sql/BaseResultSet.java index 396b3e4..34baf63 100644 --- a/src/main/java/com/aerospike/jdbc/sql/BaseResultSet.java +++ b/src/main/java/com/aerospike/jdbc/sql/BaseResultSet.java @@ -85,7 +85,7 @@ public SQLWarning getWarnings() { @Override public void clearWarnings() { - // do nothing + // no-op } @Override