mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-29 14:04:22 +01:00
Merge 'bindings/java: Implement close() for LimboStatement and LimboResultSet ' from Kim Seon Woo
## Purpose of this PR - Implement `close()` method for `LimboStatement`(+`JDBC4Statement`) and `LimboResultSet`(+ `JDBC4ResultSet`) ## Changes - Add `consumeAll` method in `LimboResultSet` - Implement `close()` methods - Because `JDBC4Statement` has longer lifecycle in compared to `LimboStatement`, we manage different `close` fields(`LimboStatement` is created when first `execute` method is called on `JDBC4Statemenet`) ## Reference - [Issue](https://github.com/tursodatabase/limbo/issues/615) Closes #799
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
@@ -39,6 +40,20 @@ public class LimboResultSet {
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes all the rows in this {@link ResultSet} until the {@link #next()} method returns
|
||||
* `false`.
|
||||
*
|
||||
* @throws SQLException if the result set is not open or if an error occurs while iterating.
|
||||
*/
|
||||
public void consumeAll() throws SQLException {
|
||||
if (!open) {
|
||||
throw new SQLException("The result set is not open");
|
||||
}
|
||||
|
||||
while (next()) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor forward one row from its current position. A {@link LimboResultSet} cursor is
|
||||
* initially positioned before the first fow; the first call to the method <code>next</code> makes
|
||||
@@ -50,7 +65,11 @@ public class LimboResultSet {
|
||||
* cursor can only move forward.
|
||||
*/
|
||||
public boolean next() throws SQLException {
|
||||
if (!open || isEmptyResultSet || pastLastRow) {
|
||||
if (!open) {
|
||||
throw new SQLException("The resultSet is not open");
|
||||
}
|
||||
|
||||
if (isEmptyResultSet || pastLastRow) {
|
||||
return false; // completed ResultSet
|
||||
}
|
||||
|
||||
@@ -70,9 +89,6 @@ public class LimboResultSet {
|
||||
}
|
||||
|
||||
pastLastRow = lastStepResult.isDone();
|
||||
if (pastLastRow) {
|
||||
open = false;
|
||||
}
|
||||
return !pastLastRow;
|
||||
}
|
||||
|
||||
@@ -97,6 +113,10 @@ public class LimboResultSet {
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws SQLException {
|
||||
this.open = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboResultSet{"
|
||||
|
||||
@@ -21,6 +21,8 @@ public class LimboStatement {
|
||||
private final long statementPointer;
|
||||
private final LimboResultSet resultSet;
|
||||
|
||||
private boolean closed;
|
||||
|
||||
// TODO: what if the statement we ran was DDL, update queries and etc. Should we still create a
|
||||
// resultSet?
|
||||
public LimboStatement(String sql, long statementPointer) {
|
||||
@@ -67,6 +69,30 @@ public class LimboStatement {
|
||||
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current statement and releases any resources associated with it. This method calls
|
||||
* the native `_close` method to perform the actual closing operation.
|
||||
*/
|
||||
public void close() throws SQLException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
this.resultSet.close();
|
||||
_close(statementPointer);
|
||||
closed = true;
|
||||
}
|
||||
|
||||
private native void _close(long statementPointer);
|
||||
|
||||
/**
|
||||
* Checks if the statement is closed.
|
||||
*
|
||||
* @return true if the statement is closed, false otherwise.
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboStatement{"
|
||||
|
||||
@@ -25,7 +25,7 @@ public class JDBC4ResultSet implements ResultSet {
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
// TODO
|
||||
resultSet.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -866,8 +866,7 @@ public class JDBC4ResultSet implements ResultSet {
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
return !resultSet.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,8 @@ public class JDBC4Statement implements Statement {
|
||||
private final LimboConnection connection;
|
||||
@Nullable private LimboStatement statement = null;
|
||||
|
||||
// Because JDBC4Statement has different life cycle in compared to LimboStatement, let's use this
|
||||
// field to manage JDBC4Statement lifecycle
|
||||
private boolean closed;
|
||||
private boolean closeOnCompletion;
|
||||
|
||||
@@ -65,9 +67,7 @@ public class JDBC4Statement implements Statement {
|
||||
|
||||
requireNonNull(statement, "statement should not be null after running execute method");
|
||||
final LimboResultSet resultSet = statement.getResultSet();
|
||||
while (resultSet.isOpen()) {
|
||||
resultSet.next();
|
||||
}
|
||||
resultSet.consumeAll();
|
||||
|
||||
// TODO: return update count;
|
||||
return 0;
|
||||
@@ -75,8 +75,14 @@ public class JDBC4Statement implements Statement {
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
clearGeneratedKeys();
|
||||
internalClose();
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.statement != null) {
|
||||
this.statement.close();
|
||||
}
|
||||
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@@ -150,8 +156,7 @@ public class JDBC4Statement implements Statement {
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(String sql) throws SQLException {
|
||||
internalClose();
|
||||
|
||||
ensureOpen();
|
||||
return this.withConnectionTimeout(
|
||||
() -> {
|
||||
try {
|
||||
@@ -298,8 +303,7 @@ public class JDBC4Statement implements Statement {
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
return this.closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,14 +350,6 @@ public class JDBC4Statement implements Statement {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void internalClose() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected void clearGeneratedKeys() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
protected void updateGeneratedKeys() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@@ -378,4 +374,10 @@ public class JDBC4Statement implements Statement {
|
||||
protected interface SQLCallable<T> {
|
||||
T call() throws SQLException;
|
||||
}
|
||||
|
||||
private void ensureOpen() throws SQLException {
|
||||
if (closed) {
|
||||
throw new SQLException("Statement is closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.github.tursodatabase.jdbc4.JDBC4Connection;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class LimboStatementTest {
|
||||
|
||||
private JDBC4Connection connection;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
connection = new JDBC4Connection(url, filePath, new Properties());
|
||||
}
|
||||
|
||||
@Test
|
||||
void closing_statement_closes_related_resources() throws Exception {
|
||||
LimboStatement stmt = connection.prepare("SELECT 1;");
|
||||
stmt.execute();
|
||||
|
||||
stmt.close();
|
||||
assertTrue(stmt.isClosed());
|
||||
assertFalse(stmt.getResultSet().isOpen());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.github.tursodatabase.jdbc4;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
@@ -57,4 +59,24 @@ class JDBC4ResultSetTest {
|
||||
// as well
|
||||
assertFalse(resultSet.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
void close_resultSet_test() throws Exception {
|
||||
stmt.executeQuery("SELECT 1;");
|
||||
ResultSet resultSet = stmt.getResultSet();
|
||||
|
||||
assertFalse(resultSet.isClosed());
|
||||
resultSet.close();
|
||||
assertTrue(resultSet.isClosed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void calling_methods_on_closed_resultSet_should_throw_exception() throws Exception {
|
||||
stmt.executeQuery("SELECT 1;");
|
||||
ResultSet resultSet = stmt.getResultSet();
|
||||
resultSet.close();
|
||||
assertTrue(resultSet.isClosed());
|
||||
|
||||
assertThrows(SQLException.class, resultSet::next);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.github.tursodatabase.jdbc4;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
@@ -51,4 +52,22 @@ class JDBC4StatementTest {
|
||||
stmt.execute("INSERT INTO users VALUES (1, 'limbo');");
|
||||
assertTrue(stmt.execute("SELECT * FROM users;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void close_statement_test() throws Exception {
|
||||
stmt.close();
|
||||
assertTrue(stmt.isClosed());
|
||||
}
|
||||
|
||||
@Test
|
||||
void double_close_is_no_op() throws SQLException {
|
||||
stmt.close();
|
||||
assertDoesNotThrow(() -> stmt.close());
|
||||
}
|
||||
|
||||
@Test
|
||||
void operations_on_closed_statement_should_throw_exception() throws Exception {
|
||||
stmt.close();
|
||||
assertThrows(SQLException.class, () -> stmt.execute("SELECT 1;"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user