Remove the tight coupling(using inheritance) between LimboXXX and JDBCXXX and favor composition instead

This commit is contained in:
김선우
2025-01-19 11:48:45 +09:00
parent 876788588b
commit 73f8eab651
8 changed files with 148 additions and 68 deletions

View File

@@ -10,6 +10,6 @@ import java.lang.annotation.Target;
* Annotation to mark methods that are called by native functions.
*/
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface NativeInvocation {
}

View File

@@ -79,13 +79,13 @@ public abstract class LimboConnection implements Connection {
* @return Pointer to statement.
* @throws SQLException if a database access error occurs.
*/
public long prepare(String sql) throws SQLException {
public LimboStatement prepare(String sql) throws SQLException {
logger.trace("DriverManager [{}] [SQLite EXEC] {}", Thread.currentThread().getName(), sql);
byte[] sqlBytes = stringToUtf8ByteArray(sql);
if (sqlBytes == null) {
throw new SQLException("Failed to convert " + sql + " into bytes");
}
return prepareUtf8(connectionPtr, sqlBytes);
return new LimboStatement(prepareUtf8(connectionPtr, sqlBytes));
}
private native long prepareUtf8(long connectionPtr, byte[] sqlUtf8) throws SQLException;

View File

@@ -3,25 +3,51 @@ package org.github.tursodatabase.core;
import java.sql.SQLException;
/**
* JDBC ResultSet.
* A table of data representing limbo database result set, which is generated by executing a statement that queries the
* database.
* <p>
* A {@link LimboResultSet} object is automatically closed when the {@link LimboStatement} object that generated it is
* closed or re-executed.
*/
public abstract class LimboResultSet {
public class LimboResultSet {
protected final LimboStatement statement;
private final LimboStatement statement;
// Whether the result set does not have any rows.
protected boolean isEmptyResultSet = false;
private boolean isEmptyResultSet = false;
// If the result set is open. Doesn't mean it has results.
private boolean open = false;
// Maximum number of rows as set by the statement
protected long maxRows;
private long maxRows;
// number of current row, starts at 1 (0 is used to represent loading data)
protected int row = 0;
private int row = 0;
protected LimboResultSet(LimboStatement statement) {
private boolean pastLastRow = false;
public static LimboResultSet of(LimboStatement statement) {
return new LimboResultSet(statement);
}
private LimboResultSet(LimboStatement statement) {
this.open = true;
this.statement = statement;
}
public boolean next() throws SQLException {
if (!open || isEmptyResultSet || pastLastRow) {
return false; // completed ResultSet
}
if (maxRows != 0 && row == maxRows) {
return false;
}
// TODO
// int statusCode = this.statement.step();
this.statement.step();
return true;
}
/**
* Checks the status of the result set.
*
@@ -34,7 +60,7 @@ public abstract class LimboResultSet {
/**
* @throws SQLException if not {@link #open}
*/
protected void checkOpen() throws SQLException {
public void checkOpen() throws SQLException {
if (!open) {
throw new SQLException("ResultSet closed");
}

View File

@@ -2,58 +2,45 @@ package org.github.tursodatabase.core;
import org.github.tursodatabase.annotations.NativeInvocation;
import org.github.tursodatabase.annotations.Nullable;
import org.github.tursodatabase.jdbc4.JDBC4ResultSet;
import org.github.tursodatabase.utils.LimboExceptionUtils;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public abstract class LimboStatement {
/**
* 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
* have been generated by different <code>LimboStatement</code> objects. All execution method in the <code>LimboStatement</code>
* implicitly close the current <code>resultSet</code> object of the statement if an open one exists.
*/
public class LimboStatement {
protected final LimboConnection connection;
protected final LimboResultSet resultSet;
private final long statementPointer;
private final LimboResultSet resultSet;
@Nullable
protected String sql = null;
protected LimboStatement(LimboConnection connection) {
this.connection = connection;
this.resultSet = new JDBC4ResultSet(this);
public LimboStatement(long statementPointer) {
this.statementPointer = statementPointer;
this.resultSet = LimboResultSet.of(this);
}
protected void internalClose() throws SQLException {
// TODO
public LimboResultSet resultSet() {
return resultSet;
}
protected void clearGeneratedKeys() throws SQLException {
// TODO
public void execute() throws SQLException {
LimboResultSet result = LimboResultSet.of(this);
// at least, run query minimally
result.next();
}
protected void updateGeneratedKeys() throws SQLException {
// TODO
public LimboStepResult step() throws SQLException {
return step(this.statementPointer);
}
// TODO: associate the result with CoreResultSet
// TODO: we can make this async!!
// TODO: distinguish queries that return result or doesn't return result
protected List<Object[]> execute(long stmtPointer) throws SQLException {
List<Object[]> result = new ArrayList<>();
while (true) {
Object[] stepResult = step(stmtPointer);
if (stepResult != null) {
for (int i = 0; i < stepResult.length; i++) {
System.out.println("stepResult" + i + ": " + stepResult[i]);
}
}
if (stepResult == null) break;
result.add(stepResult);
}
return result;
}
private native Object[] step(long stmtPointer) throws SQLException;
private native LimboStepResult step(long stmtPointer) throws SQLException;
/**
* Throws formatted SQLException with error code and message.

View File

@@ -0,0 +1,25 @@
package org.github.tursodatabase.core;
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;
// Identifier for limbo's StepResult
private final int stepResultId;
private final Object[] result;
@NativeInvocation
public LimboStepResult(int stepResultId, Object[] result) {
this.stepResultId = stepResultId;
this.result = result;
}
}

View File

@@ -2,7 +2,6 @@ package org.github.tursodatabase.jdbc4;
import org.github.tursodatabase.annotations.SkipNullableCheck;
import org.github.tursodatabase.core.LimboResultSet;
import org.github.tursodatabase.core.LimboStatement;
import java.io.InputStream;
import java.io.Reader;
@@ -12,10 +11,12 @@ import java.sql.*;
import java.util.Calendar;
import java.util.Map;
public class JDBC4ResultSet extends LimboResultSet implements ResultSet {
public class JDBC4ResultSet implements ResultSet {
public JDBC4ResultSet(LimboStatement statement) {
super(statement);
private final LimboResultSet resultSet;
public JDBC4ResultSet(LimboResultSet resultSet) {
this.resultSet = resultSet;
}
@Override

View File

@@ -1,18 +1,18 @@
package org.github.tursodatabase.jdbc4;
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.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/**
* Implementation of the {@link Statement} interface for JDBC 4.
*/
public class JDBC4Statement extends LimboStatement implements Statement {
public class JDBC4Statement implements Statement {
private final LimboConnection connection;
@Nullable
private LimboStatement statement = null;
private boolean closed;
private boolean closeOnCompletion;
@@ -32,7 +32,7 @@ public class JDBC4Statement extends LimboStatement implements Statement {
}
public JDBC4Statement(LimboConnection connection, int resultSetType, int resultSetConcurrency, int resultSetHoldability) {
super(connection);
this.connection = connection;
this.resultSetType = resultSetType;
this.resultSetConcurrency = resultSetConcurrency;
this.resultSetHoldability = resultSetHoldability;
@@ -129,11 +129,12 @@ public class JDBC4Statement extends LimboStatement implements Statement {
() -> {
try {
connectionLock.lock();
final long stmtPointer = connection.prepare(sql);
List<Object[]> result = execute(stmtPointer);
statement = connection.prepare(sql);
statement.execute();
updateGeneratedKeys();
exhaustedResults = false;
return !result.isEmpty();
return true;
// return !result.isEmpty();
} finally {
connectionLock.unlock();
}
@@ -314,6 +315,18 @@ public class JDBC4Statement extends LimboStatement implements Statement {
return false;
}
protected void internalClose() throws SQLException {
// TODO
}
protected void clearGeneratedKeys() throws SQLException {
// TODO
}
protected void updateGeneratedKeys() throws SQLException {
// TODO
}
private <T> T withConnectionTimeout(SQLCallable<T> callable) throws SQLException {
final int originalBusyTimeoutMillis = connection.getBusyTimeout();
if (queryTimeoutSeconds > 0) {