diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java index c7c69ab79..ce8bfadd4 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java @@ -15,16 +15,16 @@ import java.util.concurrent.atomic.AtomicBoolean; * differences between the JDBC specification and the Limbo API. */ public abstract class AbstractDB { - private final String url; - private final String fileName; + protected final String url; + protected final String filePath; private final AtomicBoolean closed = new AtomicBoolean(true); // Tracer for statements to avoid unfinalized statements on db close. private final Set statementPointerSet = ConcurrentHashMap.newKeySet(); - public AbstractDB(String url, String filaName) { + public AbstractDB(String url, String filePath) { this.url = url; - this.fileName = filaName; + this.filePath = filePath; } public boolean isClosed() { @@ -55,7 +55,7 @@ public abstract class AbstractDB { * @throws SQLException if a database access error occurs. */ public final synchronized void open(int openFlags) throws SQLException { - open0(fileName, openFlags); + open0(filePath, openFlags); } protected abstract void open0(String fileName, int openFlags) throws SQLException; @@ -72,29 +72,36 @@ public abstract class AbstractDB { } /** - * Compiles an SQL statement. + * Connects to a database. * - * @param stmt The SQL statement to compile. - * @throws SQLException if a database access error occurs. + * @return Pointer to the connection. */ - public final void prepare(CoreStatement stmt) throws SQLException { - if (stmt.sql == null) { - throw new SQLException("Statement must not be null"); - } + public abstract long connect() throws SQLException; - // TODO: check whether closing the pointer and replacing stamt.pointer should work atomically using locks etc - final SafeStatementPointer pointer = stmt.getStmtPointer(); - if (pointer != null) { - pointer.close(); - } - - final SafeStatementPointer newPointer = prepare(stmt.sql); - stmt.setStmtPointer(newPointer); - final boolean added = statementPointerSet.add(newPointer); - if (!added) { - throw new IllegalStateException("The pointer is already added to statements set"); - } - } +// /** +// * Compiles an SQL statement. +// * +// * @param stmt The SQL statement to compile. +// * @throws SQLException if a database access error occurs. +// */ +// public final void prepare(CoreStatement stmt) throws SQLException { +// if (stmt.sql == null) { +// throw new SQLException("Statement must not be null"); +// } +// +// // TODO: check whether closing the pointer and replacing stamt.pointer should work atomically using locks etc +// final SafeStatementPointer pointer = stmt.getStmtPointer(); +// if (pointer != null) { +// pointer.close(); +// } +// +// final SafeStatementPointer newPointer = stmt.connection.prepare(stmt.sql); +// stmt.setStmtPointer(newPointer); +// final boolean added = statementPointerSet.add(newPointer); +// if (!added) { +// throw new IllegalStateException("The pointer is already added to statements set"); +// } +// } /** * Destroys a statement. @@ -135,15 +142,6 @@ public abstract class AbstractDB { */ public abstract int exec(String sql) throws SQLException; - /** - * Compiles an SQL statement. - * - * @param sql An SQL statement. - * @return A SafeStmtPtr object. - * @throws SQLException if a database access error occurs. - */ - protected abstract SafeStatementPointer prepare(String sql) throws SQLException; - /** * Destroys a prepared statement. * @@ -189,21 +187,4 @@ public abstract class AbstractDB { // TODO: add implementation throw new SQLFeatureNotSupportedException(); } - - /** - * @param stmt Pointer to the statement. - * @return Number of columns in the result set returned by the prepared statement. - * @throws SQLException - * @see https://www.sqlite.org/c3ref/column_count.html - */ - public abstract int columnCount(long stmt) throws SQLException; - - /** - * @return Number of rows that were changed, inserted or deleted by the last SQL statement - * @throws SQLException - * @see https://www.sqlite.org/c3ref/changes.html - */ - public abstract long changes() throws SQLException; } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/LimboConnection.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java similarity index 75% rename from bindings/java/src/main/java/org/github/tursodatabase/LimboConnection.java rename to bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java index 38af814eb..254908db7 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/LimboConnection.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboConnection.java @@ -1,36 +1,38 @@ -package org.github.tursodatabase; +package org.github.tursodatabase.core; -import org.github.tursodatabase.core.AbstractDB; -import org.github.tursodatabase.core.LimboDBFactory; +import org.github.tursodatabase.annotations.NativeInvocation; +import org.github.tursodatabase.utils.LimboExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; -public abstract class LimboConnection implements Connection { +import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray; +public abstract class LimboConnection implements Connection { + private static final Logger logger = LoggerFactory.getLogger(LimboConnection.class); + + private final long connectionPtr; private final AbstractDB database; - public LimboConnection(AbstractDB database) { - this.database = database; - } - - public LimboConnection(String url, String fileName) throws SQLException { - this(url, fileName, new Properties()); + public LimboConnection(String url, String filePath) throws SQLException { + this(url, filePath, new Properties()); } /** * Creates a connection to limbo database. * * @param url e.g. "jdbc:sqlite:fileName" - * @param fileName path to file + * @param filePath path to file */ - public LimboConnection(String url, String fileName, Properties properties) throws SQLException { + public LimboConnection(String url, String filePath, Properties properties) throws SQLException { AbstractDB db = null; try { - db = open(url, fileName, properties); + db = open(url, filePath, properties); } catch (Throwable t) { try { if (db != null) { @@ -44,10 +46,11 @@ public abstract class LimboConnection implements Connection { } this.database = db; + this.connectionPtr = db.connect(); } - private static AbstractDB open(String url, String fileName, Properties properties) throws SQLException { - return LimboDBFactory.open(url, fileName, properties); + private static AbstractDB open(String url, String filePath, Properties properties) throws SQLException { + return LimboDBFactory.open(url, filePath, properties); } protected void checkOpen() throws SQLException { diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java index 7a6a5903e..c3eacdb21 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java @@ -4,23 +4,28 @@ package org.github.tursodatabase.core; import org.github.tursodatabase.LimboErrorCode; import org.github.tursodatabase.annotations.NativeInvocation; import org.github.tursodatabase.annotations.VisibleForTesting; -import org.github.tursodatabase.annotations.Nullable; -import org.github.tursodatabase.exceptions.LimboException; +import org.github.tursodatabase.utils.ByteArrayUtils; +import org.github.tursodatabase.utils.LimboExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; +import java.util.concurrent.locks.ReentrantLock; + +import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray; /** * This class provides a thin JNI layer over the SQLite3 C API. */ public final class LimboDB extends AbstractDB { - + private static final Logger logger = LoggerFactory.getLogger(LimboDB.class); // Pointer to database instance - private long dbPtr; + private long dbPointer; private boolean isOpen; private static boolean isLoaded; + private ReentrantLock dbLock = new ReentrantLock(); static { if ("The Android Project".equals(System.getProperty("java.vm.vendor"))) { @@ -46,80 +51,69 @@ public final class LimboDB extends AbstractDB { /** * @param url e.g. "jdbc:sqlite:fileName - * @param fileName e.g. path to file + * @param filePath e.g. path to file */ - public static LimboDB create(String url, String fileName) throws SQLException { - return new LimboDB(url, fileName); + public static LimboDB create(String url, String filePath) throws SQLException { + return new LimboDB(url, filePath); } // TODO: receive config as argument - private LimboDB(String url, String fileName) { - super(url, fileName); + private LimboDB(String url, String filePath) { + super(url, filePath); } // WRAPPER FUNCTIONS //////////////////////////////////////////// // TODO: add support for JNI @Override - protected synchronized native long openUtf8(byte[] file, int openFlags) throws SQLException; + protected native long openUtf8(byte[] file, int openFlags) throws SQLException; // TODO: add support for JNI @Override - protected synchronized native void close0() throws SQLException; + protected native void close0() throws SQLException; @Override - public synchronized int exec(String sql) throws SQLException { + public int exec(String sql) throws SQLException { // TODO: add implementation throw new SQLFeatureNotSupportedException(); } // TODO: add support for JNI - synchronized native int execUtf8(byte[] sqlUtf8) throws SQLException; + native int execUtf8(byte[] sqlUtf8) throws SQLException; // TODO: add support for JNI @Override public native void interrupt(); @Override - protected void open0(String fileName, int openFlags) throws SQLException { + protected void open0(String filePath, int openFlags) throws SQLException { if (isOpen) { - throw buildLimboException(LimboErrorCode.ETC.code, "Already opened"); + throw LimboExceptionUtils.buildLimboException(LimboErrorCode.LIMBO_ETC.code, "Already opened"); } - byte[] fileNameBytes = stringToUtf8ByteArray(fileName); - if (fileNameBytes == null) { - throw buildLimboException(LimboErrorCode.ETC.code, "File name cannot be converted to byteArray. File name: " + fileName); + byte[] filePathBytes = stringToUtf8ByteArray(filePath); + if (filePathBytes == null) { + throw LimboExceptionUtils.buildLimboException(LimboErrorCode.LIMBO_ETC.code, "File name cannot be converted to byteArray. File name: " + filePath); } - dbPtr = openUtf8(fileNameBytes, openFlags); + dbPointer = openUtf8(filePathBytes, openFlags); isOpen = true; } @Override - protected synchronized SafeStatementPointer prepare(String sql) throws SQLException { - // TODO: add implementation - throw new SQLFeatureNotSupportedException(); + public long connect() throws SQLException { + return connect0(ByteArrayUtils.stringToUtf8ByteArray(filePath), dbPointer); } + private native long connect0(byte[] path, long databasePtr) throws SQLException; + // TODO: add support for JNI @Override - protected synchronized native int finalize(long stmt); + protected native int finalize(long stmt); // TODO: add support for JNI @Override - public synchronized native int step(long stmt); - - @Override - public int columnCount(long stmt) throws SQLException { - // TODO - return 0; - } - - @Override - public long changes() throws SQLException { - // TODO - return 0; - } + public native int step(long stmt); @VisibleForTesting native void throwJavaException(int errorCode) throws SQLException; @@ -132,42 +126,6 @@ public final class LimboDB extends AbstractDB { */ @NativeInvocation private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException { - String errorMessage = utf8ByteBufferToString(errorMessageBytes); - throw buildLimboException(errorCode, errorMessage); - } - - /** - * Throws formatted SQLException with error code and message. - * - * @param errorCode Error code. - * @param errorMessage Error message. - */ - public LimboException buildLimboException(int errorCode, @Nullable String errorMessage) throws SQLException { - LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode); - String msg; - if (code == LimboErrorCode.UNKNOWN_ERROR) { - msg = String.format("%s:%s (%s)", code, errorCode, errorMessage); - } else { - msg = String.format("%s (%s)", code, errorMessage); - } - - return new LimboException(msg, code); - } - - @Nullable - private static String utf8ByteBufferToString(@Nullable byte[] buffer) { - if (buffer == null) { - return null; - } - - return new String(buffer, StandardCharsets.UTF_8); - } - - @Nullable - private static byte[] stringToUtf8ByteArray(@Nullable String str) { - if (str == null) { - return null; - } - return str.getBytes(StandardCharsets.UTF_8); + LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes); } } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDBFactory.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDBFactory.java index d5275cb6a..f7a81fd04 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDBFactory.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDBFactory.java @@ -17,25 +17,25 @@ public class LimboDBFactory { * Otherwise, it creates a new instance and stores it in the database holder. * * @param url the URL of the database - * @param fileName the path to the database file + * @param filePath the path to the database file * @param properties additional properties for the database connection * @return an instance of {@link LimboDB} * @throws SQLException if there is an error opening the connection * @throws IllegalArgumentException if the fileName is empty */ - public static LimboDB open(String url, String fileName, Properties properties) throws SQLException { + public static LimboDB open(String url, String filePath, Properties properties) throws SQLException { if (databaseHolder.containsKey(url)) { return databaseHolder.get(url); } - if (fileName.isEmpty()) { - throw new IllegalArgumentException("fileName should not be empty"); + if (filePath.isEmpty()) { + throw new IllegalArgumentException("filePath should not be empty"); } final LimboDB database; try { LimboDB.load(); - database = LimboDB.create(url, fileName); + database = LimboDB.create(url, filePath); } catch (Exception e) { throw new SQLException("Error opening connection", e); } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java index 5883f7487..dc404a9cb 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/jdbc4/JDBC4Connection.java @@ -1,6 +1,6 @@ package org.github.tursodatabase.jdbc4; -import org.github.tursodatabase.LimboConnection; +import org.github.tursodatabase.core.LimboConnection; import org.github.tursodatabase.annotations.SkipNullableCheck; import java.sql.*; @@ -11,8 +11,12 @@ import java.util.concurrent.Executor; public class JDBC4Connection extends LimboConnection { - public JDBC4Connection(String url, String fileName, Properties properties) throws SQLException { - super(url, fileName, properties); + public JDBC4Connection(String url, String filePath) throws SQLException { + super(url, filePath); + } + + public JDBC4Connection(String url, String filePath, Properties properties) throws SQLException { + super(url, filePath, properties); } @Override diff --git a/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ConnectionTest.java b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ConnectionTest.java index bf2a20b88..6c39f1c3d 100644 --- a/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ConnectionTest.java +++ b/bindings/java/src/test/java/org/github/tursodatabase/jdbc4/JDBC4ConnectionTest.java @@ -18,9 +18,9 @@ class JDBC4ConnectionTest { @BeforeEach void setUp() throws Exception { - String fileUrl = TestUtils.createTempFile(); - String url = "jdbc:sqlite:" + fileUrl; - connection = new JDBC4Connection(url, fileUrl, new Properties()); + String filePath = TestUtils.createTempFile(); + String url = "jdbc:sqlite:" + filePath; + connection = new JDBC4Connection(url, filePath, new Properties()); } @Test