From b77bf879f799f68eb741653d0b34af3ea847894b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Fri, 17 Jan 2025 04:26:44 +0900 Subject: [PATCH] Implement prepare on java side --- .../java/org/github/tursodatabase/JDBC.java | 1 + .../github/tursodatabase/LimboErrorCode.java | 3 +- .../tursodatabase/core/CoreStatement.java | 3 +- .../tursodatabase/core/LimboConnection.java | 29 ++++++++++++++ .../core/SafeStatementPointer.java | 38 ++++++------------- .../tursodatabase/jdbc4/JDBC4Statement.java | 8 ++-- .../tursodatabase/utils/ByteArrayUtils.java | 24 ++++++++++++ 7 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 bindings/java/src/main/java/org/github/tursodatabase/utils/ByteArrayUtils.java diff --git a/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java b/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java index 0b2196058..bb46fb05e 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/JDBC.java @@ -2,6 +2,7 @@ package org.github.tursodatabase; import org.github.tursodatabase.annotations.Nullable; import org.github.tursodatabase.annotations.SkipNullableCheck; +import org.github.tursodatabase.core.LimboConnection; import org.github.tursodatabase.jdbc4.JDBC4Connection; import java.sql.*; diff --git a/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java b/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java index e3edd21a9..d2450d266 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java @@ -39,7 +39,8 @@ public enum LimboErrorCode { SQLITE_NULL(SqliteCode.SQLITE_NULL, "Null type"), UNKNOWN_ERROR(-1, "Unknown error"), - LIMBO_DATABASE_ALREADY_CLOSED(1000, "Database already closed"), + LIMBO_FAILED_TO_PARSE_BYTE_ARRAY(1100, "Failed to parse ut8 byte array"), + LIMBO_FAILED_TO_PREPARE_STATEMENT(1200, "Failed to prepare statement"), LIMBO_ETC(9999, "Unclassified error"); public final int code; diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/CoreStatement.java b/bindings/java/src/main/java/org/github/tursodatabase/core/CoreStatement.java index c58ecf81d..21848379b 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/CoreStatement.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/CoreStatement.java @@ -1,6 +1,5 @@ package org.github.tursodatabase.core; -import org.github.tursodatabase.LimboConnection; import org.github.tursodatabase.annotations.Nullable; import org.github.tursodatabase.jdbc4.JDBC4ResultSet; @@ -68,6 +67,6 @@ public abstract class CoreStatement { } } - return stmtPointer.safeRunInt(AbstractDB::columnCount) != 0; + return this.stmtPointer.columnCount() != 0; } } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java index 254908db7..91820c002 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java @@ -72,6 +72,24 @@ public abstract class LimboConnection implements Connection { return database; } + /** + * Compiles an SQL statement. + * + * @param sql An SQL statement. + * @return A SafeStmtPtr object. + * @throws SQLException if a database access error occurs. + */ + public SafeStatementPointer 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 new SafeStatementPointer(this, prepareUtf8(connectionPtr, sqlBytes)); + } + + private native long prepareUtf8(long connectionPtr, byte[] sqlUtf8) throws SQLException; + /** * @return busy timeout in milliseconds. */ @@ -108,4 +126,15 @@ public abstract class LimboConnection implements Connection { public void setBusyTimeout(int busyTimeout) { // TODO: add support for busy timeout } + + /** + * Throws formatted SQLException with error code and message. + * + * @param errorCode Error code. + * @param errorMessageBytes Error message. + */ + @NativeInvocation + private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException { + LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes); + } } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/SafeStatementPointer.java b/bindings/java/src/main/java/org/github/tursodatabase/core/SafeStatementPointer.java index 4ed4bcbea..a8a90f3cd 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/SafeStatementPointer.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/SafeStatementPointer.java @@ -10,16 +10,16 @@ import java.util.concurrent.locks.ReentrantLock; public class SafeStatementPointer { // Store a reference to database, so we can lock it before calling any safe functions. - private final AbstractDB database; - private final long databasePointer; + private final LimboConnection connection; + private final long statementPtr; private volatile boolean closed = false; - private final ReentrantLock databaseLock = new ReentrantLock(); + private final ReentrantLock connectionLock = new ReentrantLock(); - public SafeStatementPointer(AbstractDB database, long databasePointer) { - this.database = database; - this.databasePointer = databasePointer; + public SafeStatementPointer(LimboConnection connection, long statementPtr) { + this.connection = connection; + this.statementPtr = statementPtr; } /** @@ -36,10 +36,10 @@ public class SafeStatementPointer { */ public int close() throws SQLException { try { - databaseLock.lock(); + connectionLock.lock(); return internalClose(); } finally { - databaseLock.unlock(); + connectionLock.unlock(); } } @@ -48,24 +48,8 @@ public class SafeStatementPointer { return 0; } - public int safeRunInt(SafePointerIntFunction function) throws SQLException, E { - try { - databaseLock.lock(); - this.ensureOpen(); - return function.run(database, databasePointer); - } finally { - databaseLock.unlock(); - } - } - - private void ensureOpen() throws SQLException { - if (this.closed) { - throw new SQLException("Pointer is closed"); - } - } - - @FunctionalInterface - public interface SafePointerIntFunction { - int run(AbstractDB database, long pointer) throws E; + public long columnCount() throws SQLException { + // TODO + return 0; } } 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 c1bc0f810..3e370e329 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,8 +1,7 @@ package org.github.tursodatabase.jdbc4; -import org.github.tursodatabase.LimboConnection; +import org.github.tursodatabase.core.LimboConnection; import org.github.tursodatabase.annotations.SkipNullableCheck; -import org.github.tursodatabase.core.AbstractDB; import org.github.tursodatabase.core.CoreStatement; import java.sql.*; @@ -126,13 +125,12 @@ public class JDBC4Statement extends CoreStatement implements Statement { return this.withConnectionTimeout( () -> { - final AbstractDB database = connection.getDatabase(); try { connectionLock.lock(); - database.prepare(this); + connection.prepare(sql); boolean result = exec(); updateGeneratedKeys(); - updateCount = database.changes(); + // TODO: updateCount = connection.changes(); exhaustedResults = false; return result; } finally { diff --git a/bindings/java/src/main/java/org/github/tursodatabase/utils/ByteArrayUtils.java b/bindings/java/src/main/java/org/github/tursodatabase/utils/ByteArrayUtils.java new file mode 100644 index 000000000..a89f05042 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/utils/ByteArrayUtils.java @@ -0,0 +1,24 @@ +package org.github.tursodatabase.utils; + +import org.github.tursodatabase.annotations.Nullable; + +import java.nio.charset.StandardCharsets; + +public class ByteArrayUtils { + @Nullable + public static String utf8ByteBufferToString(@Nullable byte[] buffer) { + if (buffer == null) { + return null; + } + + return new String(buffer, StandardCharsets.UTF_8); + } + + @Nullable + public static byte[] stringToUtf8ByteArray(@Nullable String str) { + if (str == null) { + return null; + } + return str.getBytes(StandardCharsets.UTF_8); + } +}