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 033a297c7..ebcbb8860 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,9 +1,9 @@
package org.github.tursodatabase.core;
-import org.github.tursodatabase.annotations.Nullable;
-
import java.sql.SQLException;
+import org.github.tursodatabase.annotations.Nullable;
+
/**
* A table of data representing limbo database result set, which is generated by executing a statement that queries the
* database.
@@ -26,7 +26,7 @@ public class LimboResultSet {
private boolean pastLastRow = false;
@Nullable
- private LimboStepResult lastResult;
+ private LimboStepResult lastStepResult;
public static LimboResultSet of(LimboStatement statement) {
return new LimboResultSet(statement);
@@ -54,11 +54,18 @@ public class LimboResultSet {
return false;
}
- lastResult = this.statement.step();
- pastLastRow = lastResult == null || lastResult.isDone();
+ lastStepResult = this.statement.step();
+ pastLastRow = lastStepResult == null || lastStepResult.isDone();
return !pastLastRow;
}
+ /**
+ * Checks whether the last step result has returned row result.
+ */
+ public boolean hasLastStepReturnedRow() {
+ return lastStepResult != null && lastStepResult.isRow();
+ }
+
/**
* Checks the status of the result set.
*
@@ -76,4 +83,17 @@ public class LimboResultSet {
throw new SQLException("ResultSet closed");
}
}
+
+ @Override
+ public String toString() {
+ return "LimboResultSet{" +
+ "statement=" + statement +
+ ", isEmptyResultSet=" + isEmptyResultSet +
+ ", open=" + open +
+ ", maxRows=" + maxRows +
+ ", row=" + row +
+ ", pastLastRow=" + pastLastRow +
+ ", lastResult=" + lastStepResult +
+ '}';
+ }
}
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 0d2c2a169..1cad05d14 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
@@ -1,11 +1,11 @@
package org.github.tursodatabase.core;
+import java.sql.SQLException;
+
import org.github.tursodatabase.annotations.NativeInvocation;
import org.github.tursodatabase.annotations.Nullable;
import org.github.tursodatabase.utils.LimboExceptionUtils;
-import java.sql.SQLException;
-
/**
* By default, only one resultSet object per LimboStatement can be open at the same time.
* Therefore, if the reading of one resultSet object is interleaved with the reading of another, each must
@@ -13,14 +13,13 @@ import java.sql.SQLException;
* implicitly close the current resultSet object of the statement if an open one exists.
*/
public class LimboStatement {
-
+ private final String sql;
private final long statementPointer;
private final LimboResultSet resultSet;
- @Nullable
- protected String sql = null;
-
- public LimboStatement(long statementPointer) {
+ // 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) {
+ this.sql = sql;
this.statementPointer = statementPointer;
this.resultSet = LimboResultSet.of(this);
}
@@ -29,12 +28,18 @@ public class LimboStatement {
return resultSet;
}
- public void execute() throws SQLException {
+ /**
+ * Expects a clean statement created right after prepare method is called.
+ *
+ * @return true if the ResultSet has at least one row; false otherwise.
+ */
+ public boolean execute() throws SQLException {
resultSet.next();
+ return resultSet.hasLastStepReturnedRow();
}
@Nullable
- public LimboStepResult step() throws SQLException {
+ LimboStepResult step() throws SQLException {
return step(this.statementPointer);
}
@@ -44,11 +49,19 @@ public class LimboStatement {
/**
* Throws formatted SQLException with error code and message.
*
- * @param errorCode Error code.
+ * @param errorCode Error code.
* @param errorMessageBytes Error message.
*/
- @NativeInvocation
+ @NativeInvocation(invokedFrom = "limbo_statement.rs")
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
}
+
+ @Override
+ public String toString() {
+ return "LimboStatement{" +
+ "statementPointer=" + statementPointer +
+ ", sql='" + sql + '\'' +
+ '}';
+ }
}
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 de501da29..71e343d90 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
@@ -8,23 +8,27 @@ import org.github.tursodatabase.annotations.NativeInvocation;
* Represents the step result of limbo's statement's step function.
*/
public class LimboStepResult {
- public static final int STEP_RESULT_ID_ROW = 10;
- public static final int STEP_RESULT_ID_IO = 20;
- public static final int STEP_RESULT_ID_DONE = 30;
- public static final int STEP_RESULT_ID_INTERRUPT = 40;
- public static final int STEP_RESULT_ID_BUSY = 50;
- public static final int STEP_RESULT_ID_ERROR = 60;
+ private static final int STEP_RESULT_ID_ROW = 10;
+ private static final int STEP_RESULT_ID_IO = 20;
+ private static final int STEP_RESULT_ID_DONE = 30;
+ private static final int STEP_RESULT_ID_INTERRUPT = 40;
+ private static final int STEP_RESULT_ID_BUSY = 50;
+ private static final int STEP_RESULT_ID_ERROR = 60;
// Identifier for limbo's StepResult
private final int stepResultId;
private final Object[] result;
- @NativeInvocation
+ @NativeInvocation(invokedFrom = "limbo_statement.rs")
public LimboStepResult(int stepResultId, Object[] result) {
this.stepResultId = stepResultId;
this.result = result;
}
+ public boolean isRow() {
+ return stepResultId == STEP_RESULT_ID_ROW;
+ }
+
public boolean isDone() {
return stepResultId == STEP_RESULT_ID_DONE;
}
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 03f3864a7..2f7f2c0d6 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
@@ -135,10 +135,6 @@ public class JDBC4Statement implements Statement {
* 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 {
@@ -147,13 +143,14 @@ public class JDBC4Statement implements Statement {
return this.withConnectionTimeout(
() -> {
try {
+ // TODO: if sql is a readOnly query, do we still need the locks?
connectionLock.lock();
statement = connection.prepare(sql);
- statement.execute();
+ final boolean result = statement.execute();
updateGeneratedKeys();
exhaustedResults = false;
- // TODO: determine whether
- return true;
+
+ return result;
} finally {
connectionLock.unlock();
}
diff --git a/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4StatementTest.java b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4StatementTest.java
new file mode 100644
index 000000000..f81e9d482
--- /dev/null
+++ b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4StatementTest.java
@@ -0,0 +1,53 @@
+package org.github.tursodatabase.jdbc4;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+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.Disabled;
+import org.junit.jupiter.api.Test;
+
+class JDBC4StatementTest {
+
+ 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 execute_ddl_should_return_false() throws Exception{
+ assertFalse(stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);"));
+ }
+
+ @Test
+ void execute_insert_should_return_false() throws Exception {
+ stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
+ assertFalse(stmt.execute("INSERT INTO users VALUES (1, 'limbo');"));
+ }
+
+ @Test
+ @Disabled("UPDATE not supported yet")
+ void execute_update_should_return_false() throws Exception {
+ stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
+ stmt.execute("INSERT INTO users VALUES (1, 'limbo');");
+ assertFalse(stmt.execute("UPDATE users SET username = 'seonwoo' WHERE id = 1;"));
+ }
+
+ @Test
+ void execute_select_should_return_true() throws Exception {
+ stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
+ stmt.execute("INSERT INTO users VALUES (1, 'limbo');");
+ assertTrue(stmt.execute("SELECT * FROM users;"));
+ }
+}