diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java
index fd2845a17..033a297c7 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboResultSet.java
@@ -1,5 +1,7 @@
package org.github.tursodatabase.core;
+import org.github.tursodatabase.annotations.Nullable;
+
import java.sql.SQLException;
/**
@@ -21,9 +23,11 @@ public class LimboResultSet {
private long maxRows;
// number of current row, starts at 1 (0 is used to represent loading data)
private int row = 0;
-
private boolean pastLastRow = false;
+ @Nullable
+ private LimboStepResult lastResult;
+
public static LimboResultSet of(LimboStatement statement) {
return new LimboResultSet(statement);
}
@@ -33,6 +37,14 @@ public class LimboResultSet {
this.statement = statement;
}
+ /**
+ * 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 next makes the first row the current row; the second call
+ * makes the second row the current row, and so on.
+ * When a call to the next method returns false, the cursor is positioned after the last row.
+ *
+ * Note that limbo only supports ResultSet.TYPE_FORWARD_ONLY, which means that the cursor can only move forward.
+ */
public boolean next() throws SQLException {
if (!open || isEmptyResultSet || pastLastRow) {
return false; // completed ResultSet
@@ -42,10 +54,9 @@ public class LimboResultSet {
return false;
}
- // TODO
- // int statusCode = this.statement.step();
- this.statement.step();
- return true;
+ lastResult = this.statement.step();
+ pastLastRow = lastResult == null || lastResult.isDone();
+ return !pastLastRow;
}
/**
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStatement.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStatement.java
index 8dc6e3346..0d2c2a169 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStatement.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStatement.java
@@ -25,21 +25,20 @@ public class LimboStatement {
this.resultSet = LimboResultSet.of(this);
}
- public LimboResultSet resultSet() {
+ public LimboResultSet getResultSet() {
return resultSet;
}
public void execute() throws SQLException {
- LimboResultSet result = LimboResultSet.of(this);
-
- // at least, run query minimally
- result.next();
+ resultSet.next();
}
+ @Nullable
public LimboStepResult step() throws SQLException {
return step(this.statementPointer);
}
+ @Nullable
private native LimboStepResult step(long stmtPointer) throws SQLException;
/**
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java
index 5a33920b7..de501da29 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboStepResult.java
@@ -1,5 +1,7 @@
package org.github.tursodatabase.core;
+import java.util.Arrays;
+
import org.github.tursodatabase.annotations.NativeInvocation;
/**
@@ -22,4 +24,16 @@ public class LimboStepResult {
this.stepResultId = stepResultId;
this.result = result;
}
+
+ public boolean isDone() {
+ return stepResultId == STEP_RESULT_ID_DONE;
+ }
+
+ @Override
+ public String toString() {
+ return "LimboStepResult{" +
+ "stepResultId=" + stepResultId +
+ ", result=" + Arrays.toString(result) +
+ '}';
+ }
}
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4ResultSet.java b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4ResultSet.java
index 69b51a8f0..0d7cce084 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4ResultSet.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4ResultSet.java
@@ -21,8 +21,7 @@ public class JDBC4ResultSet implements ResultSet {
@Override
public boolean next() throws SQLException {
- // TODO
- return false;
+ return resultSet.next();
}
@Override
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Statement.java b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Statement.java
index 31726a5d9..03f3864a7 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Statement.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Statement.java
@@ -1,13 +1,19 @@
package org.github.tursodatabase.jdbc4;
+import static java.util.Objects.requireNonNull;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+import java.util.concurrent.locks.ReentrantLock;
+
import org.github.tursodatabase.annotations.Nullable;
import org.github.tursodatabase.annotations.SkipNullableCheck;
import org.github.tursodatabase.core.LimboConnection;
import org.github.tursodatabase.core.LimboStatement;
-import java.sql.*;
-import java.util.concurrent.locks.ReentrantLock;
-
public class JDBC4Statement implements Statement {
private final LimboConnection connection;
@@ -28,10 +34,12 @@ public class JDBC4Statement implements Statement {
private ReentrantLock connectionLock = new ReentrantLock();
public JDBC4Statement(LimboConnection connection) {
- this(connection, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
+ this(connection, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
+ ResultSet.CLOSE_CURSORS_AT_COMMIT);
}
- public JDBC4Statement(LimboConnection connection, int resultSetType, int resultSetConcurrency, int resultSetHoldability) {
+ public JDBC4Statement(LimboConnection connection, int resultSetType, int resultSetConcurrency,
+ int resultSetHoldability) {
this.connection = connection;
this.resultSetType = resultSetType;
this.resultSetConcurrency = resultSetConcurrency;
@@ -121,6 +129,17 @@ public class JDBC4Statement implements Statement {
// TODO
}
+ /**
+ * The execute method executes an SQL statement and indicates the
+ * form of the first result. You must then use the methods
+ * getResultSet or getUpdateCount
+ * to retrieve the result, and getMoreResults to
+ * move to any subsequent result(s).
+ *
+ * @return true if the first result is a ResultSet
+ * object; false if it is an update count or there are
+ * no results
+ */
@Override
public boolean execute(String sql) throws SQLException {
internalClose();
@@ -133,8 +152,8 @@ public class JDBC4Statement implements Statement {
statement.execute();
updateGeneratedKeys();
exhaustedResults = false;
+ // TODO: determine whether
return true;
- // return !result.isEmpty();
} finally {
connectionLock.unlock();
}
@@ -143,10 +162,9 @@ public class JDBC4Statement implements Statement {
}
@Override
- @SkipNullableCheck
public ResultSet getResultSet() throws SQLException {
- // TODO
- return null;
+ requireNonNull(statement, "statement is null");
+ return new JDBC4ResultSet(statement.getResultSet());
}
@Override
@@ -289,7 +307,7 @@ public class JDBC4Statement implements Statement {
@Override
public void closeOnCompletion() throws SQLException {
- if (closed) throw new SQLException("statement is closed");
+ if (closed) {throw new SQLException("statement is closed");}
closeOnCompletion = true;
}
@@ -298,7 +316,7 @@ public class JDBC4Statement implements Statement {
*/
@Override
public boolean isCloseOnCompletion() throws SQLException {
- if (closed) throw new SQLException("statement is closed");
+ if (closed) {throw new SQLException("statement is closed");}
return closeOnCompletion;
}
diff --git a/bindings/java/src/main/java/org/github/tursodatabase/utils/LimboExceptionUtils.java b/bindings/java/src/main/java/org/github/tursodatabase/utils/LimboExceptionUtils.java
index 9a45db040..08e8f5b5e 100644
--- a/bindings/java/src/main/java/org/github/tursodatabase/utils/LimboExceptionUtils.java
+++ b/bindings/java/src/main/java/org/github/tursodatabase/utils/LimboExceptionUtils.java
@@ -12,7 +12,7 @@ public class LimboExceptionUtils {
/**
* Throws formatted SQLException with error code and message.
*
- * @param errorCode Error code.
+ * @param errorCode Error code.
* @param errorMessageBytes Error message.
*/
public static void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
@@ -23,10 +23,11 @@ public class LimboExceptionUtils {
/**
* Throws formatted SQLException with error code and message.
*
- * @param errorCode Error code.
+ * @param errorCode Error code.
* @param errorMessage Error message.
*/
- public static LimboException buildLimboException(int errorCode, @Nullable String errorMessage) throws SQLException {
+ public static LimboException buildLimboException(int errorCode, @Nullable String errorMessage)
+ throws SQLException {
LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode);
String msg;
if (code == LimboErrorCode.UNKNOWN_ERROR) {
@@ -37,4 +38,18 @@ public class LimboExceptionUtils {
return new LimboException(msg, code);
}
+
+ /**
+ * Ensures that the provided object is not null.
+ *
+ * @param object the object to check for nullity
+ * @param message the message to include in the exception if the object is null
+ *
+ * @throws IllegalArgumentException if the provided object is null
+ */
+ public static void requireNonNull(Object object, String message) {
+ if (object == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
}
diff --git a/bindings/java/src/test/java/org/github/tursodatabase/IntegrationTest.java b/bindings/java/src/test/java/org/github/tursodatabase/IntegrationTest.java
index 873c41476..be25ffdff 100644
--- a/bindings/java/src/test/java/org/github/tursodatabase/IntegrationTest.java
+++ b/bindings/java/src/test/java/org/github/tursodatabase/IntegrationTest.java
@@ -22,7 +22,6 @@ public class IntegrationTest {
}
@Test
- @Disabled("Doesn't work on workflow. Need investigation.")
void create_table_multi_inserts_select() throws Exception {
Statement stmt = createDefaultStatement();
stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
diff --git a/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java
new file mode 100644
index 000000000..d32ed2731
--- /dev/null
+++ b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ResultSetTest.java
@@ -0,0 +1,58 @@
+package org.github.tursodatabase.jdbc4;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Properties;
+
+import org.github.tursodatabase.TestUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class JDBC4ResultSetTest {
+
+ private Statement stmt;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ String filePath = TestUtils.createTempFile();
+ String url = "jdbc:sqlite:" + filePath;
+ final JDBC4Connection connection = new JDBC4Connection(url, filePath, new Properties());
+ stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY,
+ ResultSet.CONCUR_READ_ONLY,
+ ResultSet.CLOSE_CURSORS_AT_COMMIT);
+ }
+
+ @Test
+ void invoking_next_before_the_last_row_should_return_true() throws Exception {
+ stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
+ stmt.execute("INSERT INTO users VALUES (1, 'sinwoo');");
+ stmt.execute("INSERT INTO users VALUES (2, 'seonwoo');");
+
+ // first call to next occur internally
+ stmt.execute("SELECT * FROM users");
+ ResultSet resultSet = stmt.getResultSet();
+
+ assertTrue(resultSet.next());
+ }
+
+ @Test
+ void invoking_next_after_the_last_row_should_return_false() throws Exception {
+ stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
+ stmt.execute("INSERT INTO users VALUES (1, 'sinwoo');");
+ stmt.execute("INSERT INTO users VALUES (2, 'seonwoo');");
+
+ // first call to next occur internally
+ stmt.execute("SELECT * FROM users");
+ ResultSet resultSet = stmt.getResultSet();
+
+ while (resultSet.next()) {
+ // this loop will break when resultSet returns false
+ }
+
+ // if the previous call to next() returned false, consecutive call to next() should return false as well
+ assertFalse(resultSet.next());
+ }
+}