Skip to content

Support Statement.closeOnCompletion. #185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/main/java/org/tarantool/jdbc/SQLDatabaseMetadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -1126,8 +1126,8 @@ private SQLNullResultSet sqlNullResultSet(List<String> columnNames, List<List<Ob
return new SQLNullResultSet(SQLResultHolder.ofQuery(meta, rows), createMetadataStatement());
}

private SQLStatement createMetadataStatement() throws SQLException {
return connection.createStatement().unwrap(SQLStatement.class);
private TarantoolStatement createMetadataStatement() throws SQLException {
return connection.createStatement().unwrap(TarantoolStatement.class);
}

private static <T> T ensureType(Class<T> cls, Object v) throws Exception {
Expand All @@ -1152,7 +1152,7 @@ private SQLNullResultSet emptyResultSet(List<String> colNames) throws SQLExcepti

protected class SQLNullResultSet extends SQLResultSet {

public SQLNullResultSet(SQLResultHolder holder, SQLStatement ownerStatement) throws SQLException {
public SQLNullResultSet(SQLResultHolder holder, TarantoolStatement ownerStatement) throws SQLException {
super(holder, ownerStatement);
}

Expand Down
20 changes: 14 additions & 6 deletions src/main/java/org/tarantool/jdbc/SQLResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@

public class SQLResultSet implements ResultSet {

private final CursorIterator<List<Object>> iterator;
private final SQLResultSetMetaData metaData;
private CursorIterator<List<Object>> iterator;
private SQLResultSetMetaData metaData;

private Map<String, Integer> columnByNameLookups;
private boolean lastColumnWasNull;

private final Statement statement;
private final TarantoolStatement statement;
private final int maxRows;

private AtomicBoolean isClosed = new AtomicBoolean(false);
Expand All @@ -53,7 +53,7 @@ public class SQLResultSet implements ResultSet {
private final int concurrencyLevel;
private final int holdability;

public SQLResultSet(SQLResultHolder holder, SQLStatement ownerStatement) throws SQLException {
public SQLResultSet(SQLResultHolder holder, TarantoolStatement ownerStatement) throws SQLException {
metaData = new SQLResultSetMetaData(holder.getSqlMetadata());
statement = ownerStatement;
scrollType = statement.getResultSetType();
Expand Down Expand Up @@ -108,7 +108,13 @@ protected Number getNullableNumber(int columnIndex) throws SQLException {
@Override
public void close() throws SQLException {
if (isClosed.compareAndSet(false, true)) {
iterator.close();
try {
iterator.close();
iterator = null;
metaData = null;
} finally {
statement.checkCompletion();
}
}
}

Expand Down Expand Up @@ -393,11 +399,13 @@ public String getCursorName() throws SQLException {

@Override
public ResultSetMetaData getMetaData() throws SQLException {
checkNotClosed();
return metaData;
}

@Override
public int findColumn(String columnLabel) throws SQLException {
checkNotClosed();
return findColumnIndex(columnLabel);
}

Expand Down Expand Up @@ -1122,7 +1130,7 @@ public <T> T unwrap(Class<T> type) throws SQLException {
if (isWrapperFor(type)) {
return type.cast(this);
}
throw new SQLNonTransientException("ResultSet does not wrap " + type.getName());
throw new SQLNonTransientException("SQLResultSet does not wrap " + type.getName());
}

@Override
Expand Down
51 changes: 34 additions & 17 deletions src/main/java/org/tarantool/jdbc/SQLStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* types of cursors.
* Supports only {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} holdability type.
*/
public class SQLStatement implements Statement {
public class SQLStatement implements TarantoolStatement {

protected final SQLConnection connection;

Expand All @@ -31,6 +31,8 @@ public class SQLStatement implements Statement {
protected SQLResultSet resultSet;
protected int updateCount;

private boolean isCloseOnCompletion;

private final int resultSetType;
private final int resultSetConcurrency;
private final int resultSetHoldability;
Expand Down Expand Up @@ -310,23 +312,44 @@ public boolean isPoolable() throws SQLException {
throw new SQLFeatureNotSupportedException();
}

/**
* {@inheritDoc}
* <p>
* <strong>Impl Note:</strong> this method doesn't affect
* execution methods which close the last result set implicitly.
* It is applied only when {@link ResultSet#close()} is invoked
* explicitly by the app.
*
* @throws SQLException if this method is called on a closed
* {@code Statement}
*/
@Override
public void closeOnCompletion() throws SQLException {

checkNotClosed();
isCloseOnCompletion = true;
}

@Override
public boolean isCloseOnCompletion() throws SQLException {
checkNotClosed();
return false;
return isCloseOnCompletion;
}

@Override
public void checkCompletion() throws SQLException {
if (isCloseOnCompletion &&
resultSet != null &&
resultSet.isClosed()) {
close();
}
}

@Override
public <T> T unwrap(Class<T> type) throws SQLException {
if (isWrapperFor(type)) {
return type.cast(this);
}
throw new SQLNonTransientException("Statement does not wrap " + type.getName());
throw new SQLNonTransientException("SQLStatement does not wrap " + type.getName());
}

@Override
Expand All @@ -338,15 +361,18 @@ public boolean isWrapperFor(Class<?> type) throws SQLException {
* Clears the results of the most recent execution.
*/
protected void discardLastResults() throws SQLException {
final SQLResultSet lastResultSet = resultSet;

clearWarnings();
updateCount = -1;
if (resultSet != null) {
resultSet = null;

if (lastResultSet != null) {
try {
resultSet.close();
lastResultSet.close();
} catch (Exception ignored) {
// No-op.
}
resultSet = null;
}
}

Expand Down Expand Up @@ -375,16 +401,7 @@ protected boolean executeInternal(String sql, Object... params) throws SQLExcept
return holder.isQueryResult();
}

/**
* Returns {@link ResultSet} which will be initialized by <code>data</code>.
*
* @param data predefined result to be wrapped by {@link ResultSet}
*
* @return wrapped result
*
* @throws SQLException if a database access error occurs or
* this method is called on a closed <code>Statement</code>
*/
@Override
public ResultSet executeMetadata(SQLResultHolder data) throws SQLException {
checkNotClosed();
return createResultSet(data);
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/tarantool/jdbc/TarantoolStatement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.tarantool.jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
* Tarantool specific statement extensions.
*/
public interface TarantoolStatement extends Statement {

/**
* Checks for statement completion and closes itself,
* according to {@link Statement#closeOnCompletion()}.
*/
void checkCompletion() throws SQLException;

/**
* Returns {@link ResultSet} which will be initialized by <code>data</code>.
*
* @param data predefined result to be wrapped by {@link ResultSet}
*
* @return wrapped result
*
* @throws SQLException if a database access error occurs or
* this method is called on a closed <code>Statement</code>
*/
ResultSet executeMetadata(SQLResultHolder data) throws SQLException;

}
6 changes: 5 additions & 1 deletion src/test/java/org/tarantool/jdbc/JdbcResultSetIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,12 @@ public void testResultSetMetadataAfterClose() throws SQLException {
assertNotNull(resultSet);
ResultSetMetaData metaData = resultSet.getMetaData();
assertNotNull(metaData);

int expectedColumnSize = 2;
assertEquals(expectedColumnSize, metaData.getColumnCount());

resultSet.close();
assertEquals(metaData, resultSet.getMetaData());
assertEquals(expectedColumnSize, metaData.getColumnCount());
}

@Test
Expand Down
101 changes: 101 additions & 0 deletions src/test/java/org/tarantool/jdbc/JdbcStatementIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,14 @@ public void execute() throws Throwable {

@Test
public void testUnwrap() throws SQLException {
assertEquals(stmt, stmt.unwrap(TarantoolStatement.class));
assertEquals(stmt, stmt.unwrap(SQLStatement.class));
assertThrows(SQLException.class, () -> stmt.unwrap(Integer.class));
}

@Test
public void testIsWrapperFor() throws SQLException {
assertTrue(stmt.isWrapperFor(TarantoolStatement.class));
assertTrue(stmt.isWrapperFor(SQLStatement.class));
assertFalse(stmt.isWrapperFor(Integer.class));
}
Expand Down Expand Up @@ -247,4 +249,103 @@ void testStatementConnection() throws SQLException {
Statement statement = conn.createStatement();
assertEquals(conn, statement.getConnection());
}

@Test
void testCloseOnCompletion() throws SQLException {
assertFalse(stmt.isCloseOnCompletion());
stmt.closeOnCompletion();
assertTrue(stmt.isCloseOnCompletion());
}

@Test
void testCloseOnCompletionDisabled() throws SQLException {
ResultSet resultSet = stmt.executeQuery("SELECT val FROM test WHERE id=1");
assertFalse(stmt.isClosed());
assertFalse(resultSet.isClosed());

resultSet.close();
assertTrue(resultSet.isClosed());
assertFalse(stmt.isClosed());
}

@Test
void testCloseOnCompletionEnabled() throws SQLException {
stmt.closeOnCompletion();
ResultSet resultSet = stmt.executeQuery("SELECT val FROM test WHERE id=1");

assertFalse(stmt.isClosed());
assertFalse(resultSet.isClosed());

resultSet.close();
assertTrue(resultSet.isClosed());
assertTrue(stmt.isClosed());
}

@Test
void testCloseOnCompletionAfterResultSet() throws SQLException {
ResultSet resultSet = stmt.executeQuery("SELECT val FROM test WHERE id=1");
stmt.closeOnCompletion();

assertFalse(stmt.isClosed());
assertFalse(resultSet.isClosed());

resultSet.close();
assertTrue(resultSet.isClosed());
assertTrue(stmt.isClosed());
}

@Test
void testCloseOnCompletionMultipleResultSets() throws SQLException {
stmt.closeOnCompletion();
ResultSet resultSet = stmt.executeQuery("SELECT val FROM test WHERE id=1");
ResultSet anotherResultSet = stmt.executeQuery("SELECT val FROM test WHERE id=2");

assertTrue(resultSet.isClosed());
assertFalse(anotherResultSet.isClosed());
assertFalse(stmt.isClosed());

anotherResultSet.close();
assertTrue(anotherResultSet.isClosed());
assertTrue(stmt.isClosed());
}

@Test
void testCloseOnCompletionUpdateQueries() throws SQLException {
stmt.closeOnCompletion();

int updateCount = stmt.executeUpdate("INSERT INTO test(id, val) VALUES (5, 'five')");
assertEquals(1, updateCount);
assertFalse(stmt.isClosed());

updateCount = stmt.executeUpdate("INSERT INTO test(id, val) VALUES (6, 'six')");
assertEquals(1, updateCount);
assertFalse(stmt.isClosed());
}

@Test
void testCloseOnCompletionMixedQueries() throws SQLException {
stmt.closeOnCompletion();

int updateCount = stmt.executeUpdate("INSERT INTO test(id, val) VALUES (7, 'seven')");
assertEquals(1, updateCount);
assertFalse(stmt.isClosed());

ResultSet resultSet = stmt.executeQuery("SELECT val FROM test WHERE id=7");
assertFalse(resultSet.isClosed());
assertFalse(stmt.isClosed());

updateCount = stmt.executeUpdate("INSERT INTO test(id, val) VALUES (8, 'eight')");
assertEquals(1, updateCount);
assertTrue(resultSet.isClosed());
assertFalse(stmt.isClosed());

resultSet = stmt.executeQuery("SELECT val FROM test WHERE id=8");
assertFalse(resultSet.isClosed());
assertFalse(stmt.isClosed());

resultSet.close();
assertTrue(resultSet.isClosed());
assertTrue(stmt.isClosed());
}

}