mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
Apply lints
This commit is contained in:
parent
8b6e761496
commit
2212cc2a09
30 changed files with 2841 additions and 2838 deletions
|
@ -1,79 +1,79 @@
|
|||
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.*;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
import org.github.tursodatabase.jdbc4.JDBC4Connection;
|
||||
|
||||
public class JDBC implements Driver {
|
||||
private static final String VALID_URL_PREFIX = "jdbc:sqlite:";
|
||||
private static final String VALID_URL_PREFIX = "jdbc:sqlite:";
|
||||
|
||||
static {
|
||||
try {
|
||||
DriverManager.registerDriver(new JDBC());
|
||||
} catch (Exception e) {
|
||||
// TODO: log
|
||||
}
|
||||
static {
|
||||
try {
|
||||
DriverManager.registerDriver(new JDBC());
|
||||
} catch (Exception e) {
|
||||
// TODO: log
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LimboConnection createConnection(String url, Properties properties) throws SQLException {
|
||||
if (!isValidURL(url)) return null;
|
||||
@Nullable
|
||||
public static LimboConnection createConnection(String url, Properties properties)
|
||||
throws SQLException {
|
||||
if (!isValidURL(url)) return null;
|
||||
|
||||
url = url.trim();
|
||||
return new JDBC4Connection(url, extractAddress(url), properties);
|
||||
}
|
||||
url = url.trim();
|
||||
return new JDBC4Connection(url, extractAddress(url), properties);
|
||||
}
|
||||
|
||||
private static boolean isValidURL(String url) {
|
||||
return url != null && url.toLowerCase(Locale.ROOT).startsWith(VALID_URL_PREFIX);
|
||||
}
|
||||
private static boolean isValidURL(String url) {
|
||||
return url != null && url.toLowerCase(Locale.ROOT).startsWith(VALID_URL_PREFIX);
|
||||
}
|
||||
|
||||
private static String extractAddress(String url) {
|
||||
return url.substring(VALID_URL_PREFIX.length());
|
||||
}
|
||||
private static String extractAddress(String url) {
|
||||
return url.substring(VALID_URL_PREFIX.length());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Connection connect(String url, Properties info) throws SQLException {
|
||||
return createConnection(url, info);
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public Connection connect(String url, Properties info) throws SQLException {
|
||||
return createConnection(url, info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsURL(String url) throws SQLException {
|
||||
return isValidURL(url);
|
||||
}
|
||||
@Override
|
||||
public boolean acceptsURL(String url) throws SQLException {
|
||||
return isValidURL(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
|
||||
return LimboConfig.getDriverPropertyInfo();
|
||||
}
|
||||
@Override
|
||||
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException {
|
||||
return LimboConfig.getDriverPropertyInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMajorVersion() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getMajorVersion() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinorVersion() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getMinorVersion() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean jdbcCompliant() {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean jdbcCompliant() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,48 +4,47 @@ import java.sql.DriverPropertyInfo;
|
|||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Limbo Configuration.
|
||||
*/
|
||||
/** Limbo Configuration. */
|
||||
public class LimboConfig {
|
||||
private final Properties pragma;
|
||||
private final Properties pragma;
|
||||
|
||||
public LimboConfig(Properties properties) {
|
||||
this.pragma = properties;
|
||||
public LimboConfig(Properties properties) {
|
||||
this.pragma = properties;
|
||||
}
|
||||
|
||||
public static DriverPropertyInfo[] getDriverPropertyInfo() {
|
||||
return Arrays.stream(Pragma.values())
|
||||
.map(
|
||||
p -> {
|
||||
DriverPropertyInfo info = new DriverPropertyInfo(p.pragmaName, null);
|
||||
info.description = p.description;
|
||||
info.choices = p.choices;
|
||||
info.required = false;
|
||||
return info;
|
||||
})
|
||||
.toArray(DriverPropertyInfo[]::new);
|
||||
}
|
||||
|
||||
public Properties toProperties() {
|
||||
Properties copy = new Properties();
|
||||
copy.putAll(pragma);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public enum Pragma {
|
||||
;
|
||||
private final String pragmaName;
|
||||
private final String description;
|
||||
private final String[] choices;
|
||||
|
||||
Pragma(String pragmaName, String description, String[] choices) {
|
||||
this.pragmaName = pragmaName;
|
||||
this.description = description;
|
||||
this.choices = choices;
|
||||
}
|
||||
|
||||
public static DriverPropertyInfo[] getDriverPropertyInfo() {
|
||||
return Arrays.stream(Pragma.values())
|
||||
.map(p -> {
|
||||
DriverPropertyInfo info = new DriverPropertyInfo(p.pragmaName, null);
|
||||
info.description = p.description;
|
||||
info.choices = p.choices;
|
||||
info.required = false;
|
||||
return info;
|
||||
})
|
||||
.toArray(DriverPropertyInfo[]::new);
|
||||
}
|
||||
|
||||
public Properties toProperties() {
|
||||
Properties copy = new Properties();
|
||||
copy.putAll(pragma);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public enum Pragma {
|
||||
;
|
||||
private final String pragmaName;
|
||||
private final String description;
|
||||
private final String[] choices;
|
||||
|
||||
Pragma(String pragmaName, String description, String[] choices) {
|
||||
this.pragmaName = pragmaName;
|
||||
this.description = description;
|
||||
this.choices = choices;
|
||||
}
|
||||
|
||||
public String getPragmaName() {
|
||||
return pragmaName;
|
||||
}
|
||||
public String getPragmaName() {
|
||||
return pragmaName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,88 +1,87 @@
|
|||
package org.github.tursodatabase;
|
||||
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
import javax.sql.DataSource;
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
|
||||
/**
|
||||
* Provides {@link DataSource} API for configuring Limbo database connection.
|
||||
*/
|
||||
/** Provides {@link DataSource} API for configuring Limbo database connection. */
|
||||
public class LimboDataSource implements DataSource {
|
||||
|
||||
private final LimboConfig limboConfig;
|
||||
private final String url;
|
||||
private final LimboConfig limboConfig;
|
||||
private final String url;
|
||||
|
||||
/**
|
||||
* Creates a datasource based on the provided configuration.
|
||||
*
|
||||
* @param limboConfig The configuration for the datasource.
|
||||
*/
|
||||
public LimboDataSource(LimboConfig limboConfig, String url) {
|
||||
this.limboConfig = limboConfig;
|
||||
this.url = url;
|
||||
}
|
||||
/**
|
||||
* Creates a datasource based on the provided configuration.
|
||||
*
|
||||
* @param limboConfig The configuration for the datasource.
|
||||
*/
|
||||
public LimboDataSource(LimboConfig limboConfig, String url) {
|
||||
this.limboConfig = limboConfig;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Connection getConnection() throws SQLException {
|
||||
return getConnection(null, null);
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public Connection getConnection() throws SQLException {
|
||||
return getConnection(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Connection getConnection(@Nullable String username, @Nullable String password) throws SQLException {
|
||||
Properties properties = limboConfig.toProperties();
|
||||
if (username != null) properties.put("user", username);
|
||||
if (password != null) properties.put("pass", password);
|
||||
return JDBC.createConnection(url, properties);
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public Connection getConnection(@Nullable String username, @Nullable String password)
|
||||
throws SQLException {
|
||||
Properties properties = limboConfig.toProperties();
|
||||
if (username != null) properties.put("user", username);
|
||||
if (password != null) properties.put("pass", password);
|
||||
return JDBC.createConnection(url, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PrintWriter getLogWriter() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setLogWriter(PrintWriter out) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public void setLoginTimeout(int seconds) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public int getLoginTimeout() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,72 +2,67 @@ package org.github.tursodatabase;
|
|||
|
||||
import org.github.tursodatabase.core.SqliteCode;
|
||||
|
||||
/**
|
||||
* Limbo error code. Superset of SQLite3 error code.
|
||||
*/
|
||||
/** Limbo error code. Superset of SQLite3 error code. */
|
||||
public enum LimboErrorCode {
|
||||
SQLITE_OK(SqliteCode.SQLITE_OK, "Successful result"),
|
||||
SQLITE_ERROR(SqliteCode.SQLITE_ERROR, "SQL error or missing database"),
|
||||
SQLITE_INTERNAL(SqliteCode.SQLITE_INTERNAL, "An internal logic error in SQLite"),
|
||||
SQLITE_PERM(SqliteCode.SQLITE_PERM, "Access permission denied"),
|
||||
SQLITE_ABORT(SqliteCode.SQLITE_ABORT, "Callback routine requested an abort"),
|
||||
SQLITE_BUSY(SqliteCode.SQLITE_BUSY, "The database file is locked"),
|
||||
SQLITE_LOCKED(SqliteCode.SQLITE_LOCKED, "A table in the database is locked"),
|
||||
SQLITE_NOMEM(SqliteCode.SQLITE_NOMEM, "A malloc() failed"),
|
||||
SQLITE_READONLY(SqliteCode.SQLITE_READONLY, "Attempt to write a readonly database"),
|
||||
SQLITE_INTERRUPT(SqliteCode.SQLITE_INTERRUPT, "Operation terminated by sqlite_interrupt()"),
|
||||
SQLITE_IOERR(SqliteCode.SQLITE_IOERR, "Some kind of disk I/O error occurred"),
|
||||
SQLITE_CORRUPT(SqliteCode.SQLITE_CORRUPT, "The database disk image is malformed"),
|
||||
SQLITE_NOTFOUND(SqliteCode.SQLITE_NOTFOUND, "(Internal Only) Table or record not found"),
|
||||
SQLITE_FULL(SqliteCode.SQLITE_FULL, "Insertion failed because database is full"),
|
||||
SQLITE_CANTOPEN(SqliteCode.SQLITE_CANTOPEN, "Unable to open the database file"),
|
||||
SQLITE_PROTOCOL(SqliteCode.SQLITE_PROTOCOL, "Database lock protocol error"),
|
||||
SQLITE_EMPTY(SqliteCode.SQLITE_EMPTY, "(Internal Only) Database table is empty"),
|
||||
SQLITE_SCHEMA(SqliteCode.SQLITE_SCHEMA, "The database schema changed"),
|
||||
SQLITE_TOOBIG(SqliteCode.SQLITE_TOOBIG, "Too much data for one row of a table"),
|
||||
SQLITE_CONSTRAINT(SqliteCode.SQLITE_CONSTRAINT, "Abort due to constraint violation"),
|
||||
SQLITE_MISMATCH(SqliteCode.SQLITE_MISMATCH, "Data type mismatch"),
|
||||
SQLITE_MISUSE(SqliteCode.SQLITE_MISUSE, "Library used incorrectly"),
|
||||
SQLITE_NOLFS(SqliteCode.SQLITE_NOLFS, "Uses OS features not supported on host"),
|
||||
SQLITE_AUTH(SqliteCode.SQLITE_AUTH, "Authorization denied"),
|
||||
SQLITE_ROW(SqliteCode.SQLITE_ROW, "sqlite_step() has another row ready"),
|
||||
SQLITE_DONE(SqliteCode.SQLITE_DONE, "sqlite_step() has finished executing"),
|
||||
SQLITE_INTEGER(SqliteCode.SQLITE_INTEGER, "Integer type"),
|
||||
SQLITE_FLOAT(SqliteCode.SQLITE_FLOAT, "Float type"),
|
||||
SQLITE_TEXT(SqliteCode.SQLITE_TEXT, "Text type"),
|
||||
SQLITE_BLOB(SqliteCode.SQLITE_BLOB, "Blob type"),
|
||||
SQLITE_NULL(SqliteCode.SQLITE_NULL, "Null type"),
|
||||
SQLITE_OK(SqliteCode.SQLITE_OK, "Successful result"),
|
||||
SQLITE_ERROR(SqliteCode.SQLITE_ERROR, "SQL error or missing database"),
|
||||
SQLITE_INTERNAL(SqliteCode.SQLITE_INTERNAL, "An internal logic error in SQLite"),
|
||||
SQLITE_PERM(SqliteCode.SQLITE_PERM, "Access permission denied"),
|
||||
SQLITE_ABORT(SqliteCode.SQLITE_ABORT, "Callback routine requested an abort"),
|
||||
SQLITE_BUSY(SqliteCode.SQLITE_BUSY, "The database file is locked"),
|
||||
SQLITE_LOCKED(SqliteCode.SQLITE_LOCKED, "A table in the database is locked"),
|
||||
SQLITE_NOMEM(SqliteCode.SQLITE_NOMEM, "A malloc() failed"),
|
||||
SQLITE_READONLY(SqliteCode.SQLITE_READONLY, "Attempt to write a readonly database"),
|
||||
SQLITE_INTERRUPT(SqliteCode.SQLITE_INTERRUPT, "Operation terminated by sqlite_interrupt()"),
|
||||
SQLITE_IOERR(SqliteCode.SQLITE_IOERR, "Some kind of disk I/O error occurred"),
|
||||
SQLITE_CORRUPT(SqliteCode.SQLITE_CORRUPT, "The database disk image is malformed"),
|
||||
SQLITE_NOTFOUND(SqliteCode.SQLITE_NOTFOUND, "(Internal Only) Table or record not found"),
|
||||
SQLITE_FULL(SqliteCode.SQLITE_FULL, "Insertion failed because database is full"),
|
||||
SQLITE_CANTOPEN(SqliteCode.SQLITE_CANTOPEN, "Unable to open the database file"),
|
||||
SQLITE_PROTOCOL(SqliteCode.SQLITE_PROTOCOL, "Database lock protocol error"),
|
||||
SQLITE_EMPTY(SqliteCode.SQLITE_EMPTY, "(Internal Only) Database table is empty"),
|
||||
SQLITE_SCHEMA(SqliteCode.SQLITE_SCHEMA, "The database schema changed"),
|
||||
SQLITE_TOOBIG(SqliteCode.SQLITE_TOOBIG, "Too much data for one row of a table"),
|
||||
SQLITE_CONSTRAINT(SqliteCode.SQLITE_CONSTRAINT, "Abort due to constraint violation"),
|
||||
SQLITE_MISMATCH(SqliteCode.SQLITE_MISMATCH, "Data type mismatch"),
|
||||
SQLITE_MISUSE(SqliteCode.SQLITE_MISUSE, "Library used incorrectly"),
|
||||
SQLITE_NOLFS(SqliteCode.SQLITE_NOLFS, "Uses OS features not supported on host"),
|
||||
SQLITE_AUTH(SqliteCode.SQLITE_AUTH, "Authorization denied"),
|
||||
SQLITE_ROW(SqliteCode.SQLITE_ROW, "sqlite_step() has another row ready"),
|
||||
SQLITE_DONE(SqliteCode.SQLITE_DONE, "sqlite_step() has finished executing"),
|
||||
SQLITE_INTEGER(SqliteCode.SQLITE_INTEGER, "Integer type"),
|
||||
SQLITE_FLOAT(SqliteCode.SQLITE_FLOAT, "Float type"),
|
||||
SQLITE_TEXT(SqliteCode.SQLITE_TEXT, "Text type"),
|
||||
SQLITE_BLOB(SqliteCode.SQLITE_BLOB, "Blob type"),
|
||||
SQLITE_NULL(SqliteCode.SQLITE_NULL, "Null type"),
|
||||
|
||||
UNKNOWN_ERROR(-1, "Unknown error"),
|
||||
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");
|
||||
UNKNOWN_ERROR(-1, "Unknown error"),
|
||||
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;
|
||||
public final String message;
|
||||
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;
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
public static LimboErrorCode getErrorCode(int errorCode) {
|
||||
for (LimboErrorCode limboErrorCode: LimboErrorCode.values()) {
|
||||
if (errorCode == limboErrorCode.code) return limboErrorCode;
|
||||
}
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboErrorCode{" +
|
||||
"code=" + code +
|
||||
", message='" + message + '\'' +
|
||||
'}';
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboErrorCode{" + "code=" + code + ", message='" + message + '\'' + '}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
package org.github.tursodatabase.annotations;
|
||||
|
||||
|
||||
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.
|
||||
* For example, throwing exceptions or creating java objects.
|
||||
* Annotation to mark methods that are called by native functions. For example, throwing exceptions
|
||||
* or creating java objects.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
|
||||
public @interface NativeInvocation {
|
||||
String invokedFrom() default "";
|
||||
String invokedFrom() default "";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.github.tursodatabase.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -8,11 +7,10 @@ import java.lang.annotation.Target;
|
|||
|
||||
/**
|
||||
* Annotation to mark nullable types.
|
||||
* <p>
|
||||
* This annotation is used to indicate that a method, field, or parameter can be null.
|
||||
* It helps in identifying potential nullability issues and improving code quality.
|
||||
*
|
||||
* <p>This annotation is used to indicate that a method, field, or parameter can be null. It helps
|
||||
* in identifying potential nullability issues and improving code quality.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
public @interface Nullable {
|
||||
}
|
||||
public @interface Nullable {}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.github.tursodatabase.annotations;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
@ -8,11 +7,11 @@ import java.lang.annotation.Target;
|
|||
|
||||
/**
|
||||
* Marker annotation to skip nullable checks.
|
||||
* <p>
|
||||
* This annotation is used to mark methods, fields, or parameters that should be excluded from nullable checks.
|
||||
* It is typically applied to code that is still under development or requires special handling.
|
||||
*
|
||||
* <p>This annotation is used to mark methods, fields, or parameters that should be excluded from
|
||||
* nullable checks. It is typically applied to code that is still under development or requires
|
||||
* special handling.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
|
||||
public @interface SkipNullableCheck {
|
||||
}
|
||||
public @interface SkipNullableCheck {}
|
||||
|
|
|
@ -5,10 +5,7 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark methods that use larger visibility for testing purposes.
|
||||
*/
|
||||
/** Annotation to mark methods that use larger visibility for testing purposes. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface VisibleForTesting {
|
||||
}
|
||||
public @interface VisibleForTesting {}
|
||||
|
|
|
@ -5,74 +5,71 @@ import java.sql.SQLFeatureNotSupportedException;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Interface to Limbo. It provides some helper functions
|
||||
* used by other parts of the driver. The goal of the helper functions here
|
||||
* are not only to provide functionality, but to handle contractual
|
||||
* Interface to Limbo. It provides some helper functions used by other parts of the driver. The goal
|
||||
* of the helper functions here are not only to provide functionality, but to handle contractual
|
||||
* differences between the JDBC specification and the Limbo API.
|
||||
*/
|
||||
public abstract class AbstractDB {
|
||||
protected final String url;
|
||||
protected final String filePath;
|
||||
private final AtomicBoolean closed = new AtomicBoolean(true);
|
||||
protected final String url;
|
||||
protected final String filePath;
|
||||
private final AtomicBoolean closed = new AtomicBoolean(true);
|
||||
|
||||
public AbstractDB(String url, String filePath) {
|
||||
this.url = url;
|
||||
this.filePath = filePath;
|
||||
}
|
||||
public AbstractDB(String url, String filePath) {
|
||||
this.url = url;
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed.get();
|
||||
}
|
||||
public boolean isClosed() {
|
||||
return closed.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts any pending operation and returns at its earliest opportunity.
|
||||
*/
|
||||
public abstract void interrupt() throws SQLException;
|
||||
/** Aborts any pending operation and returns at its earliest opportunity. */
|
||||
public abstract void interrupt() throws SQLException;
|
||||
|
||||
/**
|
||||
* Creates an SQLite interface to a database for the given connection.
|
||||
*
|
||||
* @param openFlags Flags for opening the database.
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
public final synchronized void open(int openFlags) throws SQLException {
|
||||
open0(filePath, openFlags);
|
||||
}
|
||||
/**
|
||||
* Creates an SQLite interface to a database for the given connection.
|
||||
*
|
||||
* @param openFlags Flags for opening the database.
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
public final synchronized void open(int openFlags) throws SQLException {
|
||||
open0(filePath, openFlags);
|
||||
}
|
||||
|
||||
protected abstract void open0(String fileName, int openFlags) throws SQLException;
|
||||
protected abstract void open0(String fileName, int openFlags) throws SQLException;
|
||||
|
||||
/**
|
||||
* Closes a database connection and finalizes any remaining statements before the closing
|
||||
* operation.
|
||||
*
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
public final synchronized void close() throws SQLException {
|
||||
// TODO: add implementation
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
}
|
||||
/**
|
||||
* Closes a database connection and finalizes any remaining statements before the closing
|
||||
* operation.
|
||||
*
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
public final synchronized void close() throws SQLException {
|
||||
// TODO: add implementation
|
||||
throw new SQLFeatureNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to a database.
|
||||
*
|
||||
* @return Pointer to the connection.
|
||||
*/
|
||||
public abstract long connect() throws SQLException;
|
||||
/**
|
||||
* Connects to a database.
|
||||
*
|
||||
* @return Pointer to the connection.
|
||||
*/
|
||||
public abstract long connect() throws SQLException;
|
||||
|
||||
/**
|
||||
* Creates an SQLite interface to a database with the provided open flags.
|
||||
*
|
||||
* @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 long openUtf8(byte[] fileName, int openFlags) throws SQLException;
|
||||
/**
|
||||
* Creates an SQLite interface to a database with the provided open flags.
|
||||
*
|
||||
* @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 long openUtf8(byte[] fileName, int openFlags) throws SQLException;
|
||||
|
||||
/**
|
||||
* Closes the SQLite interface to a database.
|
||||
*
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
protected abstract void close0() throws SQLException;
|
||||
/**
|
||||
* Closes the SQLite interface to a database.
|
||||
*
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
protected abstract void close0() throws SQLException;
|
||||
}
|
||||
|
|
|
@ -1,140 +1,139 @@
|
|||
package org.github.tursodatabase.core;
|
||||
|
||||
import org.github.tursodatabase.annotations.NativeInvocation;
|
||||
import org.github.tursodatabase.utils.LimboExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArray;
|
||||
import org.github.tursodatabase.annotations.NativeInvocation;
|
||||
import org.github.tursodatabase.utils.LimboExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class LimboConnection implements Connection {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LimboConnection.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(LimboConnection.class);
|
||||
|
||||
private final long connectionPtr;
|
||||
private final AbstractDB database;
|
||||
private final long connectionPtr;
|
||||
private final AbstractDB database;
|
||||
|
||||
public LimboConnection(String url, String filePath) throws SQLException {
|
||||
this(url, filePath, 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 filePath path to file
|
||||
*/
|
||||
public LimboConnection(String url, String filePath, Properties properties) throws SQLException {
|
||||
AbstractDB db = null;
|
||||
/**
|
||||
* Creates a connection to limbo database
|
||||
*
|
||||
* @param url e.g. "jdbc:sqlite:fileName"
|
||||
* @param filePath path to file
|
||||
*/
|
||||
public LimboConnection(String url, String filePath, Properties properties) throws SQLException {
|
||||
AbstractDB db = null;
|
||||
|
||||
try {
|
||||
db = open(url, filePath, properties);
|
||||
} catch (Throwable t) {
|
||||
try {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
} catch (Throwable t2) {
|
||||
t.addSuppressed(t2);
|
||||
}
|
||||
|
||||
throw t;
|
||||
try {
|
||||
db = open(url, filePath, properties);
|
||||
} catch (Throwable t) {
|
||||
try {
|
||||
if (db != null) {
|
||||
db.close();
|
||||
}
|
||||
} catch (Throwable t2) {
|
||||
t.addSuppressed(t2);
|
||||
}
|
||||
|
||||
this.database = db;
|
||||
this.connectionPtr = db.connect();
|
||||
throw t;
|
||||
}
|
||||
|
||||
private static AbstractDB open(String url, String filePath, Properties properties) throws SQLException {
|
||||
return LimboDBFactory.open(url, filePath, properties);
|
||||
this.database = db;
|
||||
this.connectionPtr = db.connect();
|
||||
}
|
||||
|
||||
private static AbstractDB open(String url, String filePath, Properties properties)
|
||||
throws SQLException {
|
||||
return LimboDBFactory.open(url, filePath, properties);
|
||||
}
|
||||
|
||||
protected void checkOpen() throws SQLException {
|
||||
if (isClosed()) throw new SQLException("database connection closed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
if (isClosed()) return;
|
||||
database.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return database.isClosed();
|
||||
}
|
||||
|
||||
public AbstractDB getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an SQL statement.
|
||||
*
|
||||
* @param sql An SQL statement.
|
||||
* @return Pointer to statement.
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
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 new LimboStatement(sql, prepareUtf8(connectionPtr, sqlBytes));
|
||||
}
|
||||
|
||||
protected void checkOpen() throws SQLException {
|
||||
if (isClosed()) throw new SQLException("database connection closed");
|
||||
}
|
||||
private native long prepareUtf8(long connectionPtr, byte[] sqlUtf8) throws SQLException;
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
if (isClosed()) return;
|
||||
database.close();
|
||||
}
|
||||
/** @return busy timeout in milliseconds. */
|
||||
public int getBusyTimeout() {
|
||||
// TODO: add support for busyTimeout
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return database.isClosed();
|
||||
}
|
||||
// TODO: check whether this is still valid for limbo
|
||||
|
||||
public AbstractDB getDatabase() {
|
||||
return database;
|
||||
}
|
||||
/**
|
||||
* Checks whether the type, concurrency, and holdability settings for a {@link ResultSet} are
|
||||
* supported by the SQLite interface. Supported settings are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>type: {@link ResultSet#TYPE_FORWARD_ONLY}
|
||||
* <li>concurrency: {@link ResultSet#CONCUR_READ_ONLY})
|
||||
* <li>holdability: {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}
|
||||
* </ul>
|
||||
*
|
||||
* @param resultSetType the type setting.
|
||||
* @param resultSetConcurrency the concurrency setting.
|
||||
* @param resultSetHoldability the holdability setting.
|
||||
*/
|
||||
protected void checkCursor(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
|
||||
throws SQLException {
|
||||
if (resultSetType != ResultSet.TYPE_FORWARD_ONLY)
|
||||
throw new SQLException("SQLite only supports TYPE_FORWARD_ONLY cursors");
|
||||
if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY)
|
||||
throw new SQLException("SQLite only supports CONCUR_READ_ONLY cursors");
|
||||
if (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT)
|
||||
throw new SQLException("SQLite only supports closing cursors at commit");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an SQL statement.
|
||||
*
|
||||
* @param sql An SQL statement.
|
||||
* @return Pointer to statement.
|
||||
* @throws SQLException if a database access error occurs.
|
||||
*/
|
||||
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 new LimboStatement(sql, prepareUtf8(connectionPtr, sqlBytes));
|
||||
}
|
||||
public void setBusyTimeout(int busyTimeout) {
|
||||
// TODO: add support for busy timeout
|
||||
}
|
||||
|
||||
private native long prepareUtf8(long connectionPtr, byte[] sqlUtf8) throws SQLException;
|
||||
|
||||
/**
|
||||
* @return busy timeout in milliseconds.
|
||||
*/
|
||||
public int getBusyTimeout() {
|
||||
// TODO: add support for busyTimeout
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: check whether this is still valid for limbo
|
||||
|
||||
/**
|
||||
* Checks whether the type, concurrency, and holdability settings for a {@link ResultSet} are
|
||||
* supported by the SQLite interface. Supported settings are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>type: {@link ResultSet#TYPE_FORWARD_ONLY}
|
||||
* <li>concurrency: {@link ResultSet#CONCUR_READ_ONLY})
|
||||
* <li>holdability: {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}
|
||||
* </ul>
|
||||
*
|
||||
* @param resultSetType the type setting.
|
||||
* @param resultSetConcurrency the concurrency setting.
|
||||
* @param resultSetHoldability the holdability setting.
|
||||
*/
|
||||
protected void checkCursor(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
if (resultSetType != ResultSet.TYPE_FORWARD_ONLY)
|
||||
throw new SQLException("SQLite only supports TYPE_FORWARD_ONLY cursors");
|
||||
if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY)
|
||||
throw new SQLException("SQLite only supports CONCUR_READ_ONLY cursors");
|
||||
if (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT)
|
||||
throw new SQLException("SQLite only supports closing cursors at commit");
|
||||
}
|
||||
|
||||
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(invokedFrom = "limbo_connection.rs")
|
||||
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
|
||||
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
|
||||
}
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
@NativeInvocation(invokedFrom = "limbo_connection.rs")
|
||||
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
|
||||
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import static org.github.tursodatabase.utils.ByteArrayUtils.stringToUtf8ByteArra
|
|||
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
import org.github.tursodatabase.annotations.NativeInvocation;
|
||||
import org.github.tursodatabase.annotations.VisibleForTesting;
|
||||
|
@ -12,105 +11,104 @@ import org.github.tursodatabase.utils.LimboExceptionUtils;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class provides a thin JNI layer over the SQLite3 C API.
|
||||
*/
|
||||
/** 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 dbPointer;
|
||||
private boolean isOpen;
|
||||
private static final Logger logger = LoggerFactory.getLogger(LimboDB.class);
|
||||
// Pointer to database instance
|
||||
private long dbPointer;
|
||||
private boolean isOpen;
|
||||
|
||||
private static boolean isLoaded;
|
||||
private ReentrantLock dbLock = new ReentrantLock();
|
||||
private static boolean isLoaded;
|
||||
private ReentrantLock dbLock = new ReentrantLock();
|
||||
|
||||
static {
|
||||
if ("The Android Project".equals(System.getProperty("java.vm.vendor"))) {
|
||||
// TODO
|
||||
} else {
|
||||
// continue with non Android execution path
|
||||
isLoaded = false;
|
||||
}
|
||||
static {
|
||||
if ("The Android Project".equals(System.getProperty("java.vm.vendor"))) {
|
||||
// TODO
|
||||
} else {
|
||||
// continue with non Android execution path
|
||||
isLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Loads the SQLite interface backend. */
|
||||
public static void load() {
|
||||
if (isLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the SQLite interface backend.
|
||||
*/
|
||||
public static void load() {
|
||||
if (isLoaded) {return;}
|
||||
try {
|
||||
System.loadLibrary("_limbo_java");
|
||||
} finally {
|
||||
isLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
System.loadLibrary("_limbo_java");
|
||||
} finally {
|
||||
isLoaded = true;
|
||||
}
|
||||
/**
|
||||
* @param url e.g. "jdbc:sqlite:fileName
|
||||
* @param filePath e.g. path to file
|
||||
*/
|
||||
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 filePath) {
|
||||
super(url, filePath);
|
||||
}
|
||||
|
||||
// WRAPPER FUNCTIONS ////////////////////////////////////////////
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
protected native long openUtf8(byte[] file, int openFlags) throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
protected native void close0() throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
native int execUtf8(byte[] sqlUtf8) throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
public native void interrupt();
|
||||
|
||||
@Override
|
||||
protected void open0(String filePath, int openFlags) throws SQLException {
|
||||
if (isOpen) {
|
||||
throw LimboExceptionUtils.buildLimboException(
|
||||
LimboErrorCode.LIMBO_ETC.code, "Already opened");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url e.g. "jdbc:sqlite:fileName
|
||||
* @param filePath e.g. path to file
|
||||
*/
|
||||
public static LimboDB create(String url, String filePath) throws SQLException {
|
||||
return new LimboDB(url, filePath);
|
||||
byte[] filePathBytes = stringToUtf8ByteArray(filePath);
|
||||
if (filePathBytes == null) {
|
||||
throw LimboExceptionUtils.buildLimboException(
|
||||
LimboErrorCode.LIMBO_ETC.code,
|
||||
"File path cannot be converted to byteArray. File name: " + filePath);
|
||||
}
|
||||
|
||||
// TODO: receive config as argument
|
||||
private LimboDB(String url, String filePath) {
|
||||
super(url, filePath);
|
||||
}
|
||||
dbPointer = openUtf8(filePathBytes, openFlags);
|
||||
isOpen = true;
|
||||
}
|
||||
|
||||
// WRAPPER FUNCTIONS ////////////////////////////////////////////
|
||||
@Override
|
||||
public long connect() throws SQLException {
|
||||
return connect0(dbPointer);
|
||||
}
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
protected native long openUtf8(byte[] file, int openFlags) throws SQLException;
|
||||
private native long connect0(long databasePtr) throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
protected native void close0() throws SQLException;
|
||||
@VisibleForTesting
|
||||
native void throwJavaException(int errorCode) throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
native int execUtf8(byte[] sqlUtf8) throws SQLException;
|
||||
|
||||
// TODO: add support for JNI
|
||||
@Override
|
||||
public native void interrupt();
|
||||
|
||||
@Override
|
||||
protected void open0(String filePath, int openFlags) throws SQLException {
|
||||
if (isOpen) {
|
||||
throw LimboExceptionUtils.buildLimboException(LimboErrorCode.LIMBO_ETC.code, "Already opened");
|
||||
}
|
||||
|
||||
byte[] filePathBytes = stringToUtf8ByteArray(filePath);
|
||||
if (filePathBytes == null) {
|
||||
throw LimboExceptionUtils.buildLimboException(
|
||||
LimboErrorCode.LIMBO_ETC.code,
|
||||
"File path cannot be converted to byteArray. File name: " + filePath);
|
||||
}
|
||||
|
||||
dbPointer = openUtf8(filePathBytes, openFlags);
|
||||
isOpen = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long connect() throws SQLException {
|
||||
return connect0(dbPointer);
|
||||
}
|
||||
|
||||
private native long connect0(long databasePtr) throws SQLException;
|
||||
|
||||
@VisibleForTesting
|
||||
native void throwJavaException(int errorCode) throws SQLException;
|
||||
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
@NativeInvocation(invokedFrom = "limbo_db.rs")
|
||||
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
|
||||
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
|
||||
}
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
@NativeInvocation(invokedFrom = "limbo_db.rs")
|
||||
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
|
||||
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,43 +5,45 @@ import java.util.Properties;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Factory class for managing and creating instances of {@link LimboDB}.
|
||||
* This class ensures that multiple instances of {@link LimboDB} with the same URL are not created.
|
||||
* Factory class for managing and creating instances of {@link LimboDB}. This class ensures that
|
||||
* multiple instances of {@link LimboDB} with the same URL are not created.
|
||||
*/
|
||||
public class LimboDBFactory {
|
||||
|
||||
private static final ConcurrentHashMap<String, LimboDB> databaseHolder = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, LimboDB> databaseHolder =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* If a database with the same URL already exists, it returns the existing instance.
|
||||
* Otherwise, it creates a new instance and stores it in the database holder.
|
||||
*
|
||||
* @param url the URL of the database
|
||||
* @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 filePath, Properties properties) throws SQLException {
|
||||
if (databaseHolder.containsKey(url)) {
|
||||
return databaseHolder.get(url);
|
||||
}
|
||||
|
||||
if (filePath.isEmpty()) {
|
||||
throw new IllegalArgumentException("filePath should not be empty");
|
||||
}
|
||||
|
||||
final LimboDB database;
|
||||
try {
|
||||
LimboDB.load();
|
||||
database = LimboDB.create(url, filePath);
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Error opening connection", e);
|
||||
}
|
||||
|
||||
database.open(0);
|
||||
databaseHolder.put(url, database);
|
||||
return database;
|
||||
/**
|
||||
* If a database with the same URL already exists, it returns the existing instance. Otherwise, it
|
||||
* creates a new instance and stores it in the database holder.
|
||||
*
|
||||
* @param url the URL of the database
|
||||
* @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 filePath, Properties properties)
|
||||
throws SQLException {
|
||||
if (databaseHolder.containsKey(url)) {
|
||||
return databaseHolder.get(url);
|
||||
}
|
||||
|
||||
if (filePath.isEmpty()) {
|
||||
throw new IllegalArgumentException("filePath should not be empty");
|
||||
}
|
||||
|
||||
final LimboDB database;
|
||||
try {
|
||||
LimboDB.load();
|
||||
database = LimboDB.create(url, filePath);
|
||||
} catch (Exception e) {
|
||||
throw new SQLException("Error opening connection", e);
|
||||
}
|
||||
|
||||
database.open(0);
|
||||
databaseHolder.put(url, database);
|
||||
return database;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,116 +1,119 @@
|
|||
package org.github.tursodatabase.core;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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 class LimboResultSet {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LimboResultSet.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(LimboResultSet.class);
|
||||
|
||||
private final LimboStatement statement;
|
||||
private final LimboStatement statement;
|
||||
|
||||
// Whether the result set does not have any rows.
|
||||
private boolean isEmptyResultSet = false;
|
||||
// If the result set is open. Doesn't mean it has results.
|
||||
private boolean open;
|
||||
// Maximum number of rows as set by the statement
|
||||
private long maxRows;
|
||||
// number of current row, starts at 1 (0 is used to represent loading data)
|
||||
private int row = 0;
|
||||
private boolean pastLastRow = false;
|
||||
// Whether the result set does not have any rows.
|
||||
private boolean isEmptyResultSet = false;
|
||||
// If the result set is open. Doesn't mean it has results.
|
||||
private boolean open;
|
||||
// Maximum number of rows as set by the statement
|
||||
private long maxRows;
|
||||
// number of current row, starts at 1 (0 is used to represent loading data)
|
||||
private int row = 0;
|
||||
private boolean pastLastRow = false;
|
||||
|
||||
@Nullable
|
||||
private LimboStepResult lastStepResult;
|
||||
@Nullable private LimboStepResult lastStepResult;
|
||||
|
||||
public static LimboResultSet of(LimboStatement statement) {
|
||||
return new LimboResultSet(statement);
|
||||
public static LimboResultSet of(LimboStatement statement) {
|
||||
return new LimboResultSet(statement);
|
||||
}
|
||||
|
||||
private LimboResultSet(LimboStatement statement) {
|
||||
this.open = true;
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor forward one row from its current position. A {@link LimboResultSet} cursor is
|
||||
* initially positioned before the first fow; the first call to the method <code>next</code> makes
|
||||
* the first row the current row; the second call makes the second row the current row, and so on.
|
||||
* When a call to the <code>next</code> method returns <code>false</code>, the cursor is
|
||||
* positioned after the last row.
|
||||
*
|
||||
* <p>Note that limbo only supports <code>ResultSet.TYPE_FORWARD_ONLY</code>, which means that the
|
||||
* cursor can only move forward.
|
||||
*/
|
||||
public boolean next() throws SQLException {
|
||||
if (!open || isEmptyResultSet || pastLastRow) {
|
||||
return false; // completed ResultSet
|
||||
}
|
||||
|
||||
private LimboResultSet(LimboStatement statement) {
|
||||
this.open = true;
|
||||
this.statement = statement;
|
||||
if (maxRows != 0 && row == maxRows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor forward one row from its current position. A {@link LimboResultSet} cursor is initially positioned
|
||||
* before the first fow; the first call to the method <code>next</code> makes the first row the current row; the second call
|
||||
* makes the second row the current row, and so on.
|
||||
* When a call to the <code>next</code> method returns <code>false</code>, the cursor is positioned after the last row.
|
||||
* <p>
|
||||
* Note that limbo only supports <code>ResultSet.TYPE_FORWARD_ONLY</code>, which means that the cursor can only move forward.
|
||||
*/
|
||||
public boolean next() throws SQLException {
|
||||
if (!open || isEmptyResultSet || pastLastRow) {
|
||||
return false; // completed ResultSet
|
||||
}
|
||||
|
||||
if (maxRows != 0 && row == maxRows) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lastStepResult = this.statement.step();
|
||||
log.debug("lastStepResult: {}", lastStepResult);
|
||||
if (lastStepResult.isRow()) {
|
||||
row++;
|
||||
}
|
||||
|
||||
if (lastStepResult.isInInvalidState()) {
|
||||
open = false;
|
||||
throw new SQLException("step() returned invalid result: " + lastStepResult);
|
||||
}
|
||||
|
||||
pastLastRow = lastStepResult.isDone();
|
||||
if (pastLastRow) {
|
||||
open = false;
|
||||
}
|
||||
return !pastLastRow;
|
||||
lastStepResult = this.statement.step();
|
||||
log.debug("lastStepResult: {}", lastStepResult);
|
||||
if (lastStepResult.isRow()) {
|
||||
row++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the last step result has returned row result.
|
||||
*/
|
||||
public boolean hasLastStepReturnedRow() {
|
||||
return lastStepResult != null && lastStepResult.isRow();
|
||||
if (lastStepResult.isInInvalidState()) {
|
||||
open = false;
|
||||
throw new SQLException("step() returned invalid result: " + lastStepResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the status of the result set.
|
||||
*
|
||||
* @return true if it's ready to iterate over the result set; false otherwise.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
pastLastRow = lastStepResult.isDone();
|
||||
if (pastLastRow) {
|
||||
open = false;
|
||||
}
|
||||
return !pastLastRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SQLException if not {@link #open}
|
||||
*/
|
||||
public void checkOpen() throws SQLException {
|
||||
if (!open) {
|
||||
throw new SQLException("ResultSet closed");
|
||||
}
|
||||
}
|
||||
/** Checks whether the last step result has returned row result. */
|
||||
public boolean hasLastStepReturnedRow() {
|
||||
return lastStepResult != null && lastStepResult.isRow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboResultSet{" +
|
||||
"statement=" + statement +
|
||||
", isEmptyResultSet=" + isEmptyResultSet +
|
||||
", open=" + open +
|
||||
", maxRows=" + maxRows +
|
||||
", row=" + row +
|
||||
", pastLastRow=" + pastLastRow +
|
||||
", lastResult=" + lastStepResult +
|
||||
'}';
|
||||
/**
|
||||
* Checks the status of the result set.
|
||||
*
|
||||
* @return true if it's ready to iterate over the result set; false otherwise.
|
||||
*/
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
/** @throws SQLException if not {@link #open} */
|
||||
public void checkOpen() throws SQLException {
|
||||
if (!open) {
|
||||
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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
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;
|
||||
|
@ -9,68 +8,73 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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 {
|
||||
private static final Logger log = LoggerFactory.getLogger(LimboStatement.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(LimboStatement.class);
|
||||
|
||||
private final String sql;
|
||||
private final long statementPointer;
|
||||
private final LimboResultSet resultSet;
|
||||
private final String sql;
|
||||
private final long statementPointer;
|
||||
private final LimboResultSet resultSet;
|
||||
|
||||
// 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);
|
||||
log.debug("Creating statement with sql: {}", this.sql);
|
||||
// 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);
|
||||
log.debug("Creating statement with sql: {}", this.sql);
|
||||
}
|
||||
|
||||
public LimboResultSet getResultSet() {
|
||||
return resultSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
LimboStepResult step() throws SQLException {
|
||||
final LimboStepResult result = step(this.statementPointer);
|
||||
if (result == null) {
|
||||
throw new SQLException("step() returned null, which is only returned when an error occurs");
|
||||
}
|
||||
|
||||
public LimboResultSet getResultSet() {
|
||||
return resultSet;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
private native LimboStepResult step(long stmtPointer) throws SQLException;
|
||||
|
||||
LimboStepResult step() throws SQLException {
|
||||
final LimboStepResult result = step(this.statementPointer);
|
||||
if (result == null) {
|
||||
throw new SQLException("step() returned null, which is only returned when an error occurs");
|
||||
}
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
@NativeInvocation(invokedFrom = "limbo_statement.rs")
|
||||
private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException {
|
||||
LimboExceptionUtils.throwLimboException(errorCode, errorMessageBytes);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private native LimboStepResult step(long stmtPointer) throws SQLException;
|
||||
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
@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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboStatement{"
|
||||
+ "statementPointer="
|
||||
+ statementPointer
|
||||
+ ", sql='"
|
||||
+ sql
|
||||
+ '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,79 +1,78 @@
|
|||
package org.github.tursodatabase.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.github.tursodatabase.annotations.NativeInvocation;
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents the step result of limbo's statement's step function.
|
||||
*/
|
||||
/** Represents the step result of limbo's statement's step function. */
|
||||
public class LimboStepResult {
|
||||
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;
|
||||
// Indicates that the database file could not be written because of concurrent activity by some other connection
|
||||
private static final int STEP_RESULT_ID_BUSY = 50;
|
||||
private 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;
|
||||
// Indicates that the database file could not be written because of concurrent activity by some
|
||||
// other connection
|
||||
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;
|
||||
@Nullable
|
||||
private final Object[] result;
|
||||
// Identifier for limbo's StepResult
|
||||
private final int stepResultId;
|
||||
@Nullable private final Object[] result;
|
||||
|
||||
@NativeInvocation(invokedFrom = "limbo_statement.rs")
|
||||
public LimboStepResult(int stepResultId) {
|
||||
this.stepResultId = stepResultId;
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
public boolean isInInvalidState() {
|
||||
// current implementation doesn't allow STEP_RESULT_ID_IO to be returned
|
||||
return stepResultId == STEP_RESULT_ID_IO ||
|
||||
stepResultId == STEP_RESULT_ID_INTERRUPT ||
|
||||
stepResultId == STEP_RESULT_ID_BUSY ||
|
||||
stepResultId == STEP_RESULT_ID_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboStepResult{" +
|
||||
"stepResultName=" + getStepResultName() +
|
||||
", result=" + Arrays.toString(result) +
|
||||
'}';
|
||||
}
|
||||
|
||||
private String getStepResultName() {
|
||||
switch (stepResultId) {
|
||||
case STEP_RESULT_ID_ROW:
|
||||
return "ROW";
|
||||
case STEP_RESULT_ID_IO:
|
||||
return "IO";
|
||||
case STEP_RESULT_ID_DONE:
|
||||
return "DONE";
|
||||
case STEP_RESULT_ID_INTERRUPT:
|
||||
return "INTERRUPT";
|
||||
case STEP_RESULT_ID_BUSY:
|
||||
return "BUSY";
|
||||
case STEP_RESULT_ID_ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@NativeInvocation(invokedFrom = "limbo_statement.rs")
|
||||
public LimboStepResult(int stepResultId) {
|
||||
this.stepResultId = stepResultId;
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
public boolean isInInvalidState() {
|
||||
// current implementation doesn't allow STEP_RESULT_ID_IO to be returned
|
||||
return stepResultId == STEP_RESULT_ID_IO
|
||||
|| stepResultId == STEP_RESULT_ID_INTERRUPT
|
||||
|| stepResultId == STEP_RESULT_ID_BUSY
|
||||
|| stepResultId == STEP_RESULT_ID_ERROR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LimboStepResult{"
|
||||
+ "stepResultName="
|
||||
+ getStepResultName()
|
||||
+ ", result="
|
||||
+ Arrays.toString(result)
|
||||
+ '}';
|
||||
}
|
||||
|
||||
private String getStepResultName() {
|
||||
switch (stepResultId) {
|
||||
case STEP_RESULT_ID_ROW:
|
||||
return "ROW";
|
||||
case STEP_RESULT_ID_IO:
|
||||
return "IO";
|
||||
case STEP_RESULT_ID_DONE:
|
||||
return "DONE";
|
||||
case STEP_RESULT_ID_INTERRUPT:
|
||||
return "INTERRUPT";
|
||||
case STEP_RESULT_ID_BUSY:
|
||||
return "BUSY";
|
||||
case STEP_RESULT_ID_ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,93 +15,91 @@
|
|||
*/
|
||||
package org.github.tursodatabase.core;
|
||||
|
||||
/**
|
||||
* Sqlite error codes.
|
||||
*/
|
||||
/** Sqlite error codes. */
|
||||
public class SqliteCode {
|
||||
/** Successful result */
|
||||
public static final int SQLITE_OK = 0;
|
||||
/** Successful result */
|
||||
public static final int SQLITE_OK = 0;
|
||||
|
||||
/** SQL error or missing database */
|
||||
public static final int SQLITE_ERROR = 1;
|
||||
/** SQL error or missing database */
|
||||
public static final int SQLITE_ERROR = 1;
|
||||
|
||||
/** An internal logic error in SQLite */
|
||||
public static final int SQLITE_INTERNAL = 2;
|
||||
/** An internal logic error in SQLite */
|
||||
public static final int SQLITE_INTERNAL = 2;
|
||||
|
||||
/** Access permission denied */
|
||||
public static final int SQLITE_PERM = 3;
|
||||
/** Access permission denied */
|
||||
public static final int SQLITE_PERM = 3;
|
||||
|
||||
/** Callback routine requested an abort */
|
||||
public static final int SQLITE_ABORT = 4;
|
||||
/** Callback routine requested an abort */
|
||||
public static final int SQLITE_ABORT = 4;
|
||||
|
||||
/** The database file is locked */
|
||||
public static final int SQLITE_BUSY = 5;
|
||||
/** The database file is locked */
|
||||
public static final int SQLITE_BUSY = 5;
|
||||
|
||||
/** A table in the database is locked */
|
||||
public static final int SQLITE_LOCKED = 6;
|
||||
/** A table in the database is locked */
|
||||
public static final int SQLITE_LOCKED = 6;
|
||||
|
||||
/** A malloc() failed */
|
||||
public static final int SQLITE_NOMEM = 7;
|
||||
/** A malloc() failed */
|
||||
public static final int SQLITE_NOMEM = 7;
|
||||
|
||||
/** Attempt to write a readonly database */
|
||||
public static final int SQLITE_READONLY = 8;
|
||||
/** Attempt to write a readonly database */
|
||||
public static final int SQLITE_READONLY = 8;
|
||||
|
||||
/** Operation terminated by sqlite_interrupt() */
|
||||
public static final int SQLITE_INTERRUPT = 9;
|
||||
/** Operation terminated by sqlite_interrupt() */
|
||||
public static final int SQLITE_INTERRUPT = 9;
|
||||
|
||||
/** Some kind of disk I/O error occurred */
|
||||
public static final int SQLITE_IOERR = 10;
|
||||
/** Some kind of disk I/O error occurred */
|
||||
public static final int SQLITE_IOERR = 10;
|
||||
|
||||
/** The database disk image is malformed */
|
||||
public static final int SQLITE_CORRUPT = 11;
|
||||
/** The database disk image is malformed */
|
||||
public static final int SQLITE_CORRUPT = 11;
|
||||
|
||||
/** (Internal Only) Table or record not found */
|
||||
public static final int SQLITE_NOTFOUND = 12;
|
||||
/** (Internal Only) Table or record not found */
|
||||
public static final int SQLITE_NOTFOUND = 12;
|
||||
|
||||
/** Insertion failed because database is full */
|
||||
public static final int SQLITE_FULL = 13;
|
||||
/** Insertion failed because database is full */
|
||||
public static final int SQLITE_FULL = 13;
|
||||
|
||||
/** Unable to open the database file */
|
||||
public static final int SQLITE_CANTOPEN = 14;
|
||||
/** Unable to open the database file */
|
||||
public static final int SQLITE_CANTOPEN = 14;
|
||||
|
||||
/** Database lock protocol error */
|
||||
public static final int SQLITE_PROTOCOL = 15;
|
||||
/** Database lock protocol error */
|
||||
public static final int SQLITE_PROTOCOL = 15;
|
||||
|
||||
/** (Internal Only) Database table is empty */
|
||||
public static final int SQLITE_EMPTY = 16;
|
||||
/** (Internal Only) Database table is empty */
|
||||
public static final int SQLITE_EMPTY = 16;
|
||||
|
||||
/** The database schema changed */
|
||||
public static final int SQLITE_SCHEMA = 17;
|
||||
/** The database schema changed */
|
||||
public static final int SQLITE_SCHEMA = 17;
|
||||
|
||||
/** Too much data for one row of a table */
|
||||
public static final int SQLITE_TOOBIG = 18;
|
||||
/** Too much data for one row of a table */
|
||||
public static final int SQLITE_TOOBIG = 18;
|
||||
|
||||
/** Abort due to constraint violation */
|
||||
public static final int SQLITE_CONSTRAINT = 19;
|
||||
/** Abort due to constraint violation */
|
||||
public static final int SQLITE_CONSTRAINT = 19;
|
||||
|
||||
/** Data type mismatch */
|
||||
public static final int SQLITE_MISMATCH = 20;
|
||||
/** Data type mismatch */
|
||||
public static final int SQLITE_MISMATCH = 20;
|
||||
|
||||
/** Library used incorrectly */
|
||||
public static final int SQLITE_MISUSE = 21;
|
||||
/** Library used incorrectly */
|
||||
public static final int SQLITE_MISUSE = 21;
|
||||
|
||||
/** Uses OS features not supported on host */
|
||||
public static final int SQLITE_NOLFS = 22;
|
||||
/** Uses OS features not supported on host */
|
||||
public static final int SQLITE_NOLFS = 22;
|
||||
|
||||
/** Authorization denied */
|
||||
public static final int SQLITE_AUTH = 23;
|
||||
/** Authorization denied */
|
||||
public static final int SQLITE_AUTH = 23;
|
||||
|
||||
/** sqlite_step() has another row ready */
|
||||
public static final int SQLITE_ROW = 100;
|
||||
/** sqlite_step() has another row ready */
|
||||
public static final int SQLITE_ROW = 100;
|
||||
|
||||
/** sqlite_step() has finished executing */
|
||||
public static final int SQLITE_DONE = 101;
|
||||
/** sqlite_step() has finished executing */
|
||||
public static final int SQLITE_DONE = 101;
|
||||
|
||||
// types returned by sqlite3_column_type()
|
||||
// types returned by sqlite3_column_type()
|
||||
|
||||
public static final int SQLITE_INTEGER = 1;
|
||||
public static final int SQLITE_FLOAT = 2;
|
||||
public static final int SQLITE_TEXT = 3;
|
||||
public static final int SQLITE_BLOB = 4;
|
||||
public static final int SQLITE_NULL = 5;
|
||||
public static final int SQLITE_INTEGER = 1;
|
||||
public static final int SQLITE_FLOAT = 2;
|
||||
public static final int SQLITE_TEXT = 3;
|
||||
public static final int SQLITE_BLOB = 4;
|
||||
public static final int SQLITE_NULL = 5;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
package org.github.tursodatabase.exceptions;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class LimboException extends SQLException {
|
||||
private final LimboErrorCode resultCode;
|
||||
private final LimboErrorCode resultCode;
|
||||
|
||||
public LimboException(String message, LimboErrorCode resultCode) {
|
||||
super(message, null, resultCode.code & 0xff);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
public LimboException(String message, LimboErrorCode resultCode) {
|
||||
super(message, null, resultCode.code & 0xff);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public LimboErrorCode getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
public LimboErrorCode getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,353 +1,357 @@
|
|||
package org.github.tursodatabase.jdbc4;
|
||||
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executor;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
|
||||
public class JDBC4Connection extends LimboConnection {
|
||||
|
||||
public JDBC4Connection(String url, String filePath) throws SQLException {
|
||||
super(url, filePath);
|
||||
}
|
||||
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);
|
||||
}
|
||||
public JDBC4Connection(String url, String filePath, Properties properties) throws SQLException {
|
||||
super(url, filePath, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return createStatement(
|
||||
ResultSet.TYPE_FORWARD_ONLY,
|
||||
ResultSet.CONCUR_READ_ONLY,
|
||||
ResultSet.CLOSE_CURSORS_AT_COMMIT
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return createStatement(
|
||||
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
return createStatement(resultSetType, resultSetConcurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
}
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
return createStatement(resultSetType, resultSetConcurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
checkOpen();
|
||||
checkCursor(resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
@Override
|
||||
public Statement createStatement(
|
||||
int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
checkOpen();
|
||||
checkCursor(resultSetType, resultSetConcurrency, resultSetHoldability);
|
||||
|
||||
return new JDBC4Statement(this);
|
||||
}
|
||||
return new JDBC4Statement(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public CallableStatement prepareCall(String sql) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public String nativeSQL(String sql) throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public String nativeSQL(String sql) throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setAutoCommit(boolean autoCommit) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCatalog(String catalog) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setCatalog(String catalog) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionIsolation(int level) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setTransactionIsolation(int level) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||
// TODO
|
||||
return new HashMap<>();
|
||||
}
|
||||
@Override
|
||||
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||
// TODO
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHoldability(int holdability) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setHoldability(int holdability) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Savepoint setSavepoint(String name) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Savepoint setSavepoint(String name) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void rollback(Savepoint savepoint) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(
|
||||
String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
|
||||
throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public CallableStatement prepareCall(
|
||||
String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
|
||||
throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Clob createClob() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Clob createClob() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Blob createBlob() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Blob createBlob() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public NClob createNClob() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public NClob createNClob() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(int timeout) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isValid(int timeout) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(String name, String value) throws SQLClientInfoException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setClientInfo(String name, String value) throws SQLClientInfoException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(Properties properties) throws SQLClientInfoException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setClientInfo(Properties properties) throws SQLClientInfoException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientInfo(String name) throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
public String getClientInfo(String name) throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSchema(String schema) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setSchema(String schema) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public String getSchema() throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public String getSchema() throws SQLException {
|
||||
// TODO
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(Executor executor) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void abort(Executor executor) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
@Override
|
||||
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,6 @@ import java.sql.SQLException;
|
|||
import java.sql.SQLWarning;
|
||||
import java.sql.Statement;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.github.tursodatabase.annotations.SkipNullableCheck;
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
|
@ -17,359 +16,366 @@ import org.github.tursodatabase.core.LimboStatement;
|
|||
|
||||
public class JDBC4Statement implements Statement {
|
||||
|
||||
private final LimboConnection connection;
|
||||
@Nullable
|
||||
private LimboStatement statement = null;
|
||||
private final LimboConnection connection;
|
||||
@Nullable private LimboStatement statement = null;
|
||||
|
||||
private boolean closed;
|
||||
private boolean closeOnCompletion;
|
||||
private boolean closed;
|
||||
private boolean closeOnCompletion;
|
||||
|
||||
private final int resultSetType;
|
||||
private final int resultSetConcurrency;
|
||||
private final int resultSetHoldability;
|
||||
private final int resultSetType;
|
||||
private final int resultSetConcurrency;
|
||||
private final int resultSetHoldability;
|
||||
|
||||
private int queryTimeoutSeconds;
|
||||
private long updateCount;
|
||||
private boolean exhaustedResults = false;
|
||||
private int queryTimeoutSeconds;
|
||||
private long updateCount;
|
||||
private boolean exhaustedResults = false;
|
||||
|
||||
private ReentrantLock connectionLock = new ReentrantLock();
|
||||
private ReentrantLock connectionLock = new ReentrantLock();
|
||||
|
||||
public JDBC4Statement(LimboConnection connection) {
|
||||
this(connection, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
|
||||
ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
public JDBC4Statement(LimboConnection connection) {
|
||||
this(
|
||||
connection,
|
||||
ResultSet.TYPE_FORWARD_ONLY,
|
||||
ResultSet.CONCUR_READ_ONLY,
|
||||
ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
}
|
||||
|
||||
public JDBC4Statement(
|
||||
LimboConnection connection,
|
||||
int resultSetType,
|
||||
int resultSetConcurrency,
|
||||
int resultSetHoldability) {
|
||||
this.connection = connection;
|
||||
this.resultSetType = resultSetType;
|
||||
this.resultSetConcurrency = resultSetConcurrency;
|
||||
this.resultSetHoldability = resultSetHoldability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet executeQuery(String sql) throws SQLException {
|
||||
execute(sql);
|
||||
|
||||
requireNonNull(statement, "statement should not be null after running execute method");
|
||||
return new JDBC4ResultSet(statement.getResultSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql) throws SQLException {
|
||||
execute(sql);
|
||||
|
||||
requireNonNull(statement, "statement should not be null after running execute method");
|
||||
final LimboResultSet resultSet = statement.getResultSet();
|
||||
while (resultSet.isOpen()) {
|
||||
resultSet.next();
|
||||
}
|
||||
|
||||
public JDBC4Statement(LimboConnection connection, int resultSetType, int resultSetConcurrency,
|
||||
int resultSetHoldability) {
|
||||
this.connection = connection;
|
||||
this.resultSetType = resultSetType;
|
||||
this.resultSetConcurrency = resultSetConcurrency;
|
||||
this.resultSetHoldability = resultSetHoldability;
|
||||
// TODO: return update count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
clearGeneratedKeys();
|
||||
internalClose();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxFieldSize() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxFieldSize(int max) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxRows() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxRows(int max) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEscapeProcessing(boolean enable) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQueryTimeout() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQueryTimeout(int seconds) throws SQLException {
|
||||
if (seconds < 0) {
|
||||
throw new SQLException("Query timeout must be greater than 0");
|
||||
}
|
||||
this.queryTimeoutSeconds = seconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursorName(String name) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>execute</code> method executes an SQL statement and indicates the form of the first
|
||||
* result. You must then use the methods <code>getResultSet</code> or <code>getUpdateCount</code>
|
||||
* to retrieve the result, and <code>getMoreResults</code> to move to any subsequent result(s).
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(String sql) throws SQLException {
|
||||
internalClose();
|
||||
|
||||
return this.withConnectionTimeout(
|
||||
() -> {
|
||||
try {
|
||||
// TODO: if sql is a readOnly query, do we still need the locks?
|
||||
connectionLock.lock();
|
||||
statement = connection.prepare(sql);
|
||||
final boolean result = statement.execute();
|
||||
updateGeneratedKeys();
|
||||
exhaustedResults = false;
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet getResultSet() throws SQLException {
|
||||
requireNonNull(statement, "statement is null");
|
||||
return new JDBC4ResultSet(statement.getResultSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateCount() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMoreResults() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFetchDirection(int direction) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchDirection() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFetchSize(int rows) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchSize() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResultSetConcurrency() {
|
||||
return resultSetConcurrency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResultSetType() {
|
||||
return resultSetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(String sql) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBatch() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch() throws SQLException {
|
||||
// TODO
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Connection getConnection() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMoreResults(int current) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public ResultSet getGeneratedKeys() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String sql, String[] columnNames) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResultSetHoldability() {
|
||||
return resultSetHoldability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPoolable(boolean poolable) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoolable() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOnCompletion() throws SQLException {
|
||||
if (closed) {
|
||||
throw new SQLException("statement is closed");
|
||||
}
|
||||
closeOnCompletion = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the statement should be closed automatically when all its dependent result
|
||||
* sets are closed.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCloseOnCompletion() throws SQLException {
|
||||
if (closed) {
|
||||
throw new SQLException("statement is closed");
|
||||
}
|
||||
return closeOnCompletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
// TODO
|
||||
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) {
|
||||
// TODO: set busy timeout
|
||||
connection.setBusyTimeout(1000 * queryTimeoutSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet executeQuery(String sql) throws SQLException {
|
||||
execute(sql);
|
||||
|
||||
requireNonNull(statement, "statement should not be null after running execute method");
|
||||
return new JDBC4ResultSet(statement.getResultSet());
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
if (queryTimeoutSeconds > 0) {
|
||||
connection.setBusyTimeout(originalBusyTimeoutMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql) throws SQLException {
|
||||
execute(sql);
|
||||
|
||||
requireNonNull(statement, "statement should not be null after running execute method");
|
||||
final LimboResultSet resultSet = statement.getResultSet();
|
||||
while (resultSet.isOpen()) {
|
||||
resultSet.next();
|
||||
}
|
||||
|
||||
// TODO: return update count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws SQLException {
|
||||
clearGeneratedKeys();
|
||||
internalClose();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxFieldSize() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxFieldSize(int max) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxRows() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxRows(int max) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEscapeProcessing(boolean enable) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQueryTimeout() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQueryTimeout(int seconds) throws SQLException {
|
||||
if (seconds < 0) {
|
||||
throw new SQLException("Query timeout must be greater than 0");
|
||||
}
|
||||
this.queryTimeoutSeconds = seconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursorName(String name) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>execute</code> method executes an SQL statement and indicates the
|
||||
* form of the first result. You must then use the methods
|
||||
* <code>getResultSet</code> or <code>getUpdateCount</code>
|
||||
* to retrieve the result, and <code>getMoreResults</code> to
|
||||
* move to any subsequent result(s).
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(String sql) throws SQLException {
|
||||
internalClose();
|
||||
|
||||
return this.withConnectionTimeout(
|
||||
() -> {
|
||||
try {
|
||||
// TODO: if sql is a readOnly query, do we still need the locks?
|
||||
connectionLock.lock();
|
||||
statement = connection.prepare(sql);
|
||||
final boolean result = statement.execute();
|
||||
updateGeneratedKeys();
|
||||
exhaustedResults = false;
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
connectionLock.unlock();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet getResultSet() throws SQLException {
|
||||
requireNonNull(statement, "statement is null");
|
||||
return new JDBC4ResultSet(statement.getResultSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateCount() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMoreResults() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFetchDirection(int direction) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchDirection() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFetchSize(int rows) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchSize() throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResultSetConcurrency() {
|
||||
return resultSetConcurrency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResultSetType() {
|
||||
return resultSetType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBatch(String sql) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBatch() throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] executeBatch() throws SQLException {
|
||||
// TODO
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public Connection getConnection() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getMoreResults(int current) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public ResultSet getGeneratedKeys() throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String sql, String[] columnNames) throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResultSetHoldability() {
|
||||
return resultSetHoldability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPoolable(boolean poolable) throws SQLException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoolable() throws SQLException {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOnCompletion() throws SQLException {
|
||||
if (closed) {throw new SQLException("statement is closed");}
|
||||
closeOnCompletion = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the statement should be closed automatically when all its dependent result sets are closed.
|
||||
*/
|
||||
@Override
|
||||
public boolean isCloseOnCompletion() throws SQLException {
|
||||
if (closed) {throw new SQLException("statement is closed");}
|
||||
return closeOnCompletion;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SkipNullableCheck
|
||||
public <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
// TODO
|
||||
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) {
|
||||
// TODO: set busy timeout
|
||||
connection.setBusyTimeout(1000 * queryTimeoutSeconds);
|
||||
}
|
||||
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
if (queryTimeoutSeconds > 0) {
|
||||
connection.setBusyTimeout(originalBusyTimeoutMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface SQLCallable<T> {
|
||||
T call() throws SQLException;
|
||||
}
|
||||
@FunctionalInterface
|
||||
protected interface SQLCallable<T> {
|
||||
T call() throws SQLException;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
package org.github.tursodatabase.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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 String utf8ByteBufferToString(@Nullable byte[] buffer) {
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static byte[] stringToUtf8ByteArray(@Nullable String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
return str.getBytes(StandardCharsets.UTF_8);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,39 +3,39 @@ package org.github.tursodatabase.utils;
|
|||
import static org.github.tursodatabase.utils.ByteArrayUtils.utf8ByteBufferToString;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
import org.github.tursodatabase.annotations.Nullable;
|
||||
import org.github.tursodatabase.exceptions.LimboException;
|
||||
|
||||
public class LimboExceptionUtils {
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessageBytes Error message.
|
||||
*/
|
||||
public static 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 errorMessageBytes Error message.
|
||||
*/
|
||||
public static 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 static 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws formatted SQLException with error code and message.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorMessage Error message.
|
||||
*/
|
||||
public static 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);
|
||||
}
|
||||
return new LimboException(msg, code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,36 @@
|
|||
package org.github.tursodatabase;
|
||||
|
||||
import org.github.tursodatabase.jdbc4.JDBC4Connection;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
import org.github.tursodatabase.jdbc4.JDBC4Connection;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class IntegrationTest {
|
||||
|
||||
private JDBC4Connection connection;
|
||||
private JDBC4Connection connection;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
connection = new JDBC4Connection(url, filePath, new Properties());
|
||||
}
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
connection = new JDBC4Connection(url, filePath, new Properties());
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_table_multi_inserts_select() throws Exception {
|
||||
Statement stmt = createDefaultStatement();
|
||||
stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
|
||||
stmt.execute("INSERT INTO users VALUES (1, 'seonwoo');");
|
||||
stmt.execute("INSERT INTO users VALUES (2, 'seonwoo');");
|
||||
stmt.execute("INSERT INTO users VALUES (3, 'seonwoo');");
|
||||
stmt.execute("SELECT * FROM users");
|
||||
}
|
||||
@Test
|
||||
void create_table_multi_inserts_select() throws Exception {
|
||||
Statement stmt = createDefaultStatement();
|
||||
stmt.execute("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
|
||||
stmt.execute("INSERT INTO users VALUES (1, 'seonwoo');");
|
||||
stmt.execute("INSERT INTO users VALUES (2, 'seonwoo');");
|
||||
stmt.execute("INSERT INTO users VALUES (3, 'seonwoo');");
|
||||
stmt.execute("SELECT * FROM users");
|
||||
}
|
||||
|
||||
private Statement createDefaultStatement() throws SQLException {
|
||||
return connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
}
|
||||
private Statement createDefaultStatement() throws SQLException {
|
||||
return connection.createStatement(
|
||||
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,33 @@
|
|||
package org.github.tursodatabase;
|
||||
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class JDBCTest {
|
||||
|
||||
@Test
|
||||
void null_is_returned_when_invalid_url_is_passed() throws Exception {
|
||||
LimboConnection connection = JDBC.createConnection("jdbc:invalid:xxx", new Properties());
|
||||
assertThat(connection).isNull();
|
||||
}
|
||||
@Test
|
||||
void null_is_returned_when_invalid_url_is_passed() throws Exception {
|
||||
LimboConnection connection = JDBC.createConnection("jdbc:invalid:xxx", new Properties());
|
||||
assertThat(connection).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void non_null_connection_is_returned_when_valid_url_is_passed() throws Exception {
|
||||
String fileUrl = TestUtils.createTempFile();
|
||||
LimboConnection connection = JDBC.createConnection("jdbc:sqlite:" + fileUrl, new Properties());
|
||||
assertThat(connection).isNotNull();
|
||||
}
|
||||
@Test
|
||||
void non_null_connection_is_returned_when_valid_url_is_passed() throws Exception {
|
||||
String fileUrl = TestUtils.createTempFile();
|
||||
LimboConnection connection = JDBC.createConnection("jdbc:sqlite:" + fileUrl, new Properties());
|
||||
assertThat(connection).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void connection_can_be_retrieved_from_DriverManager() throws SQLException {
|
||||
try (Connection connection = DriverManager.getConnection("jdbc:sqlite:sample.db")) {
|
||||
assertThat(connection).isNotNull();
|
||||
}
|
||||
@Test
|
||||
void connection_can_be_retrieved_from_DriverManager() throws SQLException {
|
||||
try (Connection connection = DriverManager.getConnection("jdbc:sqlite:sample.db")) {
|
||||
assertThat(connection).isNotNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,8 @@ 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();
|
||||
}
|
||||
/** Create temporary file and returns the path. */
|
||||
public static String createTempFile() throws IOException {
|
||||
return Files.createTempFile("limbo_test_db", null).toAbsolutePath().toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,31 @@
|
|||
package org.github.tursodatabase.core;
|
||||
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class LimboDBFactoryTest {
|
||||
|
||||
@Test
|
||||
void single_database_should_be_created_when_urls_are_same() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
LimboDB db1 = LimboDBFactory.open(url, filePath, new Properties());
|
||||
LimboDB db2 = LimboDBFactory.open(url, filePath, new Properties());
|
||||
assertEquals(db1, db2);
|
||||
}
|
||||
@Test
|
||||
void single_database_should_be_created_when_urls_are_same() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
LimboDB db1 = LimboDBFactory.open(url, filePath, new Properties());
|
||||
LimboDB db2 = LimboDBFactory.open(url, filePath, new Properties());
|
||||
assertEquals(db1, db2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void multiple_databases_should_be_created_when_urls_differ() throws Exception {
|
||||
String filePath1 = TestUtils.createTempFile();
|
||||
String filePath2 = TestUtils.createTempFile();
|
||||
String url1 = "jdbc:sqlite:" + filePath1;
|
||||
String url2 = "jdbc:sqlite:" + filePath2;
|
||||
LimboDB db1 = LimboDBFactory.open(url1, filePath1, new Properties());
|
||||
LimboDB db2 = LimboDBFactory.open(url2, filePath2, new Properties());
|
||||
assertNotEquals(db1, db2);
|
||||
}
|
||||
@Test
|
||||
void multiple_databases_should_be_created_when_urls_differ() throws Exception {
|
||||
String filePath1 = TestUtils.createTempFile();
|
||||
String filePath2 = TestUtils.createTempFile();
|
||||
String url1 = "jdbc:sqlite:" + filePath1;
|
||||
String url2 = "jdbc:sqlite:" + filePath2;
|
||||
LimboDB db1 = LimboDBFactory.open(url1, filePath1, new Properties());
|
||||
LimboDB db2 = LimboDBFactory.open(url2, filePath2, new Properties());
|
||||
assertNotEquals(db1, db2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,47 @@
|
|||
package org.github.tursodatabase.core;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import org.github.tursodatabase.LimboErrorCode;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.github.tursodatabase.exceptions.LimboException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class LimboDBTest {
|
||||
|
||||
@Test
|
||||
void db_should_open_normally() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB.load();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite" + dbPath, dbPath);
|
||||
db.open(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_throw_exception_when_opened_twice() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB.load();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath);
|
||||
db.open(0);
|
||||
|
||||
assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwJavaException_should_throw_appropriate_java_exception() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB.load();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath);
|
||||
|
||||
final int limboExceptionCode = LimboErrorCode.LIMBO_ETC.code;
|
||||
try {
|
||||
db.throwJavaException(limboExceptionCode);
|
||||
} catch (Exception e) {
|
||||
assertThat(e).isInstanceOf(LimboException.class);
|
||||
LimboException limboException = (LimboException) e;
|
||||
assertThat(limboException.getResultCode().code).isEqualTo(limboExceptionCode);
|
||||
}
|
||||
@Test
|
||||
void db_should_open_normally() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB.load();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite" + dbPath, dbPath);
|
||||
db.open(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_throw_exception_when_opened_twice() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB.load();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath);
|
||||
db.open(0);
|
||||
|
||||
assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwJavaException_should_throw_appropriate_java_exception() throws Exception {
|
||||
String dbPath = TestUtils.createTempFile();
|
||||
LimboDB.load();
|
||||
LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath);
|
||||
|
||||
final int limboExceptionCode = LimboErrorCode.LIMBO_ETC.code;
|
||||
try {
|
||||
db.throwJavaException(limboExceptionCode);
|
||||
} catch (Exception e) {
|
||||
assertThat(e).isInstanceOf(LimboException.class);
|
||||
LimboException limboException = (LimboException) e;
|
||||
assertThat(limboException.getResultCode().code).isEqualTo(limboExceptionCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +1,68 @@
|
|||
package org.github.tursodatabase.jdbc4;
|
||||
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.github.tursodatabase.core.LimboConnection;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.github.tursodatabase.TestUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class JDBC4ConnectionTest {
|
||||
|
||||
private JDBC4Connection connection;
|
||||
private JDBC4Connection connection;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
connection = new JDBC4Connection(url, filePath, new Properties());
|
||||
}
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
String filePath = TestUtils.createTempFile();
|
||||
String url = "jdbc:sqlite:" + filePath;
|
||||
connection = new JDBC4Connection(url, filePath, new Properties());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_create_statement_valid() throws SQLException {
|
||||
Statement stmt = connection.createStatement();
|
||||
assertNotNull(stmt);
|
||||
assertEquals(ResultSet.TYPE_FORWARD_ONLY, stmt.getResultSetType());
|
||||
assertEquals(ResultSet.CONCUR_READ_ONLY, stmt.getResultSetConcurrency());
|
||||
assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, stmt.getResultSetHoldability());
|
||||
}
|
||||
@Test
|
||||
void test_create_statement_valid() throws SQLException {
|
||||
Statement stmt = connection.createStatement();
|
||||
assertNotNull(stmt);
|
||||
assertEquals(ResultSet.TYPE_FORWARD_ONLY, stmt.getResultSetType());
|
||||
assertEquals(ResultSet.CONCUR_READ_ONLY, stmt.getResultSetConcurrency());
|
||||
assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, stmt.getResultSetHoldability());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_create_statement_with_type_and_concurrency_valid() throws SQLException {
|
||||
Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
|
||||
assertNotNull(stmt);
|
||||
assertEquals(ResultSet.TYPE_FORWARD_ONLY, stmt.getResultSetType());
|
||||
assertEquals(ResultSet.CONCUR_READ_ONLY, stmt.getResultSetConcurrency());
|
||||
}
|
||||
@Test
|
||||
void test_create_statement_with_type_and_concurrency_valid() throws SQLException {
|
||||
Statement stmt =
|
||||
connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
|
||||
assertNotNull(stmt);
|
||||
assertEquals(ResultSet.TYPE_FORWARD_ONLY, stmt.getResultSetType());
|
||||
assertEquals(ResultSet.CONCUR_READ_ONLY, stmt.getResultSetConcurrency());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_create_statement_with_all_params_valid() throws SQLException {
|
||||
Statement stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
assertNotNull(stmt);
|
||||
assertEquals(ResultSet.TYPE_FORWARD_ONLY, stmt.getResultSetType());
|
||||
assertEquals(ResultSet.CONCUR_READ_ONLY, stmt.getResultSetConcurrency());
|
||||
assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, stmt.getResultSetHoldability());
|
||||
}
|
||||
@Test
|
||||
void test_create_statement_with_all_params_valid() throws SQLException {
|
||||
Statement stmt =
|
||||
connection.createStatement(
|
||||
ResultSet.TYPE_FORWARD_ONLY,
|
||||
ResultSet.CONCUR_READ_ONLY,
|
||||
ResultSet.CLOSE_CURSORS_AT_COMMIT);
|
||||
assertNotNull(stmt);
|
||||
assertEquals(ResultSet.TYPE_FORWARD_ONLY, stmt.getResultSetType());
|
||||
assertEquals(ResultSet.CONCUR_READ_ONLY, stmt.getResultSetConcurrency());
|
||||
assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, stmt.getResultSetHoldability());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_create_statement_invalid() {
|
||||
assertThrows(SQLException.class, () -> {
|
||||
connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, -1);
|
||||
@Test
|
||||
void test_create_statement_invalid() {
|
||||
assertThrows(
|
||||
SQLException.class,
|
||||
() -> {
|
||||
connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, -1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void prepare_simple_create_table() throws Exception {
|
||||
connection.prepare("CREATE TABLE users (id INT PRIMARY KEY, username TEXT)");
|
||||
}
|
||||
@Test
|
||||
void prepare_simple_create_table() throws Exception {
|
||||
connection.prepare("CREATE TABLE users (id INT PRIMARY KEY, username TEXT)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,53 +6,55 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
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.Test;
|
||||
|
||||
class JDBC4ResultSetTest {
|
||||
|
||||
private Statement stmt;
|
||||
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);
|
||||
@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 invoking_next_before_the_last_row_should_return_true() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (1, 'sinwoo');");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (2, 'seonwoo');");
|
||||
|
||||
// first call to next occur internally
|
||||
stmt.executeQuery("SELECT * FROM users");
|
||||
ResultSet resultSet = stmt.getResultSet();
|
||||
|
||||
assertTrue(resultSet.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invoking_next_after_the_last_row_should_return_false() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (1, 'sinwoo');");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (2, 'seonwoo');");
|
||||
|
||||
// first call to next occur internally
|
||||
stmt.executeQuery("SELECT * FROM users");
|
||||
ResultSet resultSet = stmt.getResultSet();
|
||||
|
||||
while (resultSet.next()) {
|
||||
// run until next() returns false
|
||||
}
|
||||
|
||||
@Test
|
||||
void invoking_next_before_the_last_row_should_return_true() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (1, 'sinwoo');");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (2, 'seonwoo');");
|
||||
|
||||
// first call to next occur internally
|
||||
stmt.executeQuery("SELECT * FROM users");
|
||||
ResultSet resultSet = stmt.getResultSet();
|
||||
|
||||
assertTrue(resultSet.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
void invoking_next_after_the_last_row_should_return_false() throws Exception {
|
||||
stmt.executeUpdate("CREATE TABLE users (id INT PRIMARY KEY, username TEXT);");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (1, 'sinwoo');");
|
||||
stmt.executeUpdate("INSERT INTO users VALUES (2, 'seonwoo');");
|
||||
|
||||
// first call to next occur internally
|
||||
stmt.executeQuery("SELECT * FROM users");
|
||||
ResultSet resultSet = stmt.getResultSet();
|
||||
|
||||
while (resultSet.next()) {
|
||||
// run until next() returns false
|
||||
}
|
||||
|
||||
// if the previous call to next() returned false, consecutive call to next() should return false as well
|
||||
assertFalse(resultSet.next());
|
||||
}
|
||||
// if the previous call to next() returned false, consecutive call to next() should return false
|
||||
// as well
|
||||
assertFalse(resultSet.next());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ 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;
|
||||
|
@ -13,41 +12,43 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
class JDBC4StatementTest {
|
||||
|
||||
private Statement stmt;
|
||||
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);
|
||||
}
|
||||
@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_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
|
||||
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
|
||||
@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;"));
|
||||
}
|
||||
@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;"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue