mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-01 15:34:19 +01:00
Enhance LimboDB.java open logic
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package org.github.tursodatabase;
|
||||
|
||||
public enum LimboErrorCode {
|
||||
UNKNOWN_ERROR(-1, "Unknown error"),
|
||||
ETC(9999, "Unclassified error");
|
||||
|
||||
public final int code;
|
||||
public final String message;
|
||||
|
||||
/**
|
||||
* @param code Error code
|
||||
* @param message Message for the error.
|
||||
*/
|
||||
LimboErrorCode(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public static LimboErrorCode getErrorCode(int errorCode) {
|
||||
for (LimboErrorCode limboErrorCode: LimboErrorCode.values()) {
|
||||
if (errorCode == limboErrorCode.code) return limboErrorCode;
|
||||
}
|
||||
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboErrorCode{" +
|
||||
"code=" + code +
|
||||
", message='" + message + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.github.tursodatabase;
|
||||
|
||||
import org.github.tursodatabase.limbo.Connection;
|
||||
import org.github.tursodatabase.limbo.Cursor;
|
||||
import org.github.tursodatabase.limbo.Limbo;
|
||||
|
||||
/**
|
||||
* TODO: Remove Main class. We can use test code to verify behaviors.
|
||||
*/
|
||||
public class Main {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Limbo limbo = Limbo.create();
|
||||
Connection connection = limbo.getConnection("database.db");
|
||||
|
||||
Cursor cursor = connection.cursor();
|
||||
cursor.execute("SELECT * FROM example_table;");
|
||||
System.out.println("result: " + cursor.fetchOne());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.github.tursodatabase;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark methods that are called by native functions.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface NativeInvocation {
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
import org.github.tursodatabase.NativeInvocation;
|
||||
import org.github.tursodatabase.exceptions.LimboException;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -10,18 +14,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* are not only to provide functionality, but to handle contractual
|
||||
* differences between the JDBC specification and the Limbo API.
|
||||
*/
|
||||
public abstract class DB {
|
||||
public abstract class AbstractDB {
|
||||
private final String url;
|
||||
private final String fileName;
|
||||
private final AtomicBoolean closed = new AtomicBoolean(true);
|
||||
|
||||
public DB(String url, String fileName) throws SQLException {
|
||||
public AbstractDB(String url, String filaName) throws SQLException {
|
||||
this.url = url;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
this.fileName = filaName;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
@@ -36,7 +36,7 @@ public abstract class DB {
|
||||
/**
|
||||
* Executes an SQL statement.
|
||||
*
|
||||
* @param sql SQL statement to be executed.
|
||||
* @param sql SQL statement to be executed.
|
||||
* @param autoCommit Whether to auto-commit the transaction.
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
@@ -47,17 +47,16 @@ public abstract class DB {
|
||||
|
||||
/**
|
||||
* Creates an SQLite interface to a database for the given connection.
|
||||
* @see <a href="https://www.sqlite.org/c3ref/c_open_autoproxy.html">SQLite Open Flags</a>
|
||||
*
|
||||
* @param fileName The database.
|
||||
* @param openFlags Flags for opening the database.
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
public final synchronized void open(String fileName, int openFlags) throws SQLException {
|
||||
// TODO: add implementation
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
public final synchronized void open(int openFlags) throws SQLException {
|
||||
_open(fileName, openFlags);
|
||||
}
|
||||
|
||||
protected abstract void _open(String fileName, int openFlags) throws SQLException;
|
||||
|
||||
/**
|
||||
* Closes a database connection and finalizes any remaining statements before the closing
|
||||
* operation.
|
||||
@@ -95,13 +94,13 @@ public abstract class DB {
|
||||
|
||||
/**
|
||||
* Creates an SQLite interface to a database with the provided open flags.
|
||||
* @see <a href="https://www.sqlite.org/c3ref/c_open_autoproxy.html">SQLite Open Flags</a>
|
||||
*
|
||||
* @param filename The database to open.
|
||||
* @param fileName The database to open.
|
||||
* @param openFlags Flags for opening the database.
|
||||
* @return pointer to database instance
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
protected abstract void _open(String filename, int openFlags) throws SQLException;
|
||||
protected abstract long _open_utf8(byte[] fileName, int openFlags) throws SQLException;
|
||||
|
||||
/**
|
||||
* Closes the SQLite interface to a database.
|
||||
@@ -173,4 +172,35 @@ public abstract class DB {
|
||||
// TODO: add implementation
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws SQL Exception with error code.
|
||||
*
|
||||
* @param errorCode Error code to be passed.
|
||||
* @throws SQLException Formatted SQLException with error code
|
||||
*/
|
||||
@NativeInvocation
|
||||
private LimboException newSQLException(int errorCode, long errorMessagePointer) throws SQLException {
|
||||
throw newSQLException(errorCode, getErrorMessage(errorMessagePointer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code to be passed.
|
||||
* @param errorMessage throw newSQLException(errorCode);Error message to be passed.
|
||||
* @return Formatted SQLException with error code and message.
|
||||
*/
|
||||
public static LimboException newSQLException(int errorCode, String errorMessage) {
|
||||
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);
|
||||
}
|
||||
|
||||
protected abstract String getErrorMessage(long errorMessagePointer);
|
||||
}
|
||||
@@ -1,65 +1,67 @@
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
|
||||
/**
|
||||
* This class provides a thin JNI layer over the SQLite3 C API.
|
||||
*/
|
||||
public final class LimboDB extends DB {
|
||||
/**
|
||||
* SQLite connection handle.
|
||||
*/
|
||||
private long pointer = 0;
|
||||
public final class LimboDB extends AbstractDB {
|
||||
|
||||
// Pointer to database instance
|
||||
private long dbPtr;
|
||||
private boolean isOpen;
|
||||
|
||||
private static boolean isLoaded;
|
||||
private static boolean loadSucceeded;
|
||||
|
||||
static {
|
||||
if ("The Android Project".equals(System.getProperty("java.vm.vendor"))) {
|
||||
System.loadLibrary("sqlitejdbc");
|
||||
isLoaded = true;
|
||||
loadSucceeded = true;
|
||||
// TODO
|
||||
} else {
|
||||
// continue with non Android execution path
|
||||
isLoaded = false;
|
||||
loadSucceeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// url example: "jdbc:sqlite:{fileName}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url e.g. "jdbc:sqlite:fileName
|
||||
* @param fileName e.g. path to file
|
||||
*/
|
||||
public static LimboDB create(String url, String fileName) throws SQLException {
|
||||
return new LimboDB(url, fileName);
|
||||
}
|
||||
|
||||
// TODO: receive config as argument
|
||||
public LimboDB(String url, String fileName) throws SQLException {
|
||||
private LimboDB(String url, String fileName) throws SQLException {
|
||||
super(url, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the SQLite interface backend.
|
||||
*
|
||||
* @return True if the SQLite JDBC driver is successfully loaded; false otherwise.
|
||||
*/
|
||||
public static boolean load() throws Exception {
|
||||
if (isLoaded) return loadSucceeded;
|
||||
public void load() {
|
||||
if (isLoaded) return;
|
||||
|
||||
try {
|
||||
System.loadLibrary("_limbo_java");
|
||||
loadSucceeded = true;
|
||||
|
||||
} finally {
|
||||
isLoaded = true;
|
||||
}
|
||||
return loadSucceeded;
|
||||
}
|
||||
|
||||
// WRAPPER FUNCTIONS ////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected synchronized void _open(String file, int openFlags) throws SQLException {
|
||||
// TODO: add implementation
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
}
|
||||
|
||||
// TODO: add support for JNI
|
||||
synchronized native void _open_utf8(byte[] fileUtf8, int openFlags) throws SQLException;
|
||||
@Override
|
||||
protected synchronized native long _open_utf8(byte[] file, int openFlags) throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
@@ -78,6 +80,15 @@ public final class LimboDB extends DB {
|
||||
@Override
|
||||
public native void interrupt();
|
||||
|
||||
@Override
|
||||
protected void _open(String fileName, int openFlags) throws SQLException {
|
||||
if (isOpen) {
|
||||
throw newSQLException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened");
|
||||
}
|
||||
dbPtr = _open_utf8(stringToUtf8ByteArray(fileName), openFlags);
|
||||
isOpen = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized SafeStmtPtr prepare(String sql) throws SQLException {
|
||||
// TODO: add implementation
|
||||
@@ -91,4 +102,26 @@ public final class LimboDB extends DB {
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
public synchronized native int step(long stmt);
|
||||
|
||||
@Override
|
||||
protected String getErrorMessage(long errorMessagePointer) {
|
||||
return utf8ByteBufferToString(getErrorMessageUtf8(errorMessagePointer));
|
||||
}
|
||||
|
||||
private native byte[] getErrorMessageUtf8(long errorMessagePointer);
|
||||
|
||||
private static String utf8ByteBufferToString(byte[] buffer) {
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new String(buffer, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static byte[] stringToUtf8ByteArray(String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
return str.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.github.tursodatabase.exceptions;
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class LimboException extends SQLException {
|
||||
private final LimboErrorCode resultCode;
|
||||
|
||||
public LimboException(String message, LimboErrorCode resultCode) {
|
||||
super(message, null, resultCode.code & 0xff);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public LimboErrorCode getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.github.tursodatabase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class TestUtils {
|
||||
/**
|
||||
* Create temporary file and returns the path.
|
||||
*/
|
||||
public static String createTempFile() throws IOException {
|
||||
return Files.createTempFile("limbo_test_db", null).toAbsolutePath().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class LimboDBTest {
|
||||
|
||||
@Test
|
||||
void db_should_open_normally() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite" + dbPath, dbPath);
|
||||
db.load();
|
||||
db.open(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_throw_exception_when_opened_twice() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath);
|
||||
db.load();
|
||||
db.open(0);
|
||||
|
||||
assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user