Update JDBC4Statement.java execute method to return result correctly

This commit is contained in:
김선우 2025-01-19 16:09:09 +09:00
parent afbf041e2f
commit 0f46aaa0ec
5 changed files with 117 additions and 30 deletions

View file

@ -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 +
'}';
}
}

View file

@ -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 <code>resultSet</code> object per <code>LimboStatement</code> can be open at the same time.
* Therefore, if the reading of one <code>resultSet</code> object is interleaved with the reading of another, each must
@ -13,14 +13,13 @@ import java.sql.SQLException;
* implicitly close the current <code>resultSet</code> 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 + '\'' +
'}';
}
}

View file

@ -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;
}

View file

@ -135,10 +135,6 @@ public class JDBC4Statement implements Statement {
* <code>getResultSet</code> or <code>getUpdateCount</code>
* to retrieve the result, and <code>getMoreResults</code> to
* move to any subsequent result(s).
*
* @return <code>true</code> if the first result is a <code>ResultSet</code>
* object; <code>false</code> 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();
}

View file

@ -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;"));
}
}