mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-07-07 12:35:00 +00:00
Add support Java bindings
This add support for Java bindings in the bindings/java directory.
This commit is contained in:
parent
1c2e074c93
commit
370e1ca5c2
22 changed files with 1253 additions and 0 deletions
121
Cargo.lock
generated
121
Cargo.lock
generated
|
@ -205,6 +205,12 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
|
@ -220,6 +226,12 @@ dependencies = [
|
|||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -359,6 +371,16 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
|
@ -1020,6 +1042,39 @@ version = "1.0.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "java-limbo"
|
||||
version = "0.0.11"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"jni",
|
||||
"lazy_static",
|
||||
"limbo_core",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"cfg-if",
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"thiserror 1.0.69",
|
||||
"walkdir",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.76"
|
||||
|
@ -2417,6 +2472,15 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -2444,6 +2508,21 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
|
@ -2475,6 +2554,12 @@ dependencies = [
|
|||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
@ -2487,6 +2572,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
|
@ -2499,6 +2590,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
|
@ -2517,6 +2614,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
|
@ -2529,6 +2632,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
|
@ -2541,6 +2650,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
@ -2553,6 +2668,12 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"bindings/java",
|
||||
"bindings/python",
|
||||
"bindings/wasm",
|
||||
"cli",
|
||||
|
|
39
bindings/java/.gitignore
vendored
Normal file
39
bindings/java/.gitignore
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
19
bindings/java/Cargo.toml
Normal file
19
bindings/java/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "java-limbo"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "_limbo_java"
|
||||
crate-type = ["cdylib"]
|
||||
path = "rs_src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
limbo_core = { path = "../../core" }
|
||||
jni = "0.21.1"
|
||||
rand = { version = "0.8.5", features = [] }
|
||||
lazy_static = "1.5.0"
|
7
bindings/java/Makefile
Normal file
7
bindings/java/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
java_run: lib
|
||||
export LIMBO_SYSTEM_PATH=../../target/debug && ./gradlew run
|
||||
|
||||
.PHONY: lib
|
||||
|
||||
lib:
|
||||
cargo build
|
31
bindings/java/build.gradle.kts
Normal file
31
bindings/java/build.gradle.kts
Normal file
|
@ -0,0 +1,31 @@
|
|||
plugins {
|
||||
java
|
||||
application
|
||||
}
|
||||
|
||||
group = "org.github.tursodatabase"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass.set("org.github.tursodatabase.Main")
|
||||
|
||||
val limboSystemLibraryPath = System.getenv("LIMBO_SYSTEM_PATH")
|
||||
if (limboSystemLibraryPath != null) {
|
||||
applicationDefaultJvmArgs = listOf(
|
||||
"-Djava.library.path=${System.getProperty("java.library.path")}:$limboSystemLibraryPath"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
BIN
bindings/java/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
bindings/java/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
bindings/java/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
bindings/java/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
252
bindings/java/gradlew
vendored
Executable file
252
bindings/java/gradlew
vendored
Executable file
|
@ -0,0 +1,252 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
94
bindings/java/gradlew.bat
vendored
Normal file
94
bindings/java/gradlew.bat
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
84
bindings/java/rs_src/connection.rs
Normal file
84
bindings/java/rs_src/connection.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use crate::cursor::Cursor;
|
||||
use jni::objects::JClass;
|
||||
use jni::sys::jlong;
|
||||
use jni::JNIEnv;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Connection {
|
||||
pub(crate) conn: Arc<Mutex<Rc<limbo_core::Connection>>>,
|
||||
pub(crate) io: Arc<limbo_core::PlatformIO>,
|
||||
}
|
||||
|
||||
/// Returns a pointer to a `Cursor` object.
|
||||
///
|
||||
/// The Java application will pass this pointer to native functions,
|
||||
/// which will use it to reference the `Cursor` object.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `_env` - The JNI environment pointer.
|
||||
/// * `_class` - The Java class calling this function.
|
||||
/// * `connection_ptr` - A pointer to the `Connection` object.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `jlong` representing the pointer to the newly created `Cursor` object.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Connection_cursor<'local>(
|
||||
_env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
connection_ptr: jlong,
|
||||
) -> jlong {
|
||||
let connection = to_connection(connection_ptr);
|
||||
let cursor = Cursor {
|
||||
array_size: 1,
|
||||
conn: connection.clone(),
|
||||
description: None,
|
||||
rowcount: -1,
|
||||
smt: None,
|
||||
};
|
||||
Box::into_raw(Box::new(cursor)) as jlong
|
||||
}
|
||||
|
||||
/// Closes the connection and releases the associated resources.
|
||||
///
|
||||
/// This function is called from the Java side to close the connection
|
||||
/// and free the memory allocated for the `Connection` object.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `_env` - The JNI environment pointer.
|
||||
/// * `_class` - The Java class calling this function.
|
||||
/// * `connection_ptr` - A pointer to the `Connection` object to be closed.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn Java_org_github_tursodatabase_limbo_Connection_close<'local>(
|
||||
_env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
connection_ptr: jlong,
|
||||
) {
|
||||
let _boxed_connection = Box::from_raw(connection_ptr as *mut Connection);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Connection_commit<'local>(
|
||||
_env: &mut JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
_connection_id: jlong,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Connection_rollback<'local>(
|
||||
_env: &mut JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
_connection_id: jlong,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_connection(connection_ptr: jlong) -> &'static mut Connection {
|
||||
unsafe { &mut *(connection_ptr as *mut Connection) }
|
||||
}
|
240
bindings/java/rs_src/cursor.rs
Normal file
240
bindings/java/rs_src/cursor.rs
Normal file
|
@ -0,0 +1,240 @@
|
|||
use crate::connection::Connection;
|
||||
use crate::errors::ErrorCode;
|
||||
use crate::utils::row_to_obj_array;
|
||||
use crate::{eprint_return, eprint_return_null};
|
||||
use jni::errors::JniError;
|
||||
use jni::objects::{JClass, JObject, JString};
|
||||
use jni::sys::jlong;
|
||||
use jni::JNIEnv;
|
||||
use limbo_core::IO;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cursor {
|
||||
/// This read/write attribute specifies the number of rows to fetch at a time with `.fetchmany()`.
|
||||
/// It defaults to `1`, meaning it fetches a single row at a time.
|
||||
pub(crate) array_size: i64,
|
||||
|
||||
pub(crate) conn: Connection,
|
||||
|
||||
/// The `.description` attribute is a read-only sequence of 7-item, each describing a column in the result set:
|
||||
///
|
||||
/// - `name`: The column's name (always present).
|
||||
/// - `type_code`: The data type code (always present).
|
||||
/// - `display_size`: Column's display size (optional).
|
||||
/// - `internal_size`: Column's internal size (optional).
|
||||
/// - `precision`: Numeric precision (optional).
|
||||
/// - `scale`: Numeric scale (optional).
|
||||
/// - `null_ok`: Indicates if null values are allowed (optional).
|
||||
///
|
||||
/// The `name` and `type_code` fields are mandatory; others default to `None` if not applicable.
|
||||
///
|
||||
/// This attribute is `None` for operations that do not return rows or if no `.execute*()` method has been invoked.
|
||||
pub(crate) description: Option<Description>,
|
||||
|
||||
/// Read-only attribute that provides the number of modified rows for `INSERT`, `UPDATE`, `DELETE`,
|
||||
/// and `REPLACE` statements; it is `-1` for other statements, including CTE queries.
|
||||
/// It is only updated by the `execute()` and `executemany()` methods after the statement has run to completion.
|
||||
/// This means any resulting rows must be fetched for `rowcount` to be updated.
|
||||
pub(crate) rowcount: i64,
|
||||
|
||||
pub(crate) smt: Option<Arc<Mutex<limbo_core::Statement>>>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Description {
|
||||
_name: String,
|
||||
_type_code: String,
|
||||
_display_size: Option<String>,
|
||||
_internal_size: Option<String>,
|
||||
_precision: Option<String>,
|
||||
_scale: Option<String>,
|
||||
_null_ok: Option<String>,
|
||||
}
|
||||
|
||||
impl Debug for Cursor {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Cursor")
|
||||
.field("array_size", &self.array_size)
|
||||
.field("description", &self.description)
|
||||
.field("rowcount", &self.rowcount)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: we should find a way to handle Error thrown by rust and how to handle those errors in java
|
||||
#[no_mangle]
|
||||
#[allow(improper_ctypes_definitions, clippy::arc_with_non_send_sync)]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Cursor_execute<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
cursor_ptr: jlong,
|
||||
sql: JString<'local>,
|
||||
) -> Result<(), JniError> {
|
||||
let sql: String = env
|
||||
.get_string(&sql)
|
||||
.expect("Could not extract query")
|
||||
.into();
|
||||
|
||||
let stmt_is_dml = stmt_is_dml(&sql);
|
||||
if stmt_is_dml {
|
||||
return eprint_return!(
|
||||
"DML statements (INSERT/UPDATE/DELETE) are not fully supported in this version",
|
||||
JniError::Other(ErrorCode::STATEMENT_IS_DML)
|
||||
);
|
||||
}
|
||||
|
||||
let cursor = to_cursor(cursor_ptr);
|
||||
let conn_lock = match cursor.conn.conn.lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(_) => return eprint_return!("Failed to acquire connection lock", JniError::Other(-1)),
|
||||
};
|
||||
|
||||
match conn_lock.prepare(&sql) {
|
||||
Ok(statement) => {
|
||||
cursor.smt = Some(Arc::new(Mutex::new(statement)));
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
eprint_return!(
|
||||
&format!("Failed to prepare statement: {:?}", e),
|
||||
JniError::Other(-1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Cursor_fetchOne<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
cursor_ptr: jlong,
|
||||
) -> JObject<'local> {
|
||||
let cursor = to_cursor(cursor_ptr);
|
||||
|
||||
if let Some(smt) = &cursor.smt {
|
||||
loop {
|
||||
let mut smt_lock = match smt.lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(_) => {
|
||||
return eprint_return_null!(
|
||||
"Failed to acquire statement lock",
|
||||
JniError::Other(-1)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
match smt_lock.step() {
|
||||
Ok(limbo_core::StepResult::Row(row)) => {
|
||||
return match row_to_obj_array(&mut env, &row) {
|
||||
Ok(r) => r,
|
||||
Err(e) => eprint_return_null!(&format!("{:?}", e), JniError::Other(-1)),
|
||||
}
|
||||
}
|
||||
Ok(limbo_core::StepResult::IO) => {
|
||||
if let Err(e) = cursor.conn.io.run_once() {
|
||||
return eprint_return_null!(
|
||||
&format!("IO Error: {:?}", e),
|
||||
JniError::Other(-1)
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(limbo_core::StepResult::Interrupt) => return JObject::null(),
|
||||
Ok(limbo_core::StepResult::Done) => return JObject::null(),
|
||||
Ok(limbo_core::StepResult::Busy) => {
|
||||
return eprint_return_null!("Busy error", JniError::Other(-1));
|
||||
}
|
||||
Err(e) => {
|
||||
return eprint_return_null!(
|
||||
format!("Step error: {:?}", e),
|
||||
JniError::Other(-1)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
eprint_return_null!("No statement prepared for execution", JniError::Other(-1))
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Cursor_fetchAll<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
cursor_ptr: jlong,
|
||||
) -> JObject<'local> {
|
||||
let cursor = to_cursor(cursor_ptr);
|
||||
|
||||
if let Some(smt) = &cursor.smt {
|
||||
let mut rows = Vec::new();
|
||||
loop {
|
||||
let mut smt_lock = match smt.lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(_) => {
|
||||
return eprint_return_null!(
|
||||
"Failed to acquire statement lock",
|
||||
JniError::Other(-1)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
match smt_lock.step() {
|
||||
Ok(limbo_core::StepResult::Row(row)) => match row_to_obj_array(&mut env, &row) {
|
||||
Ok(r) => rows.push(r),
|
||||
Err(e) => return eprint_return_null!(&format!("{:?}", e), JniError::Other(-1)),
|
||||
},
|
||||
Ok(limbo_core::StepResult::IO) => {
|
||||
if let Err(e) = cursor.conn.io.run_once() {
|
||||
return eprint_return_null!(
|
||||
&format!("IO Error: {:?}", e),
|
||||
JniError::Other(-1)
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(limbo_core::StepResult::Interrupt) => {
|
||||
return JObject::null();
|
||||
}
|
||||
Ok(limbo_core::StepResult::Done) => {
|
||||
break;
|
||||
}
|
||||
Ok(limbo_core::StepResult::Busy) => {
|
||||
return eprint_return_null!("Busy error", JniError::Other(-1));
|
||||
}
|
||||
Err(e) => {
|
||||
return eprint_return_null!(
|
||||
format!("Step error: {:?}", e),
|
||||
JniError::Other(-1)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let array_class = env
|
||||
.find_class("[Ljava/lang/Object;")
|
||||
.expect("Failed to find Object array class");
|
||||
let result_array = env
|
||||
.new_object_array(rows.len() as i32, array_class, JObject::null())
|
||||
.expect("Failed to create new object array");
|
||||
|
||||
for (i, row) in rows.into_iter().enumerate() {
|
||||
env.set_object_array_element(&result_array, i as i32, row)
|
||||
.expect("Failed to set object array element");
|
||||
}
|
||||
|
||||
result_array.into()
|
||||
} else {
|
||||
eprint_return_null!("No statement prepared for execution", JniError::Other(-1))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_cursor(cursor_ptr: jlong) -> &'static mut Cursor {
|
||||
unsafe { &mut *(cursor_ptr as *mut Cursor) }
|
||||
}
|
||||
|
||||
fn stmt_is_dml(sql: &str) -> bool {
|
||||
let sql = sql.trim();
|
||||
let sql = sql.to_uppercase();
|
||||
sql.starts_with("INSERT") || sql.starts_with("UPDATE") || sql.starts_with("DELETE")
|
||||
}
|
35
bindings/java/rs_src/errors.rs
Normal file
35
bindings/java/rs_src/errors.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use jni::errors::{Error, JniError};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CustomError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
/// This struct defines error codes that correspond to the constants defined in the
|
||||
/// Java package `org.github.tursodatabase.exceptions.ErrorCode`.
|
||||
///
|
||||
/// These error codes are used to handle and represent specific error conditions
|
||||
/// that may occur within the Rust code and need to be communicated to the Java side.
|
||||
#[derive(Clone)]
|
||||
pub struct ErrorCode;
|
||||
|
||||
impl ErrorCode {
|
||||
pub const CONNECTION_FAILURE: i32 = -1;
|
||||
|
||||
pub const STATEMENT_IS_DML: i32 = -1;
|
||||
}
|
||||
|
||||
impl From<jni::errors::Error> for CustomError {
|
||||
fn from(value: Error) -> Self {
|
||||
CustomError {
|
||||
message: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CustomError> for JniError {
|
||||
fn from(value: CustomError) -> Self {
|
||||
eprintln!("Error occurred: {:?}", value.message);
|
||||
JniError::Other(-1)
|
||||
}
|
||||
}
|
66
bindings/java/rs_src/lib.rs
Normal file
66
bindings/java/rs_src/lib.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
mod connection;
|
||||
mod cursor;
|
||||
mod errors;
|
||||
mod macros;
|
||||
mod utils;
|
||||
|
||||
use crate::connection::Connection;
|
||||
use crate::errors::ErrorCode;
|
||||
use jni::errors::JniError;
|
||||
use jni::objects::{JClass, JString};
|
||||
use jni::sys::jlong;
|
||||
use jni::JNIEnv;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Establishes a connection to the database specified by the given path.
|
||||
///
|
||||
/// This function is called from the Java side to create a connection to the database.
|
||||
/// It returns a pointer to the `Connection` object, which can be used in subsequent
|
||||
/// native function calls.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `env` - The JNI environment pointer.
|
||||
/// * `_class` - The Java class calling this function.
|
||||
/// * `path` - A `JString` representing the path to the database file.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `jlong` representing the pointer to the newly created `Connection` object,
|
||||
/// or [ErrorCode::CONNECTION_FAILURE] if the connection could not be established.
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_org_github_tursodatabase_limbo_Limbo_connect<'local>(
|
||||
mut env: JNIEnv<'local>,
|
||||
_class: JClass<'local>,
|
||||
path: JString<'local>,
|
||||
) -> jlong {
|
||||
connect_internal(&mut env, path).unwrap_or_else(|_| ErrorCode::CONNECTION_FAILURE as jlong)
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes_definitions, clippy::arc_with_non_send_sync)] // TODO: remove
|
||||
fn connect_internal<'local>(
|
||||
env: &mut JNIEnv<'local>,
|
||||
path: JString<'local>,
|
||||
) -> Result<jlong, JniError> {
|
||||
let io = Arc::new(limbo_core::PlatformIO::new().map_err(|e| {
|
||||
println!("IO initialization failed: {:?}", e);
|
||||
JniError::Unknown
|
||||
})?);
|
||||
|
||||
let path: String = env
|
||||
.get_string(&path)
|
||||
.expect("Failed to convert JString to Rust String")
|
||||
.into();
|
||||
let db = limbo_core::Database::open_file(io.clone(), &path).map_err(|e| {
|
||||
println!("Failed to open database: {:?}", e);
|
||||
JniError::Unknown
|
||||
})?;
|
||||
|
||||
let conn = db.connect().clone();
|
||||
let connection = Connection {
|
||||
conn: Arc::new(Mutex::new(conn)),
|
||||
io,
|
||||
};
|
||||
|
||||
Ok(Box::into_raw(Box::new(connection)) as jlong)
|
||||
}
|
16
bindings/java/rs_src/macros.rs
Normal file
16
bindings/java/rs_src/macros.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// bindings/java/src/macros.rs
|
||||
#[macro_export]
|
||||
macro_rules! eprint_return {
|
||||
($log:expr, $error:expr) => {{
|
||||
eprintln!("{}", $log);
|
||||
Err($error)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! eprint_return_null {
|
||||
($log:expr, $error:expr) => {{
|
||||
eprintln!("{}", $log);
|
||||
JObject::null()
|
||||
}};
|
||||
}
|
30
bindings/java/rs_src/utils.rs
Normal file
30
bindings/java/rs_src/utils.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use crate::errors::CustomError;
|
||||
use jni::objects::{JObject, JValue};
|
||||
use jni::JNIEnv;
|
||||
|
||||
pub(crate) fn row_to_obj_array<'local>(
|
||||
env: &mut JNIEnv<'local>,
|
||||
row: &limbo_core::Row,
|
||||
) -> Result<JObject<'local>, CustomError> {
|
||||
let obj_array =
|
||||
env.new_object_array(row.values.len() as i32, "java/lang/Object", JObject::null())?;
|
||||
|
||||
for (i, value) in row.values.iter().enumerate() {
|
||||
let obj = match value {
|
||||
limbo_core::Value::Null => JObject::null(),
|
||||
limbo_core::Value::Integer(i) => {
|
||||
env.new_object("java/lang/Long", "(J)V", &[JValue::Long(*i)])?
|
||||
}
|
||||
limbo_core::Value::Float(f) => {
|
||||
env.new_object("java/lang/Double", "(D)V", &[JValue::Double(*f)])?
|
||||
}
|
||||
limbo_core::Value::Text(s) => env.new_string(s)?.into(),
|
||||
limbo_core::Value::Blob(b) => env.byte_array_from_slice(b)?.into(),
|
||||
};
|
||||
if let Err(e) = env.set_object_array_element(&obj_array, i as i32, obj) {
|
||||
eprintln!("Error on parsing row: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(obj_array.into())
|
||||
}
|
1
bindings/java/settings.gradle.kts
Normal file
1
bindings/java/settings.gradle.kts
Normal file
|
@ -0,0 +1 @@
|
|||
rootProject.name = "limbo"
|
|
@ -0,0 +1,16 @@
|
|||
package org.github.tursodatabase;
|
||||
|
||||
import org.github.tursodatabase.limbo.Connection;
|
||||
import org.github.tursodatabase.limbo.Cursor;
|
||||
import org.github.tursodatabase.limbo.Limbo;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Limbo limbo = Limbo.create();
|
||||
Connection connection = limbo.getConnection("database.db");
|
||||
|
||||
Cursor cursor = connection.cursor();
|
||||
cursor.execute("SELECT * FROM example_table;");
|
||||
System.out.println("result: " + cursor.fetchOne());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.github.tursodatabase.exceptions;
|
||||
|
||||
|
||||
/**
|
||||
* This class defines error codes that correspond to specific error conditions
|
||||
* that may occur while communicating with the JNI.
|
||||
* <p />
|
||||
* Refer to ErrorCode in rust package.
|
||||
*/
|
||||
public class ErrorCode {
|
||||
public static int CONNECTION_FAILURE = -1;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.github.tursodatabase.limbo;
|
||||
|
||||
import java.lang.Exception;
|
||||
|
||||
/**
|
||||
* Represents a connection to the database.
|
||||
*/
|
||||
public class Connection {
|
||||
|
||||
// Pointer to the connection object
|
||||
private final long connectionPtr;
|
||||
|
||||
public Connection(long connectionPtr) {
|
||||
this.connectionPtr = connectionPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cursor object using this connection.
|
||||
*
|
||||
* @return A new Cursor object.
|
||||
* @throws Exception If the cursor cannot be created.
|
||||
*/
|
||||
public Cursor cursor() throws Exception {
|
||||
long cursorId = cursor(connectionPtr);
|
||||
return new Cursor(cursorId);
|
||||
}
|
||||
|
||||
private native long cursor(long connectionPtr);
|
||||
|
||||
/**
|
||||
* Closes the connection to the database.
|
||||
*
|
||||
* @throws Exception If there is an error closing the connection.
|
||||
*/
|
||||
public void close() throws Exception {
|
||||
close(connectionPtr);
|
||||
}
|
||||
|
||||
private native void close(long connectionPtr);
|
||||
|
||||
/**
|
||||
* Commits the current transaction.
|
||||
*
|
||||
* @throws Exception If there is an error during commit.
|
||||
*/
|
||||
public void commit() throws Exception {
|
||||
try {
|
||||
commit(connectionPtr);
|
||||
} catch (Exception e) {
|
||||
System.out.println("caught exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private native void commit(long connectionPtr) throws Exception;
|
||||
|
||||
/**
|
||||
* Rolls back the current transaction.
|
||||
*
|
||||
* @throws Exception If there is an error during rollback.
|
||||
*/
|
||||
public void rollback() throws Exception {
|
||||
rollback(connectionPtr);
|
||||
}
|
||||
|
||||
private native void rollback(long connectionPtr) throws Exception;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.github.tursodatabase.limbo;
|
||||
|
||||
/**
|
||||
* Represents a database cursor.
|
||||
*/
|
||||
public class Cursor {
|
||||
private long cursorPtr;
|
||||
|
||||
public Cursor(long cursorPtr) {
|
||||
this.cursorPtr = cursorPtr;
|
||||
}
|
||||
|
||||
// TODO: support parameters
|
||||
public Cursor execute(String sql) {
|
||||
var result = execute(cursorPtr, sql);
|
||||
System.out.println("resut: " + result);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static native int execute(long cursorPtr, String sql);
|
||||
|
||||
public Object fetchOne() throws Exception {
|
||||
Object result = fetchOne(cursorPtr);
|
||||
return processSingleResult(result);
|
||||
}
|
||||
|
||||
private static native Object fetchOne(long cursorPtr);
|
||||
|
||||
public Object fetchAll() throws Exception {
|
||||
Object result = fetchAll(cursorPtr);
|
||||
return processArrayResult(result);
|
||||
}
|
||||
|
||||
private static native Object fetchAll(long cursorPtr);
|
||||
|
||||
private Object processSingleResult(Object result) throws Exception {
|
||||
if (result instanceof Object[]) {
|
||||
System.out.println("The result is of type: Object[]");
|
||||
for (Object element : (Object[]) result) {
|
||||
printElementType(element);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
printElementType(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private Object processArrayResult(Object result) throws Exception {
|
||||
if (result instanceof Object[][]) {
|
||||
System.out.println("The result is of type: Object[][]");
|
||||
Object[][] array = (Object[][]) result;
|
||||
for (Object[] row : array) {
|
||||
for (Object element : row) {
|
||||
printElementType(element);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
} else {
|
||||
throw new Exception("result should be of type Object[][]. Maybe internal logic has error.");
|
||||
}
|
||||
}
|
||||
|
||||
private void printElementType(Object element) {
|
||||
if (element instanceof String) {
|
||||
System.out.println("String: " + element);
|
||||
} else if (element instanceof Integer) {
|
||||
System.out.println("Integer: " + element);
|
||||
} else if (element instanceof Double) {
|
||||
System.out.println("Double: " + element);
|
||||
} else if (element instanceof Boolean) {
|
||||
System.out.println("Boolean: " + element);
|
||||
} else if (element instanceof Long) {
|
||||
System.out.println("Long: " + element);
|
||||
} else if (element instanceof byte[]) {
|
||||
System.out.print("byte[]: ");
|
||||
for (byte b : (byte[]) element) {
|
||||
System.out.print(b + " ");
|
||||
}
|
||||
System.out.println();
|
||||
} else {
|
||||
System.out.println("Unknown type: " + element);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package org.github.tursodatabase.limbo;
|
||||
|
||||
import org.github.tursodatabase.exceptions.ErrorCode;
|
||||
|
||||
import java.lang.Exception;
|
||||
|
||||
public class Limbo {
|
||||
|
||||
private static volatile boolean initialized;
|
||||
|
||||
private Limbo() {
|
||||
if (!initialized) {
|
||||
System.loadLibrary("_limbo_java");
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Limbo create() {
|
||||
return new Limbo();
|
||||
}
|
||||
|
||||
public Connection getConnection(String path) throws Exception {
|
||||
long connectionId = connect(path);
|
||||
if (connectionId == ErrorCode.CONNECTION_FAILURE) {
|
||||
throw new Exception("Failed to initialize connection");
|
||||
}
|
||||
return new Connection(connectionId);
|
||||
}
|
||||
|
||||
private static native long connect(String path);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue