diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000000..cecd9fc7e9 --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,71 @@ +name: build +on: + workflow_dispatch: + push: + tags: + - '*.*.*' + +jobs: + build_natives: + permissions: + contents: write + name: Build natives libraries + runs-on: ubuntu-20.04 + steps: + - name: Make space + run: sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc + + - uses: actions/checkout@v2 + - uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: 11 + java-package: jdk + cache: 'maven' + - name: Source versions from VERSION file + run: cat VERSION >> $GITHUB_ENV + + - name: Display version + run: echo $sqliteMCVersion $version $artifactVersion + + - name: Build binaries, test, and package + run: make all + - name: Package DLL binaries + run: zip -r ./sqlite-natives-$artifactVersion.zip ./src/main/resources/org/sqlite/native + - uses: actions/upload-artifact@v2 + with: + name: SQLiteMC DLL Binaries + path: | + ./sqlite-natives-${{ env.artifactVersion }}.zip + ./target/*.jar + + - name: Prepare GPG sign + if: startsWith(github.ref, 'refs/tags/') + run: | + openssl aes-256-cbc -K ${{ secrets.ENCRYPTED_KEY }} -iv ${{ secrets.ENCRYPTED_IV }} -in ./scripts/private.key.enc -out /tmp/private.key -d + gpg --version + gpg --batch --import /tmp/private.key + rm /tmp/private.key + - name: Deploy version + env: + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + if: startsWith(github.ref, 'refs/tags/') + run: make deploy + - name: Deploy Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + name: "SQLite-jdbc-${{ env.artifactVersion }}" + generate_release_notes: true + files: | + ./sqlite-natives-${{ env.artifactVersion }}.zip + ./target/sqlite-jdbc-${{ env.artifactVersion }}.jar" + ./target/sqlite-jdbc-${{ env.artifactVersion }}-javadoc.jar" + ./target/sqlite-jdbc-${{ env.artifactVersion }}-sources.jar" + ./target/sqlite-jdbc-${{ env.artifactVersion }}.jar.asc" + ./target/sqlite-jdbc-${{ env.artifactVersion }}-javadoc.jar.asc" + ./target/sqlite-jdbc-${{ env.artifactVersion }}-sources.jar.asc" + ./sqlite-natives-${{env.artifactVersion }}.zip" + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3510b94e81..71b8a4b41a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,8 +39,8 @@ jobs: name: test ${{ matrix.os }} jdk${{ matrix.java }} strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - java: [8, 11, 16] + os: [ ubuntu-latest, windows-latest, macos-latest ] + java: [ 8, 11, 16 ] exclude: - os: windows-latest java: 11 @@ -55,10 +55,25 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v2 with: - distribution: 'adopt' + distribution: 'temurin' java-version: ${{ matrix.java }} java-package: jdk cache: 'maven' + + - name: Install tools for windows + if: matrix.os == 'windows-latest' + run: | + choco install wget --no-progress + + - name: Source versions from VERSION file + run: cat VERSION >> $GITHUB_ENV + + - name: Display version + run: echo $sqliteMCVersion $version $artifactVersion + + - name: Build natives + run: make jni-header native + - name: Test run: mvn test @@ -78,5 +93,59 @@ jobs: - name: Install native-image component run: | gu install native-image + + - name: Source versions from VERSION file + run: cat VERSION >> $GITHUB_ENV + + - name: Display version + run: echo $sqliteMCVersion $version $artifactVersion + + - name: Build natives + run: make jni-header native + - name: Test run: mvn -Pnative test + + test_multiarch: + name: test ${{ matrix.arch }} ${{ matrix.distro }} jdk${{ matrix.java }} + strategy: + matrix: + # armv6 cannot install the JDK properly via apt-get on Debian + arch: [ armv7, aarch64 ] + # no point adding Alpine, as we only have x86_64 binaries for it + distro: [ ubuntu20.04 ] + # cannot add Java 16, maven installed by apt-get is 3.6.x and does not support Java 16 + # use only java 11, so we don't run too many CI jobs + java: [ 11 ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: uraimo/run-on-arch-action@v2.1.1 + name: Test + with: + arch: ${{ matrix.arch }} + distro: ${{ matrix.distro }} + + # Not required, but speeds up builds + githubToken: ${{ github.token }} + + # Install some dependencies in the container. This speeds up builds if + # you are also using githubToken. Any dependencies installed here will + # be part of the container image that gets cached, so subsequent + # builds don't have to re-install them. The image layer is cached + # publicly in your project's package repository, so it is vital that + # no secrets are present in the container state or logs. + install: | + apt-get update -y + apt-get install -y openssl ca-certificates maven openjdk-${{ matrix.java }}-jdk build-essential curl bash jq wget unzip curl gnupg + + # We use the Github Action user, 1001:121, so that maven can access + # the /work directory and create target/classes + dockerRunArgs: | + --volume "${PWD}:/work" + --user 1001:121 + + run: | + echo "Architecture: `uname -a`" + mvn --version + cd /work && make jni-header native && mvn test diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index b87b1cad5e..25828d6273 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,13 +1,13 @@ #Thu Apr 28 10:35:24 JST 2011 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Makefile b/Makefile index 01446ed0c5..1f95773d65 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,9 @@ RESOURCE_DIR = src/main/resources .phony: all package native native-all deploy -all: setversion jni-header package +all: jni-header package -deploy: setversion +deploy: mvn package deploy -DperformRelease=true --settings settings.xml MVN:=mvn @@ -28,7 +28,9 @@ CCFLAGS:= -I$(SQLITE_OUT) -I$(SQLITE_INCLUDE) $(CCFLAGS) $(SQLITE_ARCHIVE): echo "Downloading Archive" - curl -s https://api.github.com/repos/utelle/SQLite3MultipleCiphers/releases | jq -r ".[].assets[] | select(.name | contains(\"$(version)-amalgamation\")) | .created_at |= fromdateiso8601 | .browser_download_url" | head -1 | wget -O $@ -i - + #curl -s https://api.github.com/repos/utelle/SQLite3MultipleCiphers/releases | jq -r ".[].assets[] | select(.name | contains(\"$(version)-amalgamation\")) | .created_at |= fromdateiso8601 | .browser_download_url" | head -1 | wget -O $@ -i - + #wget -O $@ https://github.com/utelle/SQLite3MultipleCiphers/releases/download/v$(sqliteMCVersion)/sqlite3mc-$(sqliteMCVersion)-sqlite-$(version)-amalgamation.zip + curl -SL "https://github.com/utelle/SQLite3MultipleCiphers/releases/download/v$(sqliteMCVersion)/sqlite3mc-$(sqliteMCVersion)-sqlite-$(version)-amalgamation.zip" > $@ #if [ ! -d "$(TARGET)/$(version)" ] ; then git clone https://github.com/utelle/SQLite3MultipleCiphers.git $(TARGET)/$(version); cd $(TARGET)/$(version); fi @mkdir -p $(@D) @@ -49,11 +51,8 @@ $(TARGET)/common-lib/NativeDB.h: src/main/java/org/sqlite/core/NativeDB.java $(JAVAC) -d $(TARGET)/common-lib -sourcepath $(SRC) -h $(TARGET)/common-lib src/main/java/org/sqlite/core/NativeDB.java mv target/common-lib/org_sqlite_core_NativeDB.h target/common-lib/NativeDB.h -setversion: - $(MVN) versions:set -DnewVersion=$(artifactVersion) - -test: setversion - $(MVN) test +test: + mvn test clean: clean-target clean-native clean-java clean-tests @@ -125,18 +124,17 @@ $(SQLITE_OUT)/$(LIBNAME): $(SQLITE_HEADER) $(SQLITE_OBJ) $(SRC)/org/sqlite/core/ $(CC) $(CCFLAGS) -I $(TARGET)/common-lib -c -o $(SQLITE_OUT)/NativeDB.o $(SRC)/org/sqlite/core/NativeDB.c $(CC) $(CCFLAGS) -o $@ $(SQLITE_OUT)/NativeDB.o $(SQLITE_OBJ) $(LINKFLAGS) # Workaround for strip Protocol error when using VirtualBox on Mac - cp $@ /tmp/$(@F) - $(STRIP) /tmp/$(@F) - cp /tmp/$(@F) $@ + #cp $@ $(_TMP)/$(@F) + $(STRIP) $@ + #cp $(_TMP)/$(@F) $@ NATIVE_DIR=src/main/resources/org/sqlite/native/$(OS_NAME)/$(OS_ARCH) NATIVE_TARGET_DIR:=$(TARGET)/classes/org/sqlite/native/$(OS_NAME)/$(OS_ARCH) NATIVE_DLL:=$(NATIVE_DIR)/$(LIBNAME) # For cross-compilation, install docker. See also https://github.com/dockcross/dockcross -# Freebsd disabled for now... -#native-all: native win32 win64 win-armv7 win-arm64 mac64 linux32 linux64 freebsd32 freebsd64 freebsd-arm64 linux-arm linux-armv6 linux-armv7 linux-arm64 linux-android-arm linux-ppc64 alpine-linux64 -native-all: native win32 win64 win-armv7 win-arm64 mac64 linux32 linux64 linux-arm linux-armv6 linux-armv7 linux-arm64 linux-android-arm linux-ppc64 alpine-linux64 +#native-all: native win32 win64 win-armv7 win-arm64 mac64 linux32 linux64 freebsd32 freebsd64 freebsd-arm64 linux-arm linux-armv6 linux-armv7 linux-arm64 linux-android-arm linux-android-arm64 linux-android-x86 linux-android-x64 linux-ppc64 alpine-linux64 +native-all: native win32 win64 win-armv7 win-arm64 mac64 linux32 linux64 linux-arm linux-armv6 linux-armv7 linux-arm64 linux-android-arm linux-android-arm64 linux-android-x86 linux-android-x64 linux-ppc64 alpine-linux64 native: $(NATIVE_DLL) @@ -176,7 +174,7 @@ freebsd-arm64: $(SQLITE_UNPACKED) jni-header docker run $(DOCKER_RUN_OPTS) -v $$PWD:/workdir gotson/freebsd-cross-build:aarch64-11.4 sh -c 'make clean-native native OS_NAME=FreeBSD OS_ARCH=aarch64 CROSS_PREFIX=aarch64-unknown-freebsd11-' alpine-linux64: $(SQLITE_UNPACKED) jni-header - docker run $(DOCKER_RUN_OPTS) -v $$PWD:/work xerial/alpine-linux-x86_64 bash -c 'make clean-native native OS_NAME=Linux-Alpine OS_ARCH=x86_64' + docker run $(DOCKER_RUN_OPTS) -v $$PWD:/work xerial/alpine-linux-x86_64 bash -c 'make clean-native native OS_NAME=Linux-Musl OS_ARCH=x86_64' linux-arm: $(SQLITE_UNPACKED) jni-header ./docker/dockcross-armv5 -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=armv5-unknown-linux-gnueabi- OS_NAME=Linux OS_ARCH=arm' @@ -191,7 +189,16 @@ linux-arm64: $(SQLITE_UNPACKED) jni-header ./docker/dockcross-arm64 -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=aarch64-unknown-linux-gnu- OS_NAME=Linux OS_ARCH=aarch64' linux-android-arm: $(SQLITE_UNPACKED) jni-header - ./docker/dockcross-android-arm -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=/usr/arm-linux-androideabi/bin/arm-linux-androideabi- OS_NAME=Linux OS_ARCH=android-arm' + ./docker/dockcross-android-arm -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=/usr/arm-linux-androideabi/bin/arm-linux-androideabi- OS_NAME=Linux-Android OS_ARCH=arm' + +linux-android-arm64: $(SQLITE_UNPACKED) jni-header + ./docker/dockcross-android-arm64 -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=/usr/aarch64-linux-android/bin/aarch64-linux-android- OS_NAME=Linux-Android OS_ARCH=aarch64' + +linux-android-x86: $(SQLITE_UNPACKED) jni-header + ./docker/dockcross-android-x86 -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=/usr/i686-linux-android/bin/i686-linux-android- OS_NAME=Linux-Android OS_ARCH=x86' + +linux-android-x64: $(SQLITE_UNPACKED) jni-header + ./docker/dockcross-android-x86_64 -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=/usr/x86_64-linux-android/bin/x86_64-linux-android- OS_NAME=Linux-Android OS_ARCH=x86_64' linux-ppc64: $(SQLITE_UNPACKED) jni-header ./docker/dockcross-ppc64 -a $(DOCKER_RUN_OPTS) bash -c 'make clean-native native CROSS_PREFIX=powerpc64le-unknown-linux-gnu- OS_NAME=Linux OS_ARCH=ppc64' diff --git a/Makefile.common b/Makefile.common index 843f84bab9..e0fd90653f 100644 --- a/Makefile.common +++ b/Makefile.common @@ -9,13 +9,14 @@ JAVAC := "$$JAVA_HOME/bin/javac" JAVAH := "$$JAVA_HOME/bin/javah" TARGET := target -OSINFO_CLASS := org.sqlite.OSInfo +OSINFO_CLASS := org.sqlite.util.OSInfo OSINFO_PROG := lib/org/sqlite/util/OSInfo.class -## building OSInfo.java -#$(info compiling OSInfo.java) -#$(shell mkdir -p lib) -#$(shell $(JAVAC) src/main/java/org/sqlite/util/OSInfo.java -d lib) +## building OSInfo.java +ifeq ("$(wildcard $(OSINFO_PROG))","") +$(info Building OSInfo tool) +$(shell $(JAVAC) -cp src/main/java -d lib src/main/java/org/sqlite/util/OSInfo.java) +endif ifndef OS_NAME OS_NAME := $(shell $(JAVA) -cp lib $(OSINFO_CLASS) --os) @@ -47,13 +48,15 @@ endif # os=Default is meant to be generic unix/linux -known_targets := Linux-x86 Linux-x86_64 Linux-Alpine-x86_64 Linux-arm Linux-armv6 Linux-armv7 Linux-aarch64 Linux-android-arm Linux-ppc64 Mac-x86 Mac-x86_64 Mac-aarch64 DragonFly-x86_64 FreeBSD-x86 FreeBSD-x86_64 FreeBSD-aarch64 OpenBSD-x86_64 Windows-x86 Windows-x86_64 Windows-armv7 Windows-aarch64 SunOS-sparcv9 HPUX-ia64_32 +known_targets := Linux-x86 Linux-x86_64 Linux-Musl-x86_64 Linux-arm Linux-armv6 Linux-armv7 Linux-aarch64 Linux-Android-arm Linux-Android-aarch64 Linux-Android-x86 Linux-Android-x86_64 Linux-ppc64 Mac-x86 Mac-x86_64 Mac-aarch64 DragonFly-x86_64 FreeBSD-x86 FreeBSD-x86_64 FreeBSD-aarch64 OpenBSD-x86_64 Windows-x86 Windows-x86_64 Windows-armv7 Windows-aarch64 SunOS-sparcv9 HPUX-ia64_32 target := $(OS_NAME)-$(OS_ARCH) ifeq (,$(findstring $(strip $(target)),$(known_targets))) target := Default endif +$(info Will build using target: $(target) (detected os: $(OS_NAME) arch: $(OS_ARCH))) + # cross-compilation toolchain prefix (e.g. "arm-linux-gnueabi-") CROSS_PREFIX := @@ -62,7 +65,7 @@ Default_STRIP := $(CROSS_PREFIX)strip Default_CCFLAGS := -I$(JAVA_HOME)/include -Ilib/inc_linux -Os -fPIC -fvisibility=hidden -std=gnu11 -march=native Default_LINKFLAGS := -shared Default_LIBNAME := libsqlitejdbc.so -Default_SQLITE_FLAGS := +Default_SQLITE_FLAGS := Linux-x86_CC := $(CROSS_PREFIX)gcc Linux-x86_STRIP := $(CROSS_PREFIX)strip @@ -78,12 +81,12 @@ Linux-x86_64_LINKFLAGS := -shared -static-libgcc Linux-x86_64_LIBNAME := libsqlitejdbc.so Linux-x86_64_SQLITE_FLAGS := -Linux-Alpine-x86_64_CC := $(CROSS_PREFIX)gcc -Linux-Alpine-x86_64_STRIP := $(CROSS_PREFIX)strip -Linux-Alpine-x86_64_CCFLAGS := -Ilib/inc_linux -I$(JAVA_HOME)/include -Os -fPIC -m64 -fvisibility=hidden -msse4.2 -maes -Linux-Alpine-x86_64_LINKFLAGS := -shared -static-libgcc -Linux-Alpine-x86_64_LIBNAME := libsqlitejdbc.so -Linux-Alpine-x86_64_SQLITE_FLAGS := +Linux-Musl-x86_64_CC := $(CROSS_PREFIX)gcc +Linux-Musl-x86_64_STRIP := $(CROSS_PREFIX)strip +Linux-Musl-x86_64_CCFLAGS := -Ilib/inc_linux -I$(JAVA_HOME)/include -Os -fPIC -m64 -fvisibility=hidden -msse4.2 -maes +Linux-Musl-x86_64_LINKFLAGS := -shared -static-libgcc +Linux-Musl-x86_64_LIBNAME := libsqlitejdbc.so +Linux-Musl-x86_64_SQLITE_FLAGS := Linux-arm_CC := $(CROSS_PREFIX)gcc Linux-arm_STRIP := $(CROSS_PREFIX)strip @@ -113,12 +116,33 @@ Linux-aarch64_LINKFLAGS := -shared -static-libgcc Linux-aarch64_LIBNAME := libsqlitejdbc.so Linux-aarch64_SQLITE_FLAGS := -Linux-android-arm_CC := $(CROSS_PREFIX)gcc -Linux-android-arm_STRIP := /usr/arm-linux-androideabi/bin/llvm-strip -Linux-android-arm_CCFLAGS := -I$(JAVA_HOME)/include -Ilib/inc_linux -I/usr/arm-linux-androideabi/sysroot/usr/include -Os -fPIC -fvisibility=hidden -fPIE -pie -lm -lc -landroid -ldl -llog -Linux-android-arm_LINKFLAGS := -shared -static-libgcc -Linux-android-arm_LIBNAME := libsqlitejdbc.so -Linux-android-arm_SQLITE_FLAGS := +Linux-Android-arm_CC := $(CROSS_PREFIX)clang +Linux-Android-arm_STRIP := $(CROSS_ROOT)/bin/llvm-strip +Linux-Android-arm_CCFLAGS := -I$(JAVA_HOME)/include -Ilib/inc_linux -I$(CROSS_ROOT)/sysroot/usr/include -Os -fPIC -fvisibility=hidden -fPIE -pie -lm -lc -landroid -ldl -llog +Linux-Android-arm_LINKFLAGS := -shared -static-libgcc +Linux-Android-arm_LIBNAME := libsqlitejdbc.so +Linux-Android-arm_SQLITE_FLAGS := + +Linux-Android-aarch64_CC := $(CROSS_PREFIX)clang +Linux-Android-aarch64_STRIP := $(CROSS_ROOT)/bin/llvm-strip +Linux-Android-aarch64_CCFLAGS := -I$(JAVA_HOME)/include -Ilib/inc_linux -I$(CROSS_ROOT)/sysroot/usr/include -Os -fPIC -fvisibility=hidden -fPIE -pie -lm -lc -landroid -ldl -llog +Linux-Android-aarch64_LINKFLAGS := -shared -static-libgcc +Linux-Android-aarch64_LIBNAME := libsqlitejdbc.so +Linux-Android-aarch64_SQLITE_FLAGS := + +Linux-Android-x86_CC := $(CROSS_PREFIX)clang +Linux-Android-x86_STRIP := $(CROSS_ROOT)/bin/llvm-strip +Linux-Android-x86_CCFLAGS := -I$(JAVA_HOME)/include -Ilib/inc_linux -I$(CROSS_ROOT)/sysroot/usr/include -Os -fPIC -fvisibility=hidden -fPIE -pie -lm -lc -landroid -ldl -llog -msse4.2 -maes +Linux-Android-x86_LINKFLAGS := -shared -static-libgcc +Linux-Android-x86_LIBNAME := libsqlitejdbc.so +Linux-Android-x86_SQLITE_FLAGS := + +Linux-Android-x86_64_CC := $(CROSS_PREFIX)clang +Linux-Android-x86_64_STRIP := $(CROSS_ROOT)/bin/llvm-strip +Linux-Android-x86_64_CCFLAGS := -I$(JAVA_HOME)/include -Ilib/inc_linux -I$(CROSS_ROOT)/sysroot/usr/include -Os -fPIC -fvisibility=hidden -fPIE -pie -lm -lc -landroid -ldl -llog -msse4.2 -maes +Linux-Android-x86_64_LINKFLAGS := -shared -static-libgcc +Linux-Android-x86_64_LIBNAME := libsqlitejdbc.so +Linux-Android-x86_64_SQLITE_FLAGS := Linux-ppc64_CC := $(CROSS_PREFIX)gcc Linux-ppc64_STRIP := $(CROSS_PREFIX)strip @@ -183,7 +207,7 @@ HPUX-ia64_32_LINKFLAGS := -b HPUX-ia64_32_LIBNAME := libsqlitejdbc.so HPUX-ia64_32_SQLITE_FLAGS := -Mac-x86_64_CC := gcc -arch $(OS_ARCH) +Mac-x86_64_CC := gcc -arch $(OS_ARCH) Mac-x86_64_STRIP := strip -x MAC_SDK := /Developer/SDKs/MacOSX10.10.sdk ifeq ($(wildcard MAC_SDK),) @@ -192,7 +216,7 @@ endif Mac-x86_64_CCFLAGS := -I$(MAC_SDK)/System/Library/Frameworks/JavaVM.framework/Headers -Ilib/inc_mac -Os -fPIC -mmacosx-version-min=10.6 -fvisibility=hidden -msse4.2 -maes -Wno-implicit-function-declaration Mac-x86_64_LINKFLAGS := -dynamiclib Mac-x86_64_LIBNAME := libsqlitejdbc.jnilib -Mac-x86_64_SQLITE_FLAGS := +Mac-x86_64_SQLITE_FLAGS := Mac-aarch64_CC := gcc -arch arm64 Mac-aarch64_STRIP := strip -x @@ -210,7 +234,7 @@ Windows-x86_STRIP := $(CROSS_PREFIX)strip Windows-x86_CCFLAGS := -D_JNI_IMPLEMENTATION_ -Ilib/inc_win -Os -msse4.2 -maes Windows-x86_LINKFLAGS := -Wl,--kill-at -shared -static-libgcc Windows-x86_LIBNAME := sqlitejdbc.dll -Windows-x86_SQLITE_FLAGS := +Windows-x86_SQLITE_FLAGS := Windows-x86_64_CC := $(CROSS_PREFIX)gcc Windows-x86_64_STRIP := $(CROSS_PREFIX)strip @@ -236,13 +260,13 @@ Windows-aarch64_SQLITE_FLAGS := CC := $($(target)_CC) STRIP := $($(target)_STRIP) -CCFLAGS := $($(target)_CCFLAGS) -LINKFLAGS := $($(target)_LINKFLAGS) +CCFLAGS := $($(target)_CCFLAGS) +LINKFLAGS := $($(target)_LINKFLAGS) LIBNAME := $($(target)_LIBNAME) SQLITE_FLAGS := $($(target)_SQLITE_FLAGS) SQLITE_AMAL_PREFIX = sqlite-amalgamation-$(shell ./amalgamation_version.sh $(version)) SQLITE_OLD_AMAL_PREFIX = sqlite-amalgamation-$(subst .,_,$(version)) -CCFLAGS := $(CCFLAGS) +CCFLAGS := $(CCFLAGS) ifneq ($(jni_include),) CCFLAGS := $(CCFLAGS) -I"$(jni_include)" endif diff --git a/VERSION b/VERSION index 895e67d775..146b6635e7 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,3 @@ version=3.36.0 -artifactVersion=3.36.0 +artifactVersion=3.36.0.1-SNAPSHOT +sqliteMCVersion=1.3.4 \ No newline at end of file diff --git a/docker/dockcross-android-arm64 b/docker/dockcross-android-arm64 new file mode 100755 index 0000000000..4896a7863d --- /dev/null +++ b/docker/dockcross-android-arm64 @@ -0,0 +1,278 @@ +#!/usr/bin/env bash + +DEFAULT_DOCKCROSS_IMAGE=dockcross/android-arm64:latest + +#------------------------------------------------------------------------------ +# Helpers +# +err() { + echo -e >&2 "ERROR: $*\n" +} + +die() { + err "$*" + exit 1 +} + +has() { + # eg. has command update + local kind=$1 + local name=$2 + + type -t $kind:$name | grep -q function +} + +# If OCI_EXE is not already set, search for a container executor (OCI stands for "Open Container Initiative") +if [ -z "$OCI_EXE" ]; then + if which docker >/dev/null 2>/dev/null; then + OCI_EXE=docker + elif which podman >/dev/null 2>/dev/null; then + OCI_EXE=podman + else + die "Cannot find a container executor. Search for docker and podman." + fi +fi + +#------------------------------------------------------------------------------ +# Command handlers +# +command:update-image() { + $OCI_EXE pull $FINAL_IMAGE +} + +help:update-image() { + echo "Pull the latest $FINAL_IMAGE ." +} + +command:update-script() { + if cmp -s <( $OCI_EXE run --rm $FINAL_IMAGE ) $0; then + echo "$0 is up to date" + else + echo -n "Updating $0 ... " + $OCI_EXE run --rm $FINAL_IMAGE > $0 && echo ok + fi +} + +help:update-image() { + echo "Update $0 from $FINAL_IMAGE ." +} + +command:update() { + command:update-image + command:update-script +} + +help:update() { + echo "Pull the latest $FINAL_IMAGE, and then update $0 from that." +} + +command:help() { + if [[ $# != 0 ]]; then + if ! has command $1; then + err \"$1\" is not an dockcross command + command:help + elif ! has help $1; then + err No help found for \"$1\" + else + help:$1 + fi + else + cat >&2 < +ENDHELP + exit 1 + fi +} + +#------------------------------------------------------------------------------ +# Option processing +# +special_update_command='' +while [[ $# != 0 ]]; do + case $1 in + + --) + shift + break + ;; + + --args|-a) + ARG_ARGS="$2" + shift 2 + ;; + + --config|-c) + ARG_CONFIG="$2" + shift 2 + ;; + + --image|-i) + ARG_IMAGE="$2" + shift 2 + ;; + update|update-image|update-script) + special_update_command=$1 + break + ;; + -*) + err Unknown option \"$1\" + command:help + exit + ;; + + *) + break + ;; + + esac +done + +# The precedence for options is: +# 1. command-line arguments +# 2. environment variables +# 3. defaults + +# Source the config file if it exists +DEFAULT_DOCKCROSS_CONFIG=~/.dockcross +FINAL_CONFIG=${ARG_CONFIG-${DOCKCROSS_CONFIG-$DEFAULT_DOCKCROSS_CONFIG}} + +[[ -f "$FINAL_CONFIG" ]] && source "$FINAL_CONFIG" + +# Set the docker image +FINAL_IMAGE=${ARG_IMAGE-${DOCKCROSS_IMAGE-$DEFAULT_DOCKCROSS_IMAGE}} + +# Handle special update command +if [ "$special_update_command" != "" ]; then + case $special_update_command in + + update) + command:update + exit $? + ;; + + update-image) + command:update-image + exit $? + ;; + + update-script) + command:update-script + exit $? + ;; + + esac +fi + +# Set the docker run extra args (if any) +FINAL_ARGS=${ARG_ARGS-${DOCKCROSS_ARGS}} + +# Bash on Ubuntu on Windows +UBUNTU_ON_WINDOWS=$([ -e /proc/version ] && grep -l Microsoft /proc/version || echo "") +# MSYS, Git Bash, etc. +MSYS=$([ -e /proc/version ] && grep -l MINGW /proc/version || echo "") +# CYGWIN +CYGWIN=$([ -e /proc/version ] && grep -l CYGWIN /proc/version || echo "") + +if [ -z "$UBUNTU_ON_WINDOWS" -a -z "$MSYS" ]; then + USER_IDS=(-e BUILDER_UID="$( id -u )" -e BUILDER_GID="$( id -g )" -e BUILDER_USER="$( id -un )" -e BUILDER_GROUP="$( id -gn )") +fi + +# Change the PWD when working in Docker on Windows +if [ -n "$UBUNTU_ON_WINDOWS" ]; then + WSL_ROOT="/mnt/" + CFG_FILE=/etc/wsl.conf + if [ -f "$CFG_FILE" ]; then + CFG_CONTENT=$(cat $CFG_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g') + eval "$CFG_CONTENT" + if [ -n "$root" ]; then + WSL_ROOT=$root + fi + fi + HOST_PWD=`pwd -P` + HOST_PWD=${HOST_PWD/$WSL_ROOT//} +elif [ -n "$MSYS" ]; then + HOST_PWD=$PWD + HOST_PWD=${HOST_PWD/\//} + HOST_PWD=${HOST_PWD/\//:\/} +elif [ -n "$CYGWIN" ]; then + for f in pwd readlink cygpath ; do + test -n "$(type "${f}" )" || { echo >&2 "Missing functionality (${f}) (in cygwin)." ; exit 1 ; } ; + done ; + HOST_PWD="$( cygpath -w "$( readlink -f "$( pwd ;)" ; )" ; )" ; +else + HOST_PWD=$PWD + [ -L $HOST_PWD ] && HOST_PWD=$(readlink $HOST_PWD) +fi + +# Mount Additional Volumes +if [ -z "$SSH_DIR" ]; then + SSH_DIR="$HOME/.ssh" +fi + +HOST_VOLUMES= +if [ -e "$SSH_DIR" -a -z "$MSYS" ]; then + if test -n "${CYGWIN}" ; then + HOST_VOLUMES+="-v $(cygpath -w ${SSH_DIR} ; ):/home/$(id -un)/.ssh" ; + else + HOST_VOLUMES+="-v $SSH_DIR:/home/$(id -un)/.ssh" ; + fi ; +fi + +#------------------------------------------------------------------------------ +# Now, finally, run the command in a container +# +TTY_ARGS= +tty -s && [ -z "$MSYS" ] && TTY_ARGS=-ti +CONTAINER_NAME=dockcross_$RANDOM +$OCI_EXE run $TTY_ARGS --name $CONTAINER_NAME \ + -v "$HOST_PWD":/work \ + $HOST_VOLUMES \ + "${USER_IDS[@]}" \ + $FINAL_ARGS \ + $FINAL_IMAGE "$@" +run_exit_code=$? + +# Attempt to delete container +rm_output=$($OCI_EXE rm -f $CONTAINER_NAME 2>&1) +rm_exit_code=$? +if [[ $rm_exit_code != 0 ]]; then + if [[ "$CIRCLECI" == "true" ]] && [[ $rm_output == *"Driver btrfs failed to remove"* ]]; then + : # Ignore error because of https://circleci.com/docs/docker-btrfs-error/ + else + echo "$rm_output" + exit $rm_exit_code + fi +fi + +exit $run_exit_code + +################################################################################ +# +# This image is not intended to be run manually. +# +# To create a dockcross helper script for the +# dockcross/android-arm64:latest image, run: +# +# docker run --rm dockcross/android-arm64:latest > dockcross-android-arm64-latest +# chmod +x dockcross-android-arm64-latest +# +# You may then wish to move the dockcross script to your PATH. +# +################################################################################ diff --git a/docker/dockcross-android-x86 b/docker/dockcross-android-x86 new file mode 100755 index 0000000000..e96276a32b --- /dev/null +++ b/docker/dockcross-android-x86 @@ -0,0 +1,278 @@ +#!/usr/bin/env bash + +DEFAULT_DOCKCROSS_IMAGE=dockcross/android-x86:latest + +#------------------------------------------------------------------------------ +# Helpers +# +err() { + echo -e >&2 "ERROR: $*\n" +} + +die() { + err "$*" + exit 1 +} + +has() { + # eg. has command update + local kind=$1 + local name=$2 + + type -t $kind:$name | grep -q function +} + +# If OCI_EXE is not already set, search for a container executor (OCI stands for "Open Container Initiative") +if [ -z "$OCI_EXE" ]; then + if which docker >/dev/null 2>/dev/null; then + OCI_EXE=docker + elif which podman >/dev/null 2>/dev/null; then + OCI_EXE=podman + else + die "Cannot find a container executor. Search for docker and podman." + fi +fi + +#------------------------------------------------------------------------------ +# Command handlers +# +command:update-image() { + $OCI_EXE pull $FINAL_IMAGE +} + +help:update-image() { + echo "Pull the latest $FINAL_IMAGE ." +} + +command:update-script() { + if cmp -s <( $OCI_EXE run --rm $FINAL_IMAGE ) $0; then + echo "$0 is up to date" + else + echo -n "Updating $0 ... " + $OCI_EXE run --rm $FINAL_IMAGE > $0 && echo ok + fi +} + +help:update-image() { + echo "Update $0 from $FINAL_IMAGE ." +} + +command:update() { + command:update-image + command:update-script +} + +help:update() { + echo "Pull the latest $FINAL_IMAGE, and then update $0 from that." +} + +command:help() { + if [[ $# != 0 ]]; then + if ! has command $1; then + err \"$1\" is not an dockcross command + command:help + elif ! has help $1; then + err No help found for \"$1\" + else + help:$1 + fi + else + cat >&2 < +ENDHELP + exit 1 + fi +} + +#------------------------------------------------------------------------------ +# Option processing +# +special_update_command='' +while [[ $# != 0 ]]; do + case $1 in + + --) + shift + break + ;; + + --args|-a) + ARG_ARGS="$2" + shift 2 + ;; + + --config|-c) + ARG_CONFIG="$2" + shift 2 + ;; + + --image|-i) + ARG_IMAGE="$2" + shift 2 + ;; + update|update-image|update-script) + special_update_command=$1 + break + ;; + -*) + err Unknown option \"$1\" + command:help + exit + ;; + + *) + break + ;; + + esac +done + +# The precedence for options is: +# 1. command-line arguments +# 2. environment variables +# 3. defaults + +# Source the config file if it exists +DEFAULT_DOCKCROSS_CONFIG=~/.dockcross +FINAL_CONFIG=${ARG_CONFIG-${DOCKCROSS_CONFIG-$DEFAULT_DOCKCROSS_CONFIG}} + +[[ -f "$FINAL_CONFIG" ]] && source "$FINAL_CONFIG" + +# Set the docker image +FINAL_IMAGE=${ARG_IMAGE-${DOCKCROSS_IMAGE-$DEFAULT_DOCKCROSS_IMAGE}} + +# Handle special update command +if [ "$special_update_command" != "" ]; then + case $special_update_command in + + update) + command:update + exit $? + ;; + + update-image) + command:update-image + exit $? + ;; + + update-script) + command:update-script + exit $? + ;; + + esac +fi + +# Set the docker run extra args (if any) +FINAL_ARGS=${ARG_ARGS-${DOCKCROSS_ARGS}} + +# Bash on Ubuntu on Windows +UBUNTU_ON_WINDOWS=$([ -e /proc/version ] && grep -l Microsoft /proc/version || echo "") +# MSYS, Git Bash, etc. +MSYS=$([ -e /proc/version ] && grep -l MINGW /proc/version || echo "") +# CYGWIN +CYGWIN=$([ -e /proc/version ] && grep -l CYGWIN /proc/version || echo "") + +if [ -z "$UBUNTU_ON_WINDOWS" -a -z "$MSYS" ]; then + USER_IDS=(-e BUILDER_UID="$( id -u )" -e BUILDER_GID="$( id -g )" -e BUILDER_USER="$( id -un )" -e BUILDER_GROUP="$( id -gn )") +fi + +# Change the PWD when working in Docker on Windows +if [ -n "$UBUNTU_ON_WINDOWS" ]; then + WSL_ROOT="/mnt/" + CFG_FILE=/etc/wsl.conf + if [ -f "$CFG_FILE" ]; then + CFG_CONTENT=$(cat $CFG_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g') + eval "$CFG_CONTENT" + if [ -n "$root" ]; then + WSL_ROOT=$root + fi + fi + HOST_PWD=`pwd -P` + HOST_PWD=${HOST_PWD/$WSL_ROOT//} +elif [ -n "$MSYS" ]; then + HOST_PWD=$PWD + HOST_PWD=${HOST_PWD/\//} + HOST_PWD=${HOST_PWD/\//:\/} +elif [ -n "$CYGWIN" ]; then + for f in pwd readlink cygpath ; do + test -n "$(type "${f}" )" || { echo >&2 "Missing functionality (${f}) (in cygwin)." ; exit 1 ; } ; + done ; + HOST_PWD="$( cygpath -w "$( readlink -f "$( pwd ;)" ; )" ; )" ; +else + HOST_PWD=$PWD + [ -L $HOST_PWD ] && HOST_PWD=$(readlink $HOST_PWD) +fi + +# Mount Additional Volumes +if [ -z "$SSH_DIR" ]; then + SSH_DIR="$HOME/.ssh" +fi + +HOST_VOLUMES= +if [ -e "$SSH_DIR" -a -z "$MSYS" ]; then + if test -n "${CYGWIN}" ; then + HOST_VOLUMES+="-v $(cygpath -w ${SSH_DIR} ; ):/home/$(id -un)/.ssh" ; + else + HOST_VOLUMES+="-v $SSH_DIR:/home/$(id -un)/.ssh" ; + fi ; +fi + +#------------------------------------------------------------------------------ +# Now, finally, run the command in a container +# +TTY_ARGS= +tty -s && [ -z "$MSYS" ] && TTY_ARGS=-ti +CONTAINER_NAME=dockcross_$RANDOM +$OCI_EXE run $TTY_ARGS --name $CONTAINER_NAME \ + -v "$HOST_PWD":/work \ + $HOST_VOLUMES \ + "${USER_IDS[@]}" \ + $FINAL_ARGS \ + $FINAL_IMAGE "$@" +run_exit_code=$? + +# Attempt to delete container +rm_output=$($OCI_EXE rm -f $CONTAINER_NAME 2>&1) +rm_exit_code=$? +if [[ $rm_exit_code != 0 ]]; then + if [[ "$CIRCLECI" == "true" ]] && [[ $rm_output == *"Driver btrfs failed to remove"* ]]; then + : # Ignore error because of https://circleci.com/docs/docker-btrfs-error/ + else + echo "$rm_output" + exit $rm_exit_code + fi +fi + +exit $run_exit_code + +################################################################################ +# +# This image is not intended to be run manually. +# +# To create a dockcross helper script for the +# dockcross/android-x86:latest image, run: +# +# docker run --rm dockcross/android-x86:latest > dockcross-android-x86-latest +# chmod +x dockcross-android-x86-latest +# +# You may then wish to move the dockcross script to your PATH. +# +################################################################################ diff --git a/docker/dockcross-android-x86_64 b/docker/dockcross-android-x86_64 new file mode 100755 index 0000000000..00b7d843ea --- /dev/null +++ b/docker/dockcross-android-x86_64 @@ -0,0 +1,278 @@ +#!/usr/bin/env bash + +DEFAULT_DOCKCROSS_IMAGE=dockcross/android-x86_64:latest + +#------------------------------------------------------------------------------ +# Helpers +# +err() { + echo -e >&2 "ERROR: $*\n" +} + +die() { + err "$*" + exit 1 +} + +has() { + # eg. has command update + local kind=$1 + local name=$2 + + type -t $kind:$name | grep -q function +} + +# If OCI_EXE is not already set, search for a container executor (OCI stands for "Open Container Initiative") +if [ -z "$OCI_EXE" ]; then + if which docker >/dev/null 2>/dev/null; then + OCI_EXE=docker + elif which podman >/dev/null 2>/dev/null; then + OCI_EXE=podman + else + die "Cannot find a container executor. Search for docker and podman." + fi +fi + +#------------------------------------------------------------------------------ +# Command handlers +# +command:update-image() { + $OCI_EXE pull $FINAL_IMAGE +} + +help:update-image() { + echo "Pull the latest $FINAL_IMAGE ." +} + +command:update-script() { + if cmp -s <( $OCI_EXE run --rm $FINAL_IMAGE ) $0; then + echo "$0 is up to date" + else + echo -n "Updating $0 ... " + $OCI_EXE run --rm $FINAL_IMAGE > $0 && echo ok + fi +} + +help:update-image() { + echo "Update $0 from $FINAL_IMAGE ." +} + +command:update() { + command:update-image + command:update-script +} + +help:update() { + echo "Pull the latest $FINAL_IMAGE, and then update $0 from that." +} + +command:help() { + if [[ $# != 0 ]]; then + if ! has command $1; then + err \"$1\" is not an dockcross command + command:help + elif ! has help $1; then + err No help found for \"$1\" + else + help:$1 + fi + else + cat >&2 < +ENDHELP + exit 1 + fi +} + +#------------------------------------------------------------------------------ +# Option processing +# +special_update_command='' +while [[ $# != 0 ]]; do + case $1 in + + --) + shift + break + ;; + + --args|-a) + ARG_ARGS="$2" + shift 2 + ;; + + --config|-c) + ARG_CONFIG="$2" + shift 2 + ;; + + --image|-i) + ARG_IMAGE="$2" + shift 2 + ;; + update|update-image|update-script) + special_update_command=$1 + break + ;; + -*) + err Unknown option \"$1\" + command:help + exit + ;; + + *) + break + ;; + + esac +done + +# The precedence for options is: +# 1. command-line arguments +# 2. environment variables +# 3. defaults + +# Source the config file if it exists +DEFAULT_DOCKCROSS_CONFIG=~/.dockcross +FINAL_CONFIG=${ARG_CONFIG-${DOCKCROSS_CONFIG-$DEFAULT_DOCKCROSS_CONFIG}} + +[[ -f "$FINAL_CONFIG" ]] && source "$FINAL_CONFIG" + +# Set the docker image +FINAL_IMAGE=${ARG_IMAGE-${DOCKCROSS_IMAGE-$DEFAULT_DOCKCROSS_IMAGE}} + +# Handle special update command +if [ "$special_update_command" != "" ]; then + case $special_update_command in + + update) + command:update + exit $? + ;; + + update-image) + command:update-image + exit $? + ;; + + update-script) + command:update-script + exit $? + ;; + + esac +fi + +# Set the docker run extra args (if any) +FINAL_ARGS=${ARG_ARGS-${DOCKCROSS_ARGS}} + +# Bash on Ubuntu on Windows +UBUNTU_ON_WINDOWS=$([ -e /proc/version ] && grep -l Microsoft /proc/version || echo "") +# MSYS, Git Bash, etc. +MSYS=$([ -e /proc/version ] && grep -l MINGW /proc/version || echo "") +# CYGWIN +CYGWIN=$([ -e /proc/version ] && grep -l CYGWIN /proc/version || echo "") + +if [ -z "$UBUNTU_ON_WINDOWS" -a -z "$MSYS" ]; then + USER_IDS=(-e BUILDER_UID="$( id -u )" -e BUILDER_GID="$( id -g )" -e BUILDER_USER="$( id -un )" -e BUILDER_GROUP="$( id -gn )") +fi + +# Change the PWD when working in Docker on Windows +if [ -n "$UBUNTU_ON_WINDOWS" ]; then + WSL_ROOT="/mnt/" + CFG_FILE=/etc/wsl.conf + if [ -f "$CFG_FILE" ]; then + CFG_CONTENT=$(cat $CFG_FILE | sed -r '/[^=]+=[^=]+/!d' | sed -r 's/\s+=\s/=/g') + eval "$CFG_CONTENT" + if [ -n "$root" ]; then + WSL_ROOT=$root + fi + fi + HOST_PWD=`pwd -P` + HOST_PWD=${HOST_PWD/$WSL_ROOT//} +elif [ -n "$MSYS" ]; then + HOST_PWD=$PWD + HOST_PWD=${HOST_PWD/\//} + HOST_PWD=${HOST_PWD/\//:\/} +elif [ -n "$CYGWIN" ]; then + for f in pwd readlink cygpath ; do + test -n "$(type "${f}" )" || { echo >&2 "Missing functionality (${f}) (in cygwin)." ; exit 1 ; } ; + done ; + HOST_PWD="$( cygpath -w "$( readlink -f "$( pwd ;)" ; )" ; )" ; +else + HOST_PWD=$PWD + [ -L $HOST_PWD ] && HOST_PWD=$(readlink $HOST_PWD) +fi + +# Mount Additional Volumes +if [ -z "$SSH_DIR" ]; then + SSH_DIR="$HOME/.ssh" +fi + +HOST_VOLUMES= +if [ -e "$SSH_DIR" -a -z "$MSYS" ]; then + if test -n "${CYGWIN}" ; then + HOST_VOLUMES+="-v $(cygpath -w ${SSH_DIR} ; ):/home/$(id -un)/.ssh" ; + else + HOST_VOLUMES+="-v $SSH_DIR:/home/$(id -un)/.ssh" ; + fi ; +fi + +#------------------------------------------------------------------------------ +# Now, finally, run the command in a container +# +TTY_ARGS= +tty -s && [ -z "$MSYS" ] && TTY_ARGS=-ti +CONTAINER_NAME=dockcross_$RANDOM +$OCI_EXE run $TTY_ARGS --name $CONTAINER_NAME \ + -v "$HOST_PWD":/work \ + $HOST_VOLUMES \ + "${USER_IDS[@]}" \ + $FINAL_ARGS \ + $FINAL_IMAGE "$@" +run_exit_code=$? + +# Attempt to delete container +rm_output=$($OCI_EXE rm -f $CONTAINER_NAME 2>&1) +rm_exit_code=$? +if [[ $rm_exit_code != 0 ]]; then + if [[ "$CIRCLECI" == "true" ]] && [[ $rm_output == *"Driver btrfs failed to remove"* ]]; then + : # Ignore error because of https://circleci.com/docs/docker-btrfs-error/ + else + echo "$rm_output" + exit $rm_exit_code + fi +fi + +exit $run_exit_code + +################################################################################ +# +# This image is not intended to be run manually. +# +# To create a dockcross helper script for the +# dockcross/android-x86_64:latest image, run: +# +# docker run --rm dockcross/android-x86_64:latest > dockcross-android-x86_64-latest +# chmod +x dockcross-android-x86_64-latest +# +# You may then wish to move the dockcross script to your PATH. +# +################################################################################ diff --git a/lib/org/sqlite/OSInfo.class b/lib/org/sqlite/OSInfo.class deleted file mode 100644 index f41e53371b..0000000000 Binary files a/lib/org/sqlite/OSInfo.class and /dev/null differ diff --git a/lib/org/sqlite/util/OSInfo.class b/lib/org/sqlite/util/OSInfo.class deleted file mode 100644 index 0ddc8acf49..0000000000 Binary files a/lib/org/sqlite/util/OSInfo.class and /dev/null differ diff --git a/pom.xml b/pom.xml index 81a42a60aa..4ab8da1ff1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 io.github.willena sqlite-jdbc - 3.36.0 + 3.36.0.1-SNAPSHOT SQLite JDBC SQLite JDBC library with encryption and authentication support https://github.com/Willena/sqlite-jdbc-crypt @@ -325,7 +325,6 @@ - org.junit.jupiter @@ -345,5 +344,17 @@ 1.3 test + + org.junit-pioneer + junit-pioneer + 1.4.2 + test + + + org.mockito + mockito-core + 3.12.4 + test + diff --git a/src/main/java/org/sqlite/SQLiteConfig.java b/src/main/java/org/sqlite/SQLiteConfig.java index 40d075af26..e764b74945 100755 --- a/src/main/java/org/sqlite/SQLiteConfig.java +++ b/src/main/java/org/sqlite/SQLiteConfig.java @@ -23,8 +23,6 @@ // -------------------------------------- package org.sqlite; -import org.sqlite.mc.SQLiteMCConfig; - import java.sql.Connection; import java.sql.DriverManager; import java.sql.DriverPropertyInfo; @@ -34,6 +32,7 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import org.sqlite.mc.SQLiteMCConfig; /** * SQLite Configuration @@ -185,9 +184,9 @@ public void apply(Connection conn) throws SQLException { pragmaParams.remove(Pragma.LIMIT_WORKER_THREADS.pragmaName); pragmaParams.remove(Pragma.LIMIT_PAGE_COUNT.pragmaName); - - //Remove SQLiteMC related PRAGMAS, so that we are not applied twice - //TODO : Clone the pragmaTable and remove each Pragma when used so that no checking is required ? + // Remove SQLiteMC related PRAGMAS, so that we are not applied twice + // TODO : Clone the pragmaTable and remove each Pragma when used so that no checking is + // required ? // will make a lighter code ? pragmaParams.remove(Pragma.KEY.pragmaName); pragmaParams.remove(Pragma.REKEY.pragmaName); @@ -205,13 +204,16 @@ public void apply(Connection conn) throws SQLException { pragmaParams.remove(Pragma.PLAINTEXT_HEADER_SIZE.pragmaName); pragmaParams.remove(Pragma.MC_USE_SQL_INTERFACE.pragmaName); - Statement stat = conn.createStatement(); try { - if (pragmaTable.containsKey(Pragma.PASSWORD.pragmaName) || pragmaTable.containsKey(Pragma.KEY.pragmaName)) { + if (pragmaTable.containsKey(Pragma.PASSWORD.pragmaName) + || pragmaTable.containsKey(Pragma.KEY.pragmaName)) { String password = pragmaTable.getProperty(Pragma.KEY.pragmaName); - password = password == null || password.isEmpty() ? pragmaTable.getProperty(Pragma.PASSWORD.pragmaName) : password; + password = + password == null || password.isEmpty() + ? pragmaTable.getProperty(Pragma.PASSWORD.pragmaName) + : password; String cipherName = pragmaTable.getProperty(Pragma.CIPHER.pragmaName); @@ -222,7 +224,6 @@ public void apply(Connection conn) throws SQLException { config.applyCipherParameters(conn, stat); } - if (password != null && !password.isEmpty()) { String hexkeyMode = pragmaTable.getProperty(Pragma.HEXKEY_MODE.pragmaName); String passwordPragma; @@ -260,7 +261,7 @@ public void apply(Connection conn) throws SQLException { * Sets a pragma to the given boolean value. * * @param pragma The pragma to set. - * @param flag The boolean value. + * @param flag The boolean value. */ private void set(Pragma pragma, boolean flag) { setPragma(pragma, Boolean.toString(flag)); @@ -270,7 +271,7 @@ private void set(Pragma pragma, boolean flag) { * Sets a pragma to the given int value. * * @param pragma The pragma to set. - * @param num The int value. + * @param num The int value. */ private void set(Pragma pragma, int num) { setPragma(pragma, Integer.toString(num)); @@ -279,7 +280,7 @@ private void set(Pragma pragma, int num) { /** * Checks if the provided value is the default for a given pragma. * - * @param pragma The pragma on which to check. + * @param pragma The pragma on which to check. * @param defaultValue The value to check for. * @return True if the given value is the default value; false otherwise. */ @@ -290,7 +291,7 @@ private boolean getBoolean(Pragma pragma, String defaultValue) { /** * Retrives a pragma integer value. * - * @param pragma The pragma. + * @param pragma The pragma. * @param defaultValue The default value. * @return The value of the pragma or defaultValue. */ @@ -321,7 +322,9 @@ public boolean isEnabledSharedCache() { * @return True if turned on; false otherwise. */ public boolean isEnabledSharedCacheConnection() { - return pragmaTable.getProperty(Pragma.CACHE.pragmaName, Cache.PRIVATE.getValue()).equalsIgnoreCase(Cache.SHARED.getValue()); + return pragmaTable + .getProperty(Pragma.CACHE.pragmaName, Cache.PRIVATE.getValue()) + .equalsIgnoreCase(Cache.SHARED.getValue()); } /** @@ -342,7 +345,7 @@ public int getOpenModeFlags() { * Sets a pragma's value. * * @param pragma The pragma to change. - * @param value The value to set it to. + * @param value The value to set it to. */ public void setPragma(Pragma pragma, String value) { pragmaTable.put(pragma.pragmaName, value); @@ -387,7 +390,7 @@ static DriverPropertyInfo[] getDriverPropertyInfo() { return result; } - private static final String[] OnOff = new String[]{"true", "false"}; + private static final String[] OnOff = new String[] {"true", "false"}; static final Set pragmaSet = new TreeSet(); @@ -406,7 +409,10 @@ public static enum Pragma { "enable_load_extension", "Enable SQLite load_extention() function, native driver only", OnOff), - CACHE("cache", "Enable SQLite Shared-Cache mode (connection)", toStringArray(Cache.values())), + CACHE( + "cache", + "Enable SQLite Shared-Cache mode (connection)", + toStringArray(Cache.values())), // Pragmas that can be set after opening the database CACHE_SIZE("cache_size"), @@ -498,11 +504,11 @@ public static enum Pragma { null), BUSY_TIMEOUT("busy_timeout", null), - //Keep compatibility for legacy Xenial JDBC implementation + // Keep compatibility for legacy Xenial JDBC implementation HEXKEY_MODE("hexkey_mode", toStringArray(HexKeyMode.values())), PASSWORD("password", null), - //New pragmas for SQLiteMC improved support + // New pragmas for SQLiteMC improved support KEY("key", null), REKEY("rekey", null), CIPHER("cipher", null), @@ -577,10 +583,11 @@ public void setSharedCache(boolean enable) { } /** - * Enables or disables the sharing of the database cache and schema data - * structures between connections to the same database (connection). + * Enables or disables the sharing of the database cache and schema data structures between + * connections to the same database (connection). * - * @see www.sqlite.org/sharedcache.html#enabling_shared_cache_mode + * @see www.sqlite.org/sharedcache.html#enabling_shared_cache_mode */ public void setCacheMode(Cache value) { setPragma(Pragma.CACHE, value.name()); @@ -643,8 +650,8 @@ public void enableCaseSensitiveLike(boolean enable) { * @param enable True to enable; false to disable. * @see www.sqlite.org/pragma.html#pragma_count_changes - * @deprecated Enables or disables the count-changes flag. When enabled, INSERT, UPDATE - * and DELETE statements return the number of rows they modified. + * @deprecated Enables or disables the count-changes flag. When enabled, INSERT, UPDATE and + * DELETE statements return the number of rows they modified. */ @Deprecated public void enableCountChanges(boolean enable) { @@ -751,7 +758,9 @@ public String getValue() { } public static enum AutoVacuum implements PragmaValue { - NONE, FULL, INCREMENTAL; + NONE, + FULL, + INCREMENTAL; public String getValue() { return name(); @@ -759,7 +768,8 @@ public String getValue() { } public static enum Cache implements PragmaValue { - PRIVATE, SHARED; + PRIVATE, + SHARED; public String getValue() { return name(); @@ -819,7 +829,8 @@ public void enableFullSync(boolean enable) { /** * Sets the auto vacuum mode; * - * @see www.sqlite.org/pragma.html#pragma_auto_vacuum + * @see www.sqlite.org/pragma.html#pragma_auto_vacuum */ public void setAutoVacuum(AutoVacuum value) { setPragma(Pragma.AUTO_VACUUM, value.name()); diff --git a/src/main/java/org/sqlite/SQLiteConnection.java b/src/main/java/org/sqlite/SQLiteConnection.java index 29a80d528b..fc17932a81 100644 --- a/src/main/java/org/sqlite/SQLiteConnection.java +++ b/src/main/java/org/sqlite/SQLiteConnection.java @@ -57,19 +57,21 @@ public SQLiteConnection(String url, String fileName) throws SQLException { * @throws SQLException */ public SQLiteConnection(String url, String fileName, Properties prop) throws SQLException { - this.db = open(url, fileName, prop); - SQLiteConfig config = db.getConfig(); - this.connectionConfig = config.newConnectionConfig(); + DB newDB = null; try { + this.db = newDB = open(url, fileName, prop); + SQLiteConfig config = this.db.getConfig(); + this.connectionConfig = this.db.getConfig().newConnectionConfig(); config.apply(this); - } catch (SQLException openException) { + } catch (Throwable t) { try { - this.db.close(); - } catch (SQLException closeException) { - // That may happen because there could be a misconfiguration. - // The original exception should be thrown to help people debugging. + if (newDB != null) { + newDB.close(); + } + } catch (Exception e) { + t.addSuppressed(e); } - throw openException; + throw t; } } @@ -249,7 +251,7 @@ private static DB open(String url, String origFileName, Properties props) throws } // load the native DB - DB db; + DB db = null; try { NativeDB.load(); db = new NativeDB(url, fileName, config); @@ -511,8 +513,8 @@ protected static String extractPragmasFromFilename(String url, String filename, } String value = kvp[1].trim(); - //Special case for keys given by URL. Should be URL encoded - if (key.equals(SQLiteConfig.Pragma.KEY.pragmaName)){ + // Special case for keys given by URL. Should be URL encoded + if (key.equals(SQLiteConfig.Pragma.KEY.pragmaName)) { try { value = URLDecoder.decode(value, "utf8"); } catch (UnsupportedEncodingException ignored) { diff --git a/src/main/java/org/sqlite/SQLiteDataSource.java b/src/main/java/org/sqlite/SQLiteDataSource.java index e16706e97a..3ae10fd92d 100755 --- a/src/main/java/org/sqlite/SQLiteDataSource.java +++ b/src/main/java/org/sqlite/SQLiteDataSource.java @@ -118,10 +118,12 @@ public void setSharedCache(boolean enable) { } /** - * Enables or disables the sharing of the database cache and schema data - * structures between connections to the same database. + * Enables or disables the sharing of the database cache and schema data structures between + * connections to the same database. + * * @param enable True to enable; false to disable. - * @see http://www.sqlite.org/c3ref/enable_shared_cache.html + * @see http://www.sqlite.org/c3ref/enable_shared_cache.html */ public void setSharedCacheConnection(boolean enable) { config.setCacheMode(enable ? Cache.SHARED : Cache.PRIVATE); diff --git a/src/main/java/org/sqlite/SQLiteJDBCLoader.java b/src/main/java/org/sqlite/SQLiteJDBCLoader.java index d028cc8420..0bc43e1835 100644 --- a/src/main/java/org/sqlite/SQLiteJDBCLoader.java +++ b/src/main/java/org/sqlite/SQLiteJDBCLoader.java @@ -436,8 +436,7 @@ public static String getVersion() { versionData.load(versionFile.openStream()); version = versionData.getProperty("version", version); version = version.trim().replaceAll("[^0-9\\.]", ""); - } - else { + } else { throw new FileNotFoundException("Version file is null"); } } catch (IOException e) { diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java b/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java index 1ce3209f29..9d5c5eb08e 100644 --- a/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java +++ b/src/main/java/org/sqlite/jdbc3/JDBC3ResultSet.java @@ -153,14 +153,28 @@ public boolean wasNull() throws SQLException { /** @see java.sql.ResultSet#getBigDecimal(int) */ public BigDecimal getBigDecimal(int col) throws SQLException { - final String stringValue = getString(col); - if (stringValue == null) { - return null; + final int columnType = getColumnType(col); + + if (columnType == Types.INTEGER) { + long decimal = getLong(col); + return BigDecimal.valueOf(decimal); + } else if (columnType == Types.FLOAT || columnType == Types.DOUBLE) { + final double decimal = getDouble(col); + if (Double.isNaN(decimal)) { + throw new SQLException("Bad value for type BigDecimal : Not a Number"); + } else { + return BigDecimal.valueOf(decimal); + } } else { - try { - return new BigDecimal(stringValue); - } catch (NumberFormatException e) { - throw new SQLException("Bad value for type BigDecimal : " + stringValue); + final String stringValue = getString(col); + if (stringValue == null) { + return null; + } else { + try { + return new BigDecimal(stringValue); + } catch (NumberFormatException e) { + throw new SQLException("Bad value for type BigDecimal : " + stringValue); + } } } } diff --git a/src/main/java/org/sqlite/mc/SQLiteMCChacha20Config.java b/src/main/java/org/sqlite/mc/SQLiteMCChacha20Config.java index 579549bc27..1d5ba8586c 100644 --- a/src/main/java/org/sqlite/mc/SQLiteMCChacha20Config.java +++ b/src/main/java/org/sqlite/mc/SQLiteMCChacha20Config.java @@ -2,43 +2,42 @@ public class SQLiteMCChacha20Config extends SQLiteMCConfig.Builder { - public SQLiteMCChacha20Config(){ - super(); - setCipher(CipherAlgorithm.CHACHA20); - } - - public SQLiteMCChacha20Config setLegacy(int value) { - assert isValid(value, 0, 4); - super.setLegacy(value); - return this; - } - - public SQLiteMCChacha20Config setLegacyPageSize(int value) { - assert isValid(value, 0, 65536); - super.setLegacyPageSize(value); - return this; - } - - public SQLiteMCChacha20Config setKdfIter(int value) { - assert isValid(value, 1, Integer.MAX_VALUE); - super.setKdfIter(value); - return this; - } - - public static SQLiteMCChacha20Config getDefault() { - SQLiteMCChacha20Config config = new SQLiteMCChacha20Config(); - config.setKdfIter(64007); - config.setLegacy(0); - config.setLegacyPageSize(4096); - return config; - } - - public static SQLiteMCChacha20Config getSqlleetDefaults() { - SQLiteMCChacha20Config config = new SQLiteMCChacha20Config(); - config.setKdfIter(12345); - config.setLegacy(1); - config.setLegacyPageSize(4096); - return config; - } - + public SQLiteMCChacha20Config() { + super(); + setCipher(CipherAlgorithm.CHACHA20); + } + + public SQLiteMCChacha20Config setLegacy(int value) { + assert isValid(value, 0, 4); + super.setLegacy(value); + return this; + } + + public SQLiteMCChacha20Config setLegacyPageSize(int value) { + assert isValid(value, 0, 65536); + super.setLegacyPageSize(value); + return this; + } + + public SQLiteMCChacha20Config setKdfIter(int value) { + assert isValid(value, 1, Integer.MAX_VALUE); + super.setKdfIter(value); + return this; + } + + public static SQLiteMCChacha20Config getDefault() { + SQLiteMCChacha20Config config = new SQLiteMCChacha20Config(); + config.setKdfIter(64007); + config.setLegacy(0); + config.setLegacyPageSize(4096); + return config; + } + + public static SQLiteMCChacha20Config getSqlleetDefaults() { + SQLiteMCChacha20Config config = new SQLiteMCChacha20Config(); + config.setKdfIter(12345); + config.setLegacy(1); + config.setLegacyPageSize(4096); + return config; + } } diff --git a/src/main/java/org/sqlite/mc/SQLiteMCConfig.java b/src/main/java/org/sqlite/mc/SQLiteMCConfig.java index 227b2ec341..ac78ef760e 100644 --- a/src/main/java/org/sqlite/mc/SQLiteMCConfig.java +++ b/src/main/java/org/sqlite/mc/SQLiteMCConfig.java @@ -1,30 +1,28 @@ package org.sqlite.mc; - -import org.sqlite.SQLiteConfig; - import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; +import org.sqlite.SQLiteConfig; public class SQLiteMCConfig extends SQLiteConfig { - private static final Pragma[] CIPHER_PRAGMA_ORDER = new Pragma[]{ - Pragma.CIPHER, - Pragma.LEGACY, - Pragma.HMAC_CHECK, - Pragma.LEGACY_PAGE_SIZE, - Pragma.KDF_ITER, - Pragma.FAST_KDF_ITER, - Pragma.HMAC_USE, - Pragma.HMAC_PGNO, - Pragma.HMAC_SALT_MASK, - Pragma.KDF_ALGORITHM, - Pragma.HMAC_ALGORITHM, - Pragma.PLAINTEXT_HEADER_SIZE, - }; + private static final Pragma[] CIPHER_PRAGMA_ORDER = + new Pragma[] { + Pragma.CIPHER, + Pragma.LEGACY, + Pragma.HMAC_CHECK, + Pragma.LEGACY_PAGE_SIZE, + Pragma.KDF_ITER, + Pragma.FAST_KDF_ITER, + Pragma.HMAC_USE, + Pragma.HMAC_PGNO, + Pragma.HMAC_SALT_MASK, + Pragma.KDF_ALGORITHM, + Pragma.HMAC_ALGORITHM, + Pragma.PLAINTEXT_HEADER_SIZE, + }; public SQLiteMCConfig() { super(); @@ -43,11 +41,12 @@ protected SQLiteMCConfig setCipher(CipherAlgorithm cipherAlgorithm) { return this; } - public SQLiteMCConfig withKey(String key) { - // Hex Key is a string like any key. It will be processed by SQLite. ex: String a = "x'aecc05ff'" - // Raw Key is a string like any other key.It will be processed by SQLite. ex: String a = "raw'aecc05ff'" + // Hex Key is a string like any key. It will be processed by SQLite. ex: String a = + // "x'aecc05ff'" + // Raw Key is a string like any other key.It will be processed by SQLite. ex: String a = + // "raw'aecc05ff'" setPragma(Pragma.KEY, key); // For compatibility reason key as the password Pragma. @@ -108,7 +107,7 @@ protected SQLiteMCConfig setPlaintextHeaderSize(int value) { } public SQLiteMCConfig useSQLInterface(boolean sqlInterface) { - setPragma(Pragma.MC_USE_SQL_INTERFACE, sqlInterface?"true":"false"); + setPragma(Pragma.MC_USE_SQL_INTERFACE, sqlInterface ? "true" : "false"); return this; } @@ -116,10 +115,13 @@ public void applyCipherParameters(Connection conn, Statement stat) throws SQLExc applyCipherParametersByNames(CIPHER_PRAGMA_ORDER, conn, stat); } - protected void applyCipherParametersByNames(Pragma[] pragmas, Connection conn, Statement statement) throws SQLException { + protected void applyCipherParametersByNames( + Pragma[] pragmas, Connection conn, Statement statement) throws SQLException { Properties p = super.toProperties(); - boolean useSQLInterface = Boolean.parseBoolean(p.getProperty(Pragma.MC_USE_SQL_INTERFACE.getPragmaName(), "false" )); + boolean useSQLInterface = + Boolean.parseBoolean( + p.getProperty(Pragma.MC_USE_SQL_INTERFACE.getPragmaName(), "false")); String cipherProperty = p.getProperty(Pragma.CIPHER.getPragmaName(), null); if (cipherProperty == null) @@ -130,13 +132,20 @@ protected void applyCipherParametersByNames(Pragma[] pragmas, Connection conn, S if (property != null) { if (!useSQLInterface) - statement.execute(String.format("PRAGMA %s = %s", pragma.getPragmaName(), property)); + statement.execute( + String.format("PRAGMA %s = %s", pragma.getPragmaName(), property)); else { if (pragma.equals(Pragma.CIPHER)) { - String sql = String.format("SELECT sqlite3mc_config('default:%s', '%s');", pragma.getPragmaName(), cipherProperty); + String sql = + String.format( + "SELECT sqlite3mc_config('default:%s', '%s');", + pragma.getPragmaName(), cipherProperty); conn.createStatement().execute(sql); } else { - String sql = String.format("SELECT sqlite3mc_config('%s', 'default:%s', %s);", cipherProperty, pragma.getPragmaName(), property); + String sql = + String.format( + "SELECT sqlite3mc_config('%s', 'default:%s', %s);", + cipherProperty, pragma.getPragmaName(), property); conn.createStatement().execute(sql); } } @@ -180,7 +189,6 @@ public String getValue() { } } - public static class Builder extends SQLiteMCConfig { @Override public Builder setPlaintextHeaderSize(int value) { @@ -241,5 +249,4 @@ public SQLiteMCConfig toSQLiteMCConfig() { return this; } } - } diff --git a/src/main/java/org/sqlite/mc/SQLiteMCRC4Config.java b/src/main/java/org/sqlite/mc/SQLiteMCRC4Config.java index 8a462e9440..b44d37530b 100644 --- a/src/main/java/org/sqlite/mc/SQLiteMCRC4Config.java +++ b/src/main/java/org/sqlite/mc/SQLiteMCRC4Config.java @@ -1,37 +1,30 @@ package org.sqlite.mc; -import org.sqlite.SQLiteConfig; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; - public class SQLiteMCRC4Config extends SQLiteMCConfig.Builder { - public SQLiteMCRC4Config(){ - super(); - setCipher(CipherAlgorithm.RC4); - } - - public SQLiteMCRC4Config setLegacyValue(int value){ - assert isValid(value, 1,1); - super.setLegacy(value); - return this; - } + public SQLiteMCRC4Config() { + super(); + setCipher(CipherAlgorithm.RC4); + } - public SQLiteMCRC4Config setlegacyPageSize(int value){ - assert isValid(value, 0,65536); - super.setLegacyPageSize(value); - return this; - } + public SQLiteMCRC4Config setLegacyValue(int value) { + assert isValid(value, 1, 1); + super.setLegacy(value); + return this; + } + public SQLiteMCRC4Config setlegacyPageSize(int value) { + assert isValid(value, 0, 65536); + super.setLegacyPageSize(value); + return this; + } - public static SQLiteMCRC4Config getDefault() { - SQLiteMCRC4Config config = new SQLiteMCRC4Config(); + public static SQLiteMCRC4Config getDefault() { + SQLiteMCRC4Config config = new SQLiteMCRC4Config(); - config.setLegacyValue(1); - config.setlegacyPageSize(0); + config.setLegacyValue(1); + config.setlegacyPageSize(0); - return config; - } + return config; + } } diff --git a/src/main/java/org/sqlite/mc/SQLiteMCSqlCipherConfig.java b/src/main/java/org/sqlite/mc/SQLiteMCSqlCipherConfig.java index 9be29feb54..2132880bd2 100644 --- a/src/main/java/org/sqlite/mc/SQLiteMCSqlCipherConfig.java +++ b/src/main/java/org/sqlite/mc/SQLiteMCSqlCipherConfig.java @@ -1,131 +1,128 @@ package org.sqlite.mc; -import org.sqlite.SQLiteConfig; - public class SQLiteMCSqlCipherConfig extends SQLiteMCConfig.Builder { - public SQLiteMCSqlCipherConfig() { - super(); - setCipher(CipherAlgorithm.SQL_CIPHER); - } - - public SQLiteMCSqlCipherConfig setLegacy(int value) { - assert isValid(value, 0, 4); - super.setLegacy(value); - return this; - } - - public SQLiteMCSqlCipherConfig setLegacyPageSize(int value) { - assert isValid(value, 0, 65536); - super.setLegacyPageSize(value); - return this; - } - - public SQLiteMCSqlCipherConfig setKdfIter(int value) { - assert isValid(value, 1, Integer.MAX_VALUE); - super.setKdfIter(value); - return this; - } - - public SQLiteMCSqlCipherConfig setFastKdfIter(int value) { - assert isValid(value, 1, Integer.MAX_VALUE); - super.setFastKdfIter(value); - return this; - } - - public SQLiteMCSqlCipherConfig setHmacUse(boolean value) { - super.setHmacUse(value); - return this; - } - - public SQLiteMCSqlCipherConfig setHmacPgno(HmacPgno value) { - assert isValid(value.ordinal(), 0, 2); - super.setHmacPgno(value); - return this; - } - - public SQLiteMCSqlCipherConfig setHmacSaltMask(int value) { - assert isValid(value, 0, 255); - super.setHmacSaltMask(value); - return this; - } - - public SQLiteMCSqlCipherConfig setKdfAlgorithm(KdfAlgorithm value) { - assert isValid(value.ordinal(), 0, 2); - super.setKdfAlgorithm(value); - return this; - } - - public SQLiteMCSqlCipherConfig setHmacAlgorithm(HmacAlgorithm value) { - assert isValid(value.ordinal(), 0, 2); - super.setHmacAlgorithm(value); - return this; - } - - public SQLiteMCSqlCipherConfig setPlaintextHeaderSize(int value) { - assert isValid(value, 0, 100); - assert value % 16 == 0; //Must be multiple of 16 - super.setPlaintextHeaderSize(value); - return this; - } - - public static SQLiteMCSqlCipherConfig getDefault() { - return new SQLiteMCSqlCipherConfig(); - } - - public static SQLiteMCSqlCipherConfig getV1Defaults() { - SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); - config.setKdfIter(4000); - config.setFastKdfIter(2); - config.setHmacUse(false); - config.setLegacy(1); - config.setLegacyPageSize(1024); - config.setKdfAlgorithm(KdfAlgorithm.SHA1); - config.setHmacAlgorithm(HmacAlgorithm.SHA1); - return config; - } - - public static SQLiteMCSqlCipherConfig getV2Defaults() { - SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); - config.setKdfIter(4000); - config.setFastKdfIter(2); - config.setHmacUse(true); - config.setHmacPgno(HmacPgno.LITTLE_ENDIAN); - config.setHmacSaltMask(0x3a); - config.setLegacy(2); - config.setLegacyPageSize(1024); - config.setKdfAlgorithm(KdfAlgorithm.SHA1); - config.setHmacAlgorithm(HmacAlgorithm.SHA1); - return config; - } - - public static SQLiteMCSqlCipherConfig getV3Defaults() { - SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); - config.setKdfIter(64000); - config.setFastKdfIter(2); - config.setHmacUse(true); - config.setHmacPgno(HmacPgno.LITTLE_ENDIAN); - config.setHmacSaltMask(0x3a); - config.setLegacy(3); - config.setLegacyPageSize(1024); - config.setKdfAlgorithm(KdfAlgorithm.SHA1); - config.setHmacAlgorithm(HmacAlgorithm.SHA1); - return config; - } - - public static SQLiteMCSqlCipherConfig getV4Defaults() { - SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); - config.setKdfIter(256000); - config.setFastKdfIter(2); - config.setHmacUse(true); - config.setHmacPgno(HmacPgno.LITTLE_ENDIAN); - config.setHmacSaltMask(0x3a); - config.setLegacy(4); - config.setLegacyPageSize(4096); - config.setKdfAlgorithm(KdfAlgorithm.SHA512); - config.setHmacAlgorithm(HmacAlgorithm.SHA512); - config.setPlaintextHeaderSize(0); - return config; - } - + public SQLiteMCSqlCipherConfig() { + super(); + setCipher(CipherAlgorithm.SQL_CIPHER); + } + + public SQLiteMCSqlCipherConfig setLegacy(int value) { + assert isValid(value, 0, 4); + super.setLegacy(value); + return this; + } + + public SQLiteMCSqlCipherConfig setLegacyPageSize(int value) { + assert isValid(value, 0, 65536); + super.setLegacyPageSize(value); + return this; + } + + public SQLiteMCSqlCipherConfig setKdfIter(int value) { + assert isValid(value, 1, Integer.MAX_VALUE); + super.setKdfIter(value); + return this; + } + + public SQLiteMCSqlCipherConfig setFastKdfIter(int value) { + assert isValid(value, 1, Integer.MAX_VALUE); + super.setFastKdfIter(value); + return this; + } + + public SQLiteMCSqlCipherConfig setHmacUse(boolean value) { + super.setHmacUse(value); + return this; + } + + public SQLiteMCSqlCipherConfig setHmacPgno(HmacPgno value) { + assert isValid(value.ordinal(), 0, 2); + super.setHmacPgno(value); + return this; + } + + public SQLiteMCSqlCipherConfig setHmacSaltMask(int value) { + assert isValid(value, 0, 255); + super.setHmacSaltMask(value); + return this; + } + + public SQLiteMCSqlCipherConfig setKdfAlgorithm(KdfAlgorithm value) { + assert isValid(value.ordinal(), 0, 2); + super.setKdfAlgorithm(value); + return this; + } + + public SQLiteMCSqlCipherConfig setHmacAlgorithm(HmacAlgorithm value) { + assert isValid(value.ordinal(), 0, 2); + super.setHmacAlgorithm(value); + return this; + } + + public SQLiteMCSqlCipherConfig setPlaintextHeaderSize(int value) { + assert isValid(value, 0, 100); + assert value % 16 == 0; // Must be multiple of 16 + super.setPlaintextHeaderSize(value); + return this; + } + + public static SQLiteMCSqlCipherConfig getDefault() { + return new SQLiteMCSqlCipherConfig(); + } + + public static SQLiteMCSqlCipherConfig getV1Defaults() { + SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); + config.setKdfIter(4000); + config.setFastKdfIter(2); + config.setHmacUse(false); + config.setLegacy(1); + config.setLegacyPageSize(1024); + config.setKdfAlgorithm(KdfAlgorithm.SHA1); + config.setHmacAlgorithm(HmacAlgorithm.SHA1); + return config; + } + + public static SQLiteMCSqlCipherConfig getV2Defaults() { + SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); + config.setKdfIter(4000); + config.setFastKdfIter(2); + config.setHmacUse(true); + config.setHmacPgno(HmacPgno.LITTLE_ENDIAN); + config.setHmacSaltMask(0x3a); + config.setLegacy(2); + config.setLegacyPageSize(1024); + config.setKdfAlgorithm(KdfAlgorithm.SHA1); + config.setHmacAlgorithm(HmacAlgorithm.SHA1); + return config; + } + + public static SQLiteMCSqlCipherConfig getV3Defaults() { + SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); + config.setKdfIter(64000); + config.setFastKdfIter(2); + config.setHmacUse(true); + config.setHmacPgno(HmacPgno.LITTLE_ENDIAN); + config.setHmacSaltMask(0x3a); + config.setLegacy(3); + config.setLegacyPageSize(1024); + config.setKdfAlgorithm(KdfAlgorithm.SHA1); + config.setHmacAlgorithm(HmacAlgorithm.SHA1); + return config; + } + + public static SQLiteMCSqlCipherConfig getV4Defaults() { + SQLiteMCSqlCipherConfig config = new SQLiteMCSqlCipherConfig(); + config.setKdfIter(256000); + config.setFastKdfIter(2); + config.setHmacUse(true); + config.setHmacPgno(HmacPgno.LITTLE_ENDIAN); + config.setHmacSaltMask(0x3a); + config.setLegacy(4); + config.setLegacyPageSize(4096); + config.setKdfAlgorithm(KdfAlgorithm.SHA512); + config.setHmacAlgorithm(HmacAlgorithm.SHA512); + config.setPlaintextHeaderSize(0); + return config; + } } diff --git a/src/main/java/org/sqlite/mc/SQLiteMCWxAES128Config.java b/src/main/java/org/sqlite/mc/SQLiteMCWxAES128Config.java index 89dde2fa38..ffe8f6517e 100644 --- a/src/main/java/org/sqlite/mc/SQLiteMCWxAES128Config.java +++ b/src/main/java/org/sqlite/mc/SQLiteMCWxAES128Config.java @@ -1,32 +1,30 @@ package org.sqlite.mc; -import org.sqlite.SQLiteConfig; - public class SQLiteMCWxAES128Config extends SQLiteMCConfig.Builder { - public SQLiteMCWxAES128Config(){ - super(); - setCipher(CipherAlgorithm.WX_AES128); - } + public SQLiteMCWxAES128Config() { + super(); + setCipher(CipherAlgorithm.WX_AES128); + } - @Override - public SQLiteMCWxAES128Config setLegacy(int value) { - assert isValid(value, 0,1); - super.setLegacy(value); - return this; - } + @Override + public SQLiteMCWxAES128Config setLegacy(int value) { + assert isValid(value, 0, 1); + super.setLegacy(value); + return this; + } - @Override - public SQLiteMCWxAES128Config setLegacyPageSize(int value) { - assert isValid(value,0,65536); - super.setLegacyPageSize(value); - return this; - } + @Override + public SQLiteMCWxAES128Config setLegacyPageSize(int value) { + assert isValid(value, 0, 65536); + super.setLegacyPageSize(value); + return this; + } - public static SQLiteMCWxAES128Config getDefault(){ - SQLiteMCWxAES128Config config = new SQLiteMCWxAES128Config(); - config.setLegacy(0); - config.setLegacyPageSize(0); - return config; - } + public static SQLiteMCWxAES128Config getDefault() { + SQLiteMCWxAES128Config config = new SQLiteMCWxAES128Config(); + config.setLegacy(0); + config.setLegacyPageSize(0); + return config; + } } diff --git a/src/main/java/org/sqlite/mc/SQLiteMCWxAES256Config.java b/src/main/java/org/sqlite/mc/SQLiteMCWxAES256Config.java index 3e497a414d..8a09914ec7 100644 --- a/src/main/java/org/sqlite/mc/SQLiteMCWxAES256Config.java +++ b/src/main/java/org/sqlite/mc/SQLiteMCWxAES256Config.java @@ -1,42 +1,38 @@ package org.sqlite.mc; -import org.sqlite.SQLiteConfig; - public class SQLiteMCWxAES256Config extends SQLiteMCConfig.Builder { - public SQLiteMCWxAES256Config(){ - super(); - setCipher(CipherAlgorithm.WX_AES256); - } - - @Override - public SQLiteMCWxAES256Config setLegacy(int value) { - assert isValid(value, 0,1); - super.setLegacy(value); - return this; - } - - @Override - public SQLiteMCWxAES256Config setLegacyPageSize(int value) { - assert isValid(value,0,65536); - super.setLegacyPageSize(value); - return this; - } - - @Override - public SQLiteMCWxAES256Config setKdfIter(int value) { - assert isValid(value, 1, Integer.MAX_VALUE); - super.setKdfIter(value); - return this; - } - - public static SQLiteMCWxAES256Config getDefault(){ - SQLiteMCWxAES256Config config = new SQLiteMCWxAES256Config(); - config.setLegacy(0); - config.setLegacyPageSize(0); - config.setKdfIter(4001); - return config; - } - - + public SQLiteMCWxAES256Config() { + super(); + setCipher(CipherAlgorithm.WX_AES256); + } + + @Override + public SQLiteMCWxAES256Config setLegacy(int value) { + assert isValid(value, 0, 1); + super.setLegacy(value); + return this; + } + + @Override + public SQLiteMCWxAES256Config setLegacyPageSize(int value) { + assert isValid(value, 0, 65536); + super.setLegacyPageSize(value); + return this; + } + + @Override + public SQLiteMCWxAES256Config setKdfIter(int value) { + assert isValid(value, 1, Integer.MAX_VALUE); + super.setKdfIter(value); + return this; + } + + public static SQLiteMCWxAES256Config getDefault() { + SQLiteMCWxAES256Config config = new SQLiteMCWxAES256Config(); + config.setLegacy(0); + config.setLegacyPageSize(0); + config.setKdfIter(4001); + return config; + } } diff --git a/src/main/java/org/sqlite/util/OSInfo.java b/src/main/java/org/sqlite/util/OSInfo.java index c778638970..0f2aa19059 100644 --- a/src/main/java/org/sqlite/util/OSInfo.java +++ b/src/main/java/org/sqlite/util/OSInfo.java @@ -24,12 +24,13 @@ // -------------------------------------- package org.sqlite.util; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Locale; -import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; /** * Provides OS name and architecture name. @@ -37,7 +38,8 @@ * @author leo */ public class OSInfo { - private static HashMap archMapping = new HashMap(); + protected static ProcessRunner processRunner = new ProcessRunner(); + private static final HashMap archMapping = new HashMap<>(); public static final String X86 = "x86"; public static final String X86_64 = "x86_64"; @@ -110,50 +112,27 @@ public static boolean isAndroid() { return System.getProperty("java.runtime.name", "").toLowerCase().contains("android"); } - public static boolean isAlpine() { - try { - Process p = Runtime.getRuntime().exec("cat /etc/os-release | grep ^ID"); - p.waitFor(300, TimeUnit.MILLISECONDS); - - InputStream in = p.getInputStream(); - try { - int readLen = 0; - ByteArrayOutputStream b = new ByteArrayOutputStream(); - byte[] buf = new byte[32]; - while ((readLen = in.read(buf, 0, buf.length)) >= 0) { - b.write(buf, 0, readLen); - } - return b.toString().toLowerCase().contains("alpine"); - } finally { - if (in != null) { - in.close(); - } - } - - } catch (Throwable e) { - return false; + public static boolean isMusl() { + Path mapFilesDir = Paths.get("/proc/self/map_files"); + try (Stream dirStream = Files.list(mapFilesDir)) { + return dirStream + .map( + path -> { + try { + return path.toRealPath().toString(); + } catch (IOException e) { + return ""; + } + }) + .anyMatch(s -> s.toLowerCase().contains("musl")); + } catch (IOException ignored) { } + return false; } static String getHardwareName() { try { - Process p = Runtime.getRuntime().exec("uname -m"); - p.waitFor(); - - InputStream in = p.getInputStream(); - try { - int readLen = 0; - ByteArrayOutputStream b = new ByteArrayOutputStream(); - byte[] buf = new byte[32]; - while ((readLen = in.read(buf, 0, buf.length)) >= 0) { - b.write(buf, 0, readLen); - } - return b.toString(); - } finally { - if (in != null) { - in.close(); - } - } + return processRunner.runAndWaitFor("uname -m"); } catch (Throwable e) { System.err.println("Error while running uname -m: " + e.getMessage()); return "unknown"; @@ -165,6 +144,17 @@ static String resolveArmArchType() { String armType = getHardwareName(); // armType (uname -m) can be armv5t, armv5te, armv5tej, armv5tejl, armv6, armv7, armv7l, // aarch64, i686 + + // for Android, we fold everything that is not aarch64 into arm + if (isAndroid()) { + if (armType.startsWith("aarch64")) { + // Use arm64 + return "aarch64"; + } else { + return "arm"; + } + } + if (armType.startsWith("armv6")) { // Raspberry PI return "armv6"; @@ -206,12 +196,9 @@ static String resolveArmArchType() { } } else { System.err.println( - "WARNING! readelf not found. Cannot check if running on an armhf system, " - + "armel architecture will be presumed."); + "WARNING! readelf not found. Cannot check if running on an armhf system, armel architecture will be presumed."); } - } catch (IOException e) { - // ignored: fall back to "arm" arch (soft-float ABI) - } catch (InterruptedException e) { + } catch (IOException | InterruptedException e) { // ignored: fall back to "arm" arch (soft-float ABI) } } @@ -221,10 +208,6 @@ static String resolveArmArchType() { public static String getArchName() { String osArch = System.getProperty("os.arch"); - // For Android - if (isAndroid()) { - return "android-arm"; - } if (osArch.startsWith("arm")) { osArch = resolveArmArchType(); @@ -240,8 +223,10 @@ static String translateOSNameToFolderName(String osName) { return "Windows"; } else if (osName.contains("Mac") || osName.contains("Darwin")) { return "Mac"; - } else if (isAlpine()) { - return "Linux-Alpine"; + } else if (isMusl()) { + return "Linux-Musl"; + } else if (isAndroid()) { + return "Linux-Android"; } else if (osName.contains("Linux")) { return "Linux"; } else if (osName.contains("AIX")) { diff --git a/src/main/java/org/sqlite/util/ProcessRunner.java b/src/main/java/org/sqlite/util/ProcessRunner.java new file mode 100644 index 0000000000..977a80546d --- /dev/null +++ b/src/main/java/org/sqlite/util/ProcessRunner.java @@ -0,0 +1,35 @@ +package org.sqlite.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +public class ProcessRunner { + String runAndWaitFor(String command) throws IOException, InterruptedException { + Process p = Runtime.getRuntime().exec(command); + p.waitFor(); + + return getProcessOutput(p); + } + + String runAndWaitFor(String command, long timeout, TimeUnit unit) + throws IOException, InterruptedException { + Process p = Runtime.getRuntime().exec(command); + p.waitFor(timeout, unit); + + return getProcessOutput(p); + } + + static String getProcessOutput(Process process) throws IOException { + try (InputStream in = process.getInputStream()) { + int readLen; + ByteArrayOutputStream b = new ByteArrayOutputStream(); + byte[] buf = new byte[32]; + while ((readLen = in.read(buf, 0, buf.length)) >= 0) { + b.write(buf, 0, readLen); + } + return b.toString(); + } + } +} diff --git a/src/test/java/org/sqlite/ConnectionTest.java b/src/test/java/org/sqlite/ConnectionTest.java index 001a0accf6..4a8b78b52e 100644 --- a/src/test/java/org/sqlite/ConnectionTest.java +++ b/src/test/java/org/sqlite/ConnectionTest.java @@ -1,497 +1,503 @@ -package org.sqlite; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Properties; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import org.junit.jupiter.api.Test; -import org.sqlite.SQLiteConfig.JournalMode; -import org.sqlite.SQLiteConfig.Pragma; -import org.sqlite.SQLiteConfig.SynchronousMode; - -/** - * These tests check whether access to files is woring correctly and some Connection.close() cases. - */ -public class ConnectionTest { - - @Test - public void isValid() throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:sqlite:"); - assertTrue(conn.isValid(0)); - conn.close(); - assertFalse(conn.isValid(0)); - } - - @Test - public void executeUpdateOnClosedDB() throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:sqlite:"); - Statement stat = conn.createStatement(); - conn.close(); - - try { - stat.executeUpdate("create table A(id, name)"); - } catch (SQLException e) { - return; // successfully detect the operation on the closed DB - } - fail("should not reach here"); - } - - @Test - public void readOnly() throws SQLException { - - // set read only mode - SQLiteConfig config = new SQLiteConfig(); - config.setReadOnly(true); - - Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); - Statement stat = conn.createStatement(); - try { - assertTrue(conn.isReadOnly()); - // these updates must be forbidden in read-only mode - stat.executeUpdate("create table A(id, name)"); - stat.executeUpdate("insert into A values(1, 'leo')"); - - fail("read only flag is not properly set"); - } catch (SQLException e) { - // success - } finally { - stat.close(); - conn.close(); - } - - config.setReadOnly(true); // should be a no-op - - try { - conn.setReadOnly(false); - fail("should not change read only flag after opening connection"); - } catch (SQLException e) { - assert (e.getMessage() - .contains("Cannot change read-only flag after establishing a connection.")); - } finally { - conn.close(); - } - } - - @Test - public void foreignKeys() throws SQLException { - SQLiteConfig config = new SQLiteConfig(); - config.enforceForeignKeys(true); - Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); - Statement stat = conn.createStatement(); - - try { - stat.executeUpdate( - "create table track(id integer primary key, name, aid, foreign key (aid) references artist(id))"); - stat.executeUpdate("create table artist(id integer primary key, name)"); - - stat.executeUpdate("insert into artist values(10, 'leo')"); - stat.executeUpdate("insert into track values(1, 'first track', 10)"); // OK - - try { - stat.executeUpdate( - "insert into track values(2, 'second track', 3)"); // invalid reference - } catch (SQLException e) { - return; // successfully detect violation of foreign key constraints - } - fail("foreign key constraint must be enforced"); - } finally { - stat.close(); - conn.close(); - } - } - - @Test - public void canWrite() throws SQLException { - SQLiteConfig config = new SQLiteConfig(); - config.enforceForeignKeys(true); - Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); - Statement stat = conn.createStatement(); - - try { - assertFalse(conn.isReadOnly()); - } finally { - stat.close(); - conn.close(); - } - } - - @Test - public void synchronous() throws SQLException { - SQLiteConfig config = new SQLiteConfig(); - config.setSynchronous(SynchronousMode.OFF); - Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); - Statement stat = conn.createStatement(); - - try { - ResultSet rs = stat.executeQuery("pragma synchronous"); - if (rs.next()) { - ResultSetMetaData rm = rs.getMetaData(); - int i = rm.getColumnCount(); - int synchronous = rs.getInt(1); - assertEquals(0, synchronous); - } - - } finally { - stat.close(); - conn.close(); - } - } - - @Test - public void openMemory() throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:sqlite:"); - conn.close(); - } - - @Test - public void isClosed() throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:sqlite:"); - conn.close(); - assertTrue(conn.isClosed()); - } - - @Test - public void closeTest() throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:sqlite:"); - PreparedStatement prep = conn.prepareStatement("select null;"); - ResultSet rs = prep.executeQuery(); - conn.close(); - assertThrows(SQLException.class, prep::clearParameters); - } - - @Test - public void openInvalidLocation() { - assertThrows(SQLException.class, () -> DriverManager.getConnection("jdbc:sqlite:/")); - } - - @Test - public void openResource() throws Exception { - File testDB = copyToTemp("sample.db"); - assertTrue(testDB.exists()); - Connection conn = - DriverManager.getConnection( - String.format("jdbc:sqlite::resource:%s", testDB.toURI().toURL())); - Statement stat = conn.createStatement(); - ResultSet rs = stat.executeQuery("select * from coordinate"); - assertTrue(rs.next()); - rs.close(); - stat.close(); - conn.close(); - } - - @Test - public void openJARResource() throws Exception { - File testJAR = copyToTemp("testdb.jar"); - assertTrue(testJAR.exists()); - - Connection conn = - DriverManager.getConnection( - String.format( - "jdbc:sqlite::resource:jar:%s!/sample.db", - testJAR.toURI().toURL())); - Statement stat = conn.createStatement(); - ResultSet rs = stat.executeQuery("select * from coordinate"); - assertTrue(rs.next()); - rs.close(); - stat.close(); - conn.close(); - } - - @Test - public void openFile() throws Exception { - - File testDB = copyToTemp("sample.db"); - - assertTrue(testDB.exists()); - Connection conn = DriverManager.getConnection(String.format("jdbc:sqlite:%s", testDB)); - conn.close(); - } - - @Test - public void concurrentClose() throws SQLException, InterruptedException, ExecutionException { - final Connection conn = DriverManager.getConnection("jdbc:sqlite:"); - ResultSet[] rss = new ResultSet[512]; - for (int i = 0; i < rss.length; i++) { - rss[i] = conn.prepareStatement("select null;").executeQuery(); - } - ExecutorService finalizer = Executors.newSingleThreadExecutor(); - try { - ArrayList> futures = new ArrayList>(rss.length); - for (final ResultSet rs : rss) { - futures.add( - finalizer.submit( - new Callable() { - public Void call() throws Exception { - rs.close(); - return null; - } - })); - } - conn.close(); - for (Future f : futures) f.get(); - } finally { - finalizer.shutdown(); - } - } - - public static File copyToTemp(String fileName) throws IOException { - InputStream in = ConnectionTest.class.getResourceAsStream(fileName); - File dir = new File("target"); - if (!dir.exists()) { - dir.mkdirs(); - } - - File tmp = File.createTempFile(fileName, "", new File("target")); - tmp.deleteOnExit(); - FileOutputStream out = new FileOutputStream(tmp); - - byte[] buf = new byte[8192]; - for (int readBytes = 0; (readBytes = in.read(buf)) != -1; ) { - out.write(buf, 0, readBytes); - } - out.flush(); - out.close(); - in.close(); - - return tmp; - } - - @Test - public void URIFilenames() throws SQLException { - Connection conn1 = - DriverManager.getConnection("jdbc:sqlite:file:memdb1?mode=memory&cache=shared"); - Statement stmt1 = conn1.createStatement(); - stmt1.executeUpdate("create table tbl (col int)"); - stmt1.executeUpdate("insert into tbl values(100)"); - stmt1.close(); - - Connection conn2 = - DriverManager.getConnection("jdbc:sqlite:file:memdb1?mode=memory&cache=shared"); - Statement stmt2 = conn2.createStatement(); - ResultSet rs = stmt2.executeQuery("select * from tbl"); - assertTrue(rs.next()); - assertEquals(100, rs.getInt(1)); - stmt2.close(); - - Connection conn3 = DriverManager.getConnection("jdbc:sqlite:file::memory:?cache=shared"); - Statement stmt3 = conn3.createStatement(); - stmt3.executeUpdate("attach 'file:memdb1?mode=memory&cache=shared' as memdb1"); - rs = stmt3.executeQuery("select * from memdb1.tbl"); - assertTrue(rs.next()); - assertEquals(100, rs.getInt(1)); - stmt3.executeUpdate("create table tbl2(col int)"); - stmt3.executeUpdate("insert into tbl2 values(200)"); - stmt3.close(); - - Connection conn4 = DriverManager.getConnection("jdbc:sqlite:file::memory:?cache=shared"); - Statement stmt4 = conn4.createStatement(); - rs = stmt4.executeQuery("select * from tbl2"); - assertTrue(rs.next()); - assertEquals(200, rs.getInt(1)); - rs.close(); - stmt4.close(); - conn4.close(); - } - - @Test - public void setPragmasFromURI() throws Exception { - File testDB = copyToTemp("sample.db"); - - assertTrue(testDB.exists()); - Connection conn = - DriverManager.getConnection( - String.format( - "jdbc:sqlite:%s?journal_mode=WAL&synchronous=OFF&journal_size_limit=500", - testDB)); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma journal_mode"); - assertEquals("wal", rs.getString(1)); - rs.close(); - - rs = stat.executeQuery("pragma synchronous"); - assertEquals(false, rs.getBoolean(1)); - rs.close(); - - rs = stat.executeQuery("pragma journal_size_limit"); - assertEquals(500, rs.getInt(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void limits() throws Exception { - File testDB = copyToTemp("sample.db"); - - assertTrue(testDB.exists()); - Connection conn = - DriverManager.getConnection( - String.format("jdbc:sqlite:%s?limit_attached=0", testDB)); - Statement stat = conn.createStatement(); - - assertThrows( - SQLException.class, - () -> stat.executeUpdate("ATTACH DATABASE attach_test.db AS attachDb")); - - stat.close(); - } - - @Test - public void ignoreUnknownParametersInURI() throws Exception { - Connection conn = - DriverManager.getConnection( - "jdbc:sqlite:file::memory:?cache=shared&foreign_keys=ON&debug=&invalid"); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma foreign_keys"); - assertEquals(true, rs.getBoolean(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void errorOnEmptyPragmaValueInURI() { - assertThrows( - SQLException.class, - () -> - DriverManager.getConnection( - "jdbc:sqlite:file::memory:?journal_mode=&synchronous=")); - } - - @Test - public void ignoreDoubleAmpersandsInURI() throws Exception { - File testDB = copyToTemp("sample.db"); - - assertTrue(testDB.exists()); - Connection conn = - DriverManager.getConnection( - String.format( - "jdbc:sqlite:%s?synchronous=OFF&&&&journal_mode=WAL", testDB)); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma journal_mode"); - assertEquals("wal", rs.getString(1)); - rs.close(); - - rs = stat.executeQuery("pragma synchronous"); - assertEquals(false, rs.getBoolean(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void useLastSpecifiedPragmaValueInURI() throws Exception { - File testDB = copyToTemp("sample.db"); - - assertTrue(testDB.exists()); - Connection conn = - DriverManager.getConnection( - String.format( - "jdbc:sqlite:%s?journal_mode=WAL&journal_mode=MEMORY&journal_mode=TRUNCATE", - testDB)); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma journal_mode"); - assertEquals("truncate", rs.getString(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void overrideURIPragmaValuesWithProperties() throws Exception { - File testDB = copyToTemp("sample.db"); - - assertTrue(testDB.exists()); - Properties props = new Properties(); - props.setProperty(Pragma.JOURNAL_MODE.pragmaName, JournalMode.TRUNCATE.name()); - Connection conn = - DriverManager.getConnection( - String.format("jdbc:sqlite:%s?journal_mode=WAL", testDB), props); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma journal_mode"); - assertEquals("truncate", rs.getString(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void autoVacuumProps() throws Exception { - File testDB = File.createTempFile("test.db", "", new File("target")); - testDB.deleteOnExit(); - - Properties props = new Properties(); - props.setProperty(SQLiteConfig.Pragma.AUTO_VACUUM.pragmaName, SQLiteConfig.AutoVacuum.INCREMENTAL.name()); - Connection conn = DriverManager.getConnection(String.format("jdbc:sqlite:%s", testDB), props); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma auto_vacuum"); - assertEquals("2", rs.getString(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void autoVacuumUri() throws Exception { - File testDB = File.createTempFile("test.db", "", new File("target")); - testDB.deleteOnExit(); - - Connection conn = DriverManager.getConnection(String.format("jdbc:sqlite:%s?auto_vacuum=1", testDB)); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma auto_vacuum"); - assertEquals("1", rs.getString(1)); - rs.close(); - - stat.close(); - conn.close(); - } - - @Test - public void autoVacuumUri2() throws Exception { - File testDB = File.createTempFile("test.db", "", new File("target")); - testDB.deleteOnExit(); - - Connection conn = DriverManager.getConnection(String.format("jdbc:sqlite:%s?auto_vacuum=INCREMENTAL", testDB)); - Statement stat = conn.createStatement(); - - ResultSet rs = stat.executeQuery("pragma auto_vacuum"); - assertEquals("2", rs.getString(1)); - rs.close(); - - stat.close(); - conn.close(); - } -} +package org.sqlite; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import org.junit.jupiter.api.Test; +import org.sqlite.SQLiteConfig.JournalMode; +import org.sqlite.SQLiteConfig.Pragma; +import org.sqlite.SQLiteConfig.SynchronousMode; + +/** + * These tests check whether access to files is woring correctly and some Connection.close() cases. + */ +public class ConnectionTest { + + @Test + public void isValid() throws SQLException { + Connection conn = DriverManager.getConnection("jdbc:sqlite:"); + assertTrue(conn.isValid(0)); + conn.close(); + assertFalse(conn.isValid(0)); + } + + @Test + public void executeUpdateOnClosedDB() throws SQLException { + Connection conn = DriverManager.getConnection("jdbc:sqlite:"); + Statement stat = conn.createStatement(); + conn.close(); + + try { + stat.executeUpdate("create table A(id, name)"); + } catch (SQLException e) { + return; // successfully detect the operation on the closed DB + } + fail("should not reach here"); + } + + @Test + public void readOnly() throws SQLException { + + // set read only mode + SQLiteConfig config = new SQLiteConfig(); + config.setReadOnly(true); + + Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + Statement stat = conn.createStatement(); + try { + assertTrue(conn.isReadOnly()); + // these updates must be forbidden in read-only mode + stat.executeUpdate("create table A(id, name)"); + stat.executeUpdate("insert into A values(1, 'leo')"); + + fail("read only flag is not properly set"); + } catch (SQLException e) { + // success + } finally { + stat.close(); + conn.close(); + } + + config.setReadOnly(true); // should be a no-op + + try { + conn.setReadOnly(false); + fail("should not change read only flag after opening connection"); + } catch (SQLException e) { + assert (e.getMessage() + .contains("Cannot change read-only flag after establishing a connection.")); + } finally { + conn.close(); + } + } + + @Test + public void foreignKeys() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + config.enforceForeignKeys(true); + Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + Statement stat = conn.createStatement(); + + try { + stat.executeUpdate( + "create table track(id integer primary key, name, aid, foreign key (aid) references artist(id))"); + stat.executeUpdate("create table artist(id integer primary key, name)"); + + stat.executeUpdate("insert into artist values(10, 'leo')"); + stat.executeUpdate("insert into track values(1, 'first track', 10)"); // OK + + try { + stat.executeUpdate( + "insert into track values(2, 'second track', 3)"); // invalid reference + } catch (SQLException e) { + return; // successfully detect violation of foreign key constraints + } + fail("foreign key constraint must be enforced"); + } finally { + stat.close(); + conn.close(); + } + } + + @Test + public void canWrite() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + config.enforceForeignKeys(true); + Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + Statement stat = conn.createStatement(); + + try { + assertFalse(conn.isReadOnly()); + } finally { + stat.close(); + conn.close(); + } + } + + @Test + public void synchronous() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + config.setSynchronous(SynchronousMode.OFF); + Connection conn = DriverManager.getConnection("jdbc:sqlite:", config.toProperties()); + Statement stat = conn.createStatement(); + + try { + ResultSet rs = stat.executeQuery("pragma synchronous"); + if (rs.next()) { + ResultSetMetaData rm = rs.getMetaData(); + int i = rm.getColumnCount(); + int synchronous = rs.getInt(1); + assertEquals(0, synchronous); + } + + } finally { + stat.close(); + conn.close(); + } + } + + @Test + public void openMemory() throws SQLException { + Connection conn = DriverManager.getConnection("jdbc:sqlite:"); + conn.close(); + } + + @Test + public void isClosed() throws SQLException { + Connection conn = DriverManager.getConnection("jdbc:sqlite:"); + conn.close(); + assertTrue(conn.isClosed()); + } + + @Test + public void closeTest() throws SQLException { + Connection conn = DriverManager.getConnection("jdbc:sqlite:"); + PreparedStatement prep = conn.prepareStatement("select null;"); + ResultSet rs = prep.executeQuery(); + conn.close(); + assertThrows(SQLException.class, prep::clearParameters); + } + + @Test + public void openInvalidLocation() { + assertThrows(SQLException.class, () -> DriverManager.getConnection("jdbc:sqlite:/")); + } + + @Test + public void openResource() throws Exception { + File testDB = copyToTemp("sample.db"); + assertTrue(testDB.exists()); + Connection conn = + DriverManager.getConnection( + String.format("jdbc:sqlite::resource:%s", testDB.toURI().toURL())); + Statement stat = conn.createStatement(); + ResultSet rs = stat.executeQuery("select * from coordinate"); + assertTrue(rs.next()); + rs.close(); + stat.close(); + conn.close(); + } + + @Test + public void openJARResource() throws Exception { + File testJAR = copyToTemp("testdb.jar"); + assertTrue(testJAR.exists()); + + Connection conn = + DriverManager.getConnection( + String.format( + "jdbc:sqlite::resource:jar:%s!/sample.db", + testJAR.toURI().toURL())); + Statement stat = conn.createStatement(); + ResultSet rs = stat.executeQuery("select * from coordinate"); + assertTrue(rs.next()); + rs.close(); + stat.close(); + conn.close(); + } + + @Test + public void openFile() throws Exception { + + File testDB = copyToTemp("sample.db"); + + assertTrue(testDB.exists()); + Connection conn = DriverManager.getConnection(String.format("jdbc:sqlite:%s", testDB)); + conn.close(); + } + + @Test + public void concurrentClose() throws SQLException, InterruptedException, ExecutionException { + final Connection conn = DriverManager.getConnection("jdbc:sqlite:"); + ResultSet[] rss = new ResultSet[512]; + for (int i = 0; i < rss.length; i++) { + rss[i] = conn.prepareStatement("select null;").executeQuery(); + } + ExecutorService finalizer = Executors.newSingleThreadExecutor(); + try { + ArrayList> futures = new ArrayList>(rss.length); + for (final ResultSet rs : rss) { + futures.add( + finalizer.submit( + new Callable() { + public Void call() throws Exception { + rs.close(); + return null; + } + })); + } + conn.close(); + for (Future f : futures) f.get(); + } finally { + finalizer.shutdown(); + } + } + + public static File copyToTemp(String fileName) throws IOException { + InputStream in = ConnectionTest.class.getResourceAsStream(fileName); + File dir = new File("target"); + if (!dir.exists()) { + dir.mkdirs(); + } + + File tmp = File.createTempFile(fileName, "", new File("target")); + tmp.deleteOnExit(); + FileOutputStream out = new FileOutputStream(tmp); + + byte[] buf = new byte[8192]; + for (int readBytes = 0; (readBytes = in.read(buf)) != -1; ) { + out.write(buf, 0, readBytes); + } + out.flush(); + out.close(); + in.close(); + + return tmp; + } + + @Test + public void URIFilenames() throws SQLException { + Connection conn1 = + DriverManager.getConnection("jdbc:sqlite:file:memdb1?mode=memory&cache=shared"); + Statement stmt1 = conn1.createStatement(); + stmt1.executeUpdate("create table tbl (col int)"); + stmt1.executeUpdate("insert into tbl values(100)"); + stmt1.close(); + + Connection conn2 = + DriverManager.getConnection("jdbc:sqlite:file:memdb1?mode=memory&cache=shared"); + Statement stmt2 = conn2.createStatement(); + ResultSet rs = stmt2.executeQuery("select * from tbl"); + assertTrue(rs.next()); + assertEquals(100, rs.getInt(1)); + stmt2.close(); + + Connection conn3 = DriverManager.getConnection("jdbc:sqlite:file::memory:?cache=shared"); + Statement stmt3 = conn3.createStatement(); + stmt3.executeUpdate("attach 'file:memdb1?mode=memory&cache=shared' as memdb1"); + rs = stmt3.executeQuery("select * from memdb1.tbl"); + assertTrue(rs.next()); + assertEquals(100, rs.getInt(1)); + stmt3.executeUpdate("create table tbl2(col int)"); + stmt3.executeUpdate("insert into tbl2 values(200)"); + stmt3.close(); + + Connection conn4 = DriverManager.getConnection("jdbc:sqlite:file::memory:?cache=shared"); + Statement stmt4 = conn4.createStatement(); + rs = stmt4.executeQuery("select * from tbl2"); + assertTrue(rs.next()); + assertEquals(200, rs.getInt(1)); + rs.close(); + stmt4.close(); + conn4.close(); + } + + @Test + public void setPragmasFromURI() throws Exception { + File testDB = copyToTemp("sample.db"); + + assertTrue(testDB.exists()); + Connection conn = + DriverManager.getConnection( + String.format( + "jdbc:sqlite:%s?journal_mode=WAL&synchronous=OFF&journal_size_limit=500", + testDB)); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma journal_mode"); + assertEquals("wal", rs.getString(1)); + rs.close(); + + rs = stat.executeQuery("pragma synchronous"); + assertEquals(false, rs.getBoolean(1)); + rs.close(); + + rs = stat.executeQuery("pragma journal_size_limit"); + assertEquals(500, rs.getInt(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void limits() throws Exception { + File testDB = copyToTemp("sample.db"); + + assertTrue(testDB.exists()); + Connection conn = + DriverManager.getConnection( + String.format("jdbc:sqlite:%s?limit_attached=0", testDB)); + Statement stat = conn.createStatement(); + + assertThrows( + SQLException.class, + () -> stat.executeUpdate("ATTACH DATABASE attach_test.db AS attachDb")); + + stat.close(); + } + + @Test + public void ignoreUnknownParametersInURI() throws Exception { + Connection conn = + DriverManager.getConnection( + "jdbc:sqlite:file::memory:?cache=shared&foreign_keys=ON&debug=&invalid"); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma foreign_keys"); + assertEquals(true, rs.getBoolean(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void errorOnEmptyPragmaValueInURI() { + assertThrows( + SQLException.class, + () -> + DriverManager.getConnection( + "jdbc:sqlite:file::memory:?journal_mode=&synchronous=")); + } + + @Test + public void ignoreDoubleAmpersandsInURI() throws Exception { + File testDB = copyToTemp("sample.db"); + + assertTrue(testDB.exists()); + Connection conn = + DriverManager.getConnection( + String.format( + "jdbc:sqlite:%s?synchronous=OFF&&&&journal_mode=WAL", testDB)); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma journal_mode"); + assertEquals("wal", rs.getString(1)); + rs.close(); + + rs = stat.executeQuery("pragma synchronous"); + assertEquals(false, rs.getBoolean(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void useLastSpecifiedPragmaValueInURI() throws Exception { + File testDB = copyToTemp("sample.db"); + + assertTrue(testDB.exists()); + Connection conn = + DriverManager.getConnection( + String.format( + "jdbc:sqlite:%s?journal_mode=WAL&journal_mode=MEMORY&journal_mode=TRUNCATE", + testDB)); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma journal_mode"); + assertEquals("truncate", rs.getString(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void overrideURIPragmaValuesWithProperties() throws Exception { + File testDB = copyToTemp("sample.db"); + + assertTrue(testDB.exists()); + Properties props = new Properties(); + props.setProperty(Pragma.JOURNAL_MODE.pragmaName, JournalMode.TRUNCATE.name()); + Connection conn = + DriverManager.getConnection( + String.format("jdbc:sqlite:%s?journal_mode=WAL", testDB), props); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma journal_mode"); + assertEquals("truncate", rs.getString(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void autoVacuumProps() throws Exception { + File testDB = File.createTempFile("test.db", "", new File("target")); + testDB.deleteOnExit(); + + Properties props = new Properties(); + props.setProperty( + SQLiteConfig.Pragma.AUTO_VACUUM.pragmaName, + SQLiteConfig.AutoVacuum.INCREMENTAL.name()); + Connection conn = + DriverManager.getConnection(String.format("jdbc:sqlite:%s", testDB), props); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma auto_vacuum"); + assertEquals("2", rs.getString(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void autoVacuumUri() throws Exception { + File testDB = File.createTempFile("test.db", "", new File("target")); + testDB.deleteOnExit(); + + Connection conn = + DriverManager.getConnection(String.format("jdbc:sqlite:%s?auto_vacuum=1", testDB)); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma auto_vacuum"); + assertEquals("1", rs.getString(1)); + rs.close(); + + stat.close(); + conn.close(); + } + + @Test + public void autoVacuumUri2() throws Exception { + File testDB = File.createTempFile("test.db", "", new File("target")); + testDB.deleteOnExit(); + + Connection conn = + DriverManager.getConnection( + String.format("jdbc:sqlite:%s?auto_vacuum=INCREMENTAL", testDB)); + Statement stat = conn.createStatement(); + + ResultSet rs = stat.executeQuery("pragma auto_vacuum"); + assertEquals("2", rs.getString(1)); + rs.close(); + + stat.close(); + conn.close(); + } +} diff --git a/src/test/java/org/sqlite/ParametersTest.java b/src/test/java/org/sqlite/ParametersTest.java index 0a20c36d2d..1c31c9e5c1 100644 --- a/src/test/java/org/sqlite/ParametersTest.java +++ b/src/test/java/org/sqlite/ParametersTest.java @@ -1,11 +1,10 @@ package org.sqlite; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; import java.io.File; import java.sql.*; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class ParametersTest { @@ -33,8 +32,16 @@ public void testSqliteConfigViaStatements() throws Throwable { checkPragma(stat, "journal_mode", "truncate"); checkPragma(stat, "synchronous", "2"); checkPragma(stat, "cache_size", "-65536"); - assertFalse(((SQLiteConnection) stat.getConnection()).getDatabase().getConfig().isEnabledSharedCache()); - assertTrue(((SQLiteConnection) stat.getConnection()).getDatabase().getConfig().isEnabledSharedCacheConnection()); + assertFalse( + ((SQLiteConnection) stat.getConnection()) + .getDatabase() + .getConfig() + .isEnabledSharedCache()); + assertTrue( + ((SQLiteConnection) stat.getConnection()) + .getDatabase() + .getConfig() + .isEnabledSharedCacheConnection()); } } } @@ -44,7 +51,10 @@ public void testSqliteConfigViaURI() throws Throwable { File testDB = File.createTempFile("test.db", "", new File("target")); testDB.deleteOnExit(); - String uri = "jdbc:sqlite:file:" + testDB + "?cache=private&busy_timeout=1800000&auto_vacuum=2&journal_mode=truncate&synchronous=full&cache_size=-65536"; + String uri = + "jdbc:sqlite:file:" + + testDB + + "?cache=private&busy_timeout=1800000&auto_vacuum=2&journal_mode=truncate&synchronous=full&cache_size=-65536"; try (Connection connection = DriverManager.getConnection(uri)) { try (Statement stat = connection.createStatement()) { stat.execute("select 1 from sqlite_master"); @@ -54,8 +64,16 @@ public void testSqliteConfigViaURI() throws Throwable { checkPragma(stat, "journal_mode", "truncate"); checkPragma(stat, "synchronous", "2"); checkPragma(stat, "cache_size", "-65536"); - assertFalse(((SQLiteConnection) stat.getConnection()).getDatabase().getConfig().isEnabledSharedCache()); - assertFalse(((SQLiteConnection) stat.getConnection()).getDatabase().getConfig().isEnabledSharedCacheConnection()); + assertFalse( + ((SQLiteConnection) stat.getConnection()) + .getDatabase() + .getConfig() + .isEnabledSharedCache()); + assertFalse( + ((SQLiteConnection) stat.getConnection()) + .getDatabase() + .getConfig() + .isEnabledSharedCacheConnection()); } } } @@ -67,5 +85,4 @@ private void checkPragma(Statement stat, String key, String expectedValue) throw assertEquals(expectedValue, value); } } - } diff --git a/src/test/java/org/sqlite/SQLiteConfigTest.java b/src/test/java/org/sqlite/SQLiteConfigTest.java index 002f542af7..05104d25b0 100644 --- a/src/test/java/org/sqlite/SQLiteConfigTest.java +++ b/src/test/java/org/sqlite/SQLiteConfigTest.java @@ -1,42 +1,41 @@ -package org.sqlite; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.sql.SQLException; -import java.util.Properties; -import org.junit.jupiter.api.Test; - -public class SQLiteConfigTest { - - @Test - public void toProperites() throws SQLException { - SQLiteConfig config = new SQLiteConfig(); - - config.setReadOnly(true); - config.setDateStringFormat("yyyy/mm/dd"); - config.setDatePrecision("seconds"); - config.setDateClass("real"); - - Properties properties = config.toProperties(); - - assertEquals( - "yyyy/mm/dd", - properties.getProperty(SQLiteConfig.Pragma.DATE_STRING_FORMAT.getPragmaName())); - assertEquals( - SQLiteConfig.DatePrecision.SECONDS.name(), - properties.getProperty(SQLiteConfig.Pragma.DATE_PRECISION.getPragmaName())); - assertEquals( - SQLiteConfig.DateClass.REAL.name(), - properties.getProperty(SQLiteConfig.Pragma.DATE_CLASS.getPragmaName())); - } - - @Test - public void busyTimeout() { - SQLiteConfig config = new SQLiteConfig(); - - assertEquals(3000, config.getBusyTimeout()); - config.setBusyTimeout(1500); - assertEquals(1500, config.getBusyTimeout()); - } - -} +package org.sqlite; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.sql.SQLException; +import java.util.Properties; +import org.junit.jupiter.api.Test; + +public class SQLiteConfigTest { + + @Test + public void toProperites() throws SQLException { + SQLiteConfig config = new SQLiteConfig(); + + config.setReadOnly(true); + config.setDateStringFormat("yyyy/mm/dd"); + config.setDatePrecision("seconds"); + config.setDateClass("real"); + + Properties properties = config.toProperties(); + + assertEquals( + "yyyy/mm/dd", + properties.getProperty(SQLiteConfig.Pragma.DATE_STRING_FORMAT.getPragmaName())); + assertEquals( + SQLiteConfig.DatePrecision.SECONDS.name(), + properties.getProperty(SQLiteConfig.Pragma.DATE_PRECISION.getPragmaName())); + assertEquals( + SQLiteConfig.DateClass.REAL.name(), + properties.getProperty(SQLiteConfig.Pragma.DATE_CLASS.getPragmaName())); + } + + @Test + public void busyTimeout() { + SQLiteConfig config = new SQLiteConfig(); + + assertEquals(3000, config.getBusyTimeout()); + config.setBusyTimeout(1500); + assertEquals(1500, config.getBusyTimeout()); + } +} diff --git a/src/test/java/org/sqlite/SQLiteMCPragmaTest.java b/src/test/java/org/sqlite/SQLiteMCPragmaTest.java index da2ee67452..22c53d29e8 100644 --- a/src/test/java/org/sqlite/SQLiteMCPragmaTest.java +++ b/src/test/java/org/sqlite/SQLiteMCPragmaTest.java @@ -1,226 +1,258 @@ -package org.sqlite; - -import org.junit.jupiter.api.Test; -import org.sqlite.mc.*; - -import java.io.File; -import java.io.IOException; -import java.sql.*; - -import static org.junit.jupiter.api.Assertions.*; - -public class SQLiteMCPragmaTest { - - private static final String SQL_TABLE = "CREATE TABLE IF NOT EXISTS warehouses (" - + " id integer PRIMARY KEY," - + " name text NOT NULL," - + " capacity real" - + ");"; - - public String createFile() throws IOException { - File tmpFile = File.createTempFile("tmp-sqlite", ".db"); - tmpFile.deleteOnExit(); - return tmpFile.getAbsolutePath(); - } - - - public boolean databaseIsReadable(Connection connection) { - if (connection == null) - return false; - try { - Statement st = connection.createStatement(); - ResultSet resultSet = st.executeQuery("SELECT count(*) as nb FROM sqlite_master"); - resultSet.next(); - //System.out.println("The out is : " + resultSet.getString("nb")); - assertEquals("1", resultSet.getString("nb"), "When reading the database, the result should contain the number 1"); - return true; - } catch (SQLException e) { - //System.out.println(e.getMessage()); - return false; - } - } - - public void applySchema(Connection connection) throws SQLException { - Statement stmt = connection.createStatement(); - stmt.execute(SQL_TABLE); - } - - public void plainDatabaseCreate(String dbPath) throws SQLException { - Connection conn = DriverManager.getConnection("jdbc:sqlite:file:" + dbPath); - applySchema(conn); - conn.close(); - } - - public void cipherDatabaseCreate(SQLiteMCConfig config, String dbPath, String key) throws SQLException { - Connection connection = config.withKey(key).createConnection("jdbc:sqlite:file:" + dbPath); - applySchema(connection); - connection.close(); - } - - public Connection plainDatabaseOpen(String dbPath) throws SQLException { - return DriverManager.getConnection("jdbc:sqlite:file:" + dbPath); - } - - @Test - public void plainDatabaseTest() throws IOException, SQLException { - String path = createFile(); - // 1. Open + Write - plainDatabaseCreate(path); - - // 2. Ensure another Connection can read the databse written - Connection c = plainDatabaseOpen(path); - assertTrue(databaseIsReadable(c), "The plain database should be always readable"); - c.close(); - - } - - - public Connection cipherDatabaseOpen(SQLiteMCConfig config, String dbPath, String key) throws SQLException { - try { - return config.withKey(key).createConnection("jdbc:sqlite:file:" + dbPath); - } catch (SQLiteException e) { - return null; - } - - } - - public void genericDatabaseTest(SQLiteMCConfig config) throws IOException, SQLException { - String path = createFile(); - // 1. Open + Write + cipher with "Key1" key - String Key1 = "Key1"; - String Key2 = "Key2"; - - cipherDatabaseCreate(config, path, Key1); - - //2. Ensure db is readable with good Password - Connection c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1)); - c.close(); - - //3. Ensure db is not readable without the good password (Using Key2 as password) - c = cipherDatabaseOpen(config, path, Key2); - assertNull(c, String.format("2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", config.getClass().getSimpleName(), Key2, Key1)); - - //4. Rekey the database - c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), String.format("3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", config.getClass().getSimpleName(), Key2, Key1, Key2)); - c.createStatement().execute(String.format("PRAGMA rekey=%s", Key2)); - assertTrue(databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); - c.close(); - - //5. Should now be readable with Key2 - c = cipherDatabaseOpen(config, path, Key2); - assertTrue(databaseIsReadable(c), String.format("5. Should now be able to open the database with config %s and the new key '%s'", config.getClass().getSimpleName(), Key2)); - c.close(); - } - - @Test - public void chacha20DatabaseTest() throws SQLException, IOException { - genericDatabaseTest(SQLiteMCChacha20Config.getDefault()); - } - - @Test - public void aes128cbcDatabaseTest() throws IOException, SQLException { - genericDatabaseTest(SQLiteMCWxAES128Config.getDefault()); - } - - @Test - public void aes256cbcDatabaseTest() throws IOException, SQLException { - genericDatabaseTest(SQLiteMCWxAES256Config.getDefault()); - } - - - @Test - public void sqlCipherDatabaseTest() throws IOException, SQLException { - genericDatabaseTest(SQLiteMCSqlCipherConfig.getDefault()); - } - - @Test - public void RC4DatabaseTest() throws IOException, SQLException { - genericDatabaseTest(SQLiteMCRC4Config.getDefault()); - } - - @Test - public void defaultCihperDatabaseTest() throws IOException, SQLException { - genericDatabaseTest(new SQLiteMCConfig()); - } - - @Test - public void defaultCihperDatabaseWithSpecialKeyTest() throws IOException, SQLException { - SQLiteMCConfig config = new SQLiteMCConfig(); - String path = createFile(); - // 1. Open + Write + cipher with "Key1" key - String Key1 = "Key1&az=uies%63"; - String Key2 = "Key1"; - - cipherDatabaseCreate(config, path, Key1); - - //2. Ensure db is readable with good Password - Connection c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1)); - c.close(); - - //3. Ensure db is not readable without the good password (Using Key2 as password) - c = cipherDatabaseOpen(config, path, Key2); - assertNull(c, String.format("2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", config.getClass().getSimpleName(), Key2, Key1)); - - //4. Rekey the database - c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), String.format("3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", config.getClass().getSimpleName(), Key2, Key1, Key2)); - c.createStatement().execute(String.format("PRAGMA rekey=%s", Key2)); - assertTrue(databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); - c.close(); - - //5. Should now be readable with Key2 - c = cipherDatabaseOpen(config, path, Key2); - assertTrue(databaseIsReadable(c), String.format("5. Should now be able to open the database with config %s and the new key '%s'", config.getClass().getSimpleName(), Key2)); - c.close(); - } - - @Test - public void crossCipherAlgorithmTest() throws IOException, SQLException { - String dbfile = createFile(); - String key = "key"; - cipherDatabaseCreate(new SQLiteMCConfig(), dbfile, key); - - Connection c = cipherDatabaseOpen(new SQLiteMCConfig(), dbfile, key); - assertTrue(databaseIsReadable(c), "Crosstest : Should be able to read the base db"); - c.close(); - - c = cipherDatabaseOpen(SQLiteMCRC4Config.getDefault(), dbfile, key); - assertNull(c, "Should not be readable with RC4"); -// c.close(); - - c = cipherDatabaseOpen(SQLiteMCSqlCipherConfig.getDefault(), dbfile, key); - assertNull(c, "Should not be readable with SQLCipher"); -// c.close(); - - c = cipherDatabaseOpen(SQLiteMCWxAES128Config.getDefault(), dbfile, key); - assertNull(c, "Should not be readable with Wx128bit"); -// c.close(); - - c = cipherDatabaseOpen(SQLiteMCWxAES256Config.getDefault(), dbfile, key); - assertNull(c, "Should not be readable with Wx256"); -// c.close(); - - c = cipherDatabaseOpen(SQLiteMCChacha20Config.getDefault(), dbfile, key); - assertTrue(databaseIsReadable(c), "Should be readable with Chacha20 as it is default"); -// c.close(); - } - - @Test - public void closeDeleteTest() throws IOException, SQLException { - String dbfile = createFile(); - String key = "key"; - cipherDatabaseCreate(new SQLiteMCConfig(), dbfile, key); - - Connection c = cipherDatabaseOpen(new SQLiteMCConfig(), dbfile, key); - assertTrue(databaseIsReadable(c), "Should be able to read the base db"); - c.close(); - - c = cipherDatabaseOpen(SQLiteMCRC4Config.getDefault(), dbfile, key); - assertNull(c, "Should not be readable with RC4"); - assertTrue(new File(dbfile).delete(), "Connection must be closed, should be deleted"); - } - -} +package org.sqlite; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; +import java.sql.*; +import org.junit.jupiter.api.Test; +import org.sqlite.mc.*; + +public class SQLiteMCPragmaTest { + + private static final String SQL_TABLE = + "CREATE TABLE IF NOT EXISTS warehouses (" + + " id integer PRIMARY KEY," + + " name text NOT NULL," + + " capacity real" + + ");"; + + public String createFile() throws IOException { + File tmpFile = File.createTempFile("tmp-sqlite", ".db"); + tmpFile.deleteOnExit(); + return tmpFile.getAbsolutePath(); + } + + public boolean databaseIsReadable(Connection connection) { + if (connection == null) return false; + try { + Statement st = connection.createStatement(); + ResultSet resultSet = st.executeQuery("SELECT count(*) as nb FROM sqlite_master"); + resultSet.next(); + // System.out.println("The out is : " + resultSet.getString("nb")); + assertEquals( + "1", + resultSet.getString("nb"), + "When reading the database, the result should contain the number 1"); + return true; + } catch (SQLException e) { + // System.out.println(e.getMessage()); + return false; + } + } + + public void applySchema(Connection connection) throws SQLException { + Statement stmt = connection.createStatement(); + stmt.execute(SQL_TABLE); + } + + public void plainDatabaseCreate(String dbPath) throws SQLException { + Connection conn = DriverManager.getConnection("jdbc:sqlite:file:" + dbPath); + applySchema(conn); + conn.close(); + } + + public void cipherDatabaseCreate(SQLiteMCConfig config, String dbPath, String key) + throws SQLException { + Connection connection = config.withKey(key).createConnection("jdbc:sqlite:file:" + dbPath); + applySchema(connection); + connection.close(); + } + + public Connection plainDatabaseOpen(String dbPath) throws SQLException { + return DriverManager.getConnection("jdbc:sqlite:file:" + dbPath); + } + + @Test + public void plainDatabaseTest() throws IOException, SQLException { + String path = createFile(); + // 1. Open + Write + plainDatabaseCreate(path); + + // 2. Ensure another Connection can read the databse written + Connection c = plainDatabaseOpen(path); + assertTrue(databaseIsReadable(c), "The plain database should be always readable"); + c.close(); + } + + public Connection cipherDatabaseOpen(SQLiteMCConfig config, String dbPath, String key) + throws SQLException { + try { + return config.withKey(key).createConnection("jdbc:sqlite:file:" + dbPath); + } catch (SQLiteException e) { + return null; + } + } + + public void genericDatabaseTest(SQLiteMCConfig config) throws IOException, SQLException { + String path = createFile(); + // 1. Open + Write + cipher with "Key1" key + String Key1 = "Key1"; + String Key2 = "Key2"; + + cipherDatabaseCreate(config, path, Key1); + + // 2. Ensure db is readable with good Password + Connection c = cipherDatabaseOpen(config, path, Key1); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); + c.close(); + + // 3. Ensure db is not readable without the good password (Using Key2 as password) + c = cipherDatabaseOpen(config, path, Key2); + assertNull( + c, + String.format( + "2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", + config.getClass().getSimpleName(), Key2, Key1)); + + // 4. Rekey the database + c = cipherDatabaseOpen(config, path, Key1); + assertTrue( + databaseIsReadable(c), + String.format( + "3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", + config.getClass().getSimpleName(), Key2, Key1, Key2)); + c.createStatement().execute(String.format("PRAGMA rekey=%s", Key2)); + assertTrue( + databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); + c.close(); + + // 5. Should now be readable with Key2 + c = cipherDatabaseOpen(config, path, Key2); + assertTrue( + databaseIsReadable(c), + String.format( + "5. Should now be able to open the database with config %s and the new key '%s'", + config.getClass().getSimpleName(), Key2)); + c.close(); + } + + @Test + public void chacha20DatabaseTest() throws SQLException, IOException { + genericDatabaseTest(SQLiteMCChacha20Config.getDefault()); + } + + @Test + public void aes128cbcDatabaseTest() throws IOException, SQLException { + genericDatabaseTest(SQLiteMCWxAES128Config.getDefault()); + } + + @Test + public void aes256cbcDatabaseTest() throws IOException, SQLException { + genericDatabaseTest(SQLiteMCWxAES256Config.getDefault()); + } + + @Test + public void sqlCipherDatabaseTest() throws IOException, SQLException { + genericDatabaseTest(SQLiteMCSqlCipherConfig.getDefault()); + } + + @Test + public void RC4DatabaseTest() throws IOException, SQLException { + genericDatabaseTest(SQLiteMCRC4Config.getDefault()); + } + + @Test + public void defaultCihperDatabaseTest() throws IOException, SQLException { + genericDatabaseTest(new SQLiteMCConfig()); + } + + @Test + public void defaultCihperDatabaseWithSpecialKeyTest() throws IOException, SQLException { + SQLiteMCConfig config = new SQLiteMCConfig(); + String path = createFile(); + // 1. Open + Write + cipher with "Key1" key + String Key1 = "Key1&az=uies%63"; + String Key2 = "Key1"; + + cipherDatabaseCreate(config, path, Key1); + + // 2. Ensure db is readable with good Password + Connection c = cipherDatabaseOpen(config, path, Key1); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); + c.close(); + + // 3. Ensure db is not readable without the good password (Using Key2 as password) + c = cipherDatabaseOpen(config, path, Key2); + assertNull( + c, + String.format( + "2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", + config.getClass().getSimpleName(), Key2, Key1)); + + // 4. Rekey the database + c = cipherDatabaseOpen(config, path, Key1); + assertTrue( + databaseIsReadable(c), + String.format( + "3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", + config.getClass().getSimpleName(), Key2, Key1, Key2)); + c.createStatement().execute(String.format("PRAGMA rekey=%s", Key2)); + assertTrue( + databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); + c.close(); + + // 5. Should now be readable with Key2 + c = cipherDatabaseOpen(config, path, Key2); + assertTrue( + databaseIsReadable(c), + String.format( + "5. Should now be able to open the database with config %s and the new key '%s'", + config.getClass().getSimpleName(), Key2)); + c.close(); + } + + @Test + public void crossCipherAlgorithmTest() throws IOException, SQLException { + String dbfile = createFile(); + String key = "key"; + cipherDatabaseCreate(new SQLiteMCConfig(), dbfile, key); + + Connection c = cipherDatabaseOpen(new SQLiteMCConfig(), dbfile, key); + assertTrue(databaseIsReadable(c), "Crosstest : Should be able to read the base db"); + c.close(); + + c = cipherDatabaseOpen(SQLiteMCRC4Config.getDefault(), dbfile, key); + assertNull(c, "Should not be readable with RC4"); + // c.close(); + + c = cipherDatabaseOpen(SQLiteMCSqlCipherConfig.getDefault(), dbfile, key); + assertNull(c, "Should not be readable with SQLCipher"); + // c.close(); + + c = cipherDatabaseOpen(SQLiteMCWxAES128Config.getDefault(), dbfile, key); + assertNull(c, "Should not be readable with Wx128bit"); + // c.close(); + + c = cipherDatabaseOpen(SQLiteMCWxAES256Config.getDefault(), dbfile, key); + assertNull(c, "Should not be readable with Wx256"); + // c.close(); + + c = cipherDatabaseOpen(SQLiteMCChacha20Config.getDefault(), dbfile, key); + assertTrue(databaseIsReadable(c), "Should be readable with Chacha20 as it is default"); + // c.close(); + } + + @Test + public void closeDeleteTest() throws IOException, SQLException { + String dbfile = createFile(); + String key = "key"; + cipherDatabaseCreate(new SQLiteMCConfig(), dbfile, key); + + Connection c = cipherDatabaseOpen(new SQLiteMCConfig(), dbfile, key); + assertTrue(databaseIsReadable(c), "Should be able to read the base db"); + c.close(); + + c = cipherDatabaseOpen(SQLiteMCRC4Config.getDefault(), dbfile, key); + assertNull(c, "Should not be readable with RC4"); + assertTrue(new File(dbfile).delete(), "Connection must be closed, should be deleted"); + } +} diff --git a/src/test/java/org/sqlite/SQLiteMCSQLInterfaceTest.java b/src/test/java/org/sqlite/SQLiteMCSQLInterfaceTest.java index e513243ffe..f1191d9814 100644 --- a/src/test/java/org/sqlite/SQLiteMCSQLInterfaceTest.java +++ b/src/test/java/org/sqlite/SQLiteMCSQLInterfaceTest.java @@ -1,21 +1,21 @@ package org.sqlite; -import org.junit.jupiter.api.Test; -import org.sqlite.mc.*; +import static org.junit.jupiter.api.Assertions.*; import java.io.File; import java.io.IOException; import java.sql.*; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.sqlite.mc.*; public class SQLiteMCSQLInterfaceTest { - private static final String SQL_TABLE = "CREATE TABLE IF NOT EXISTS warehouses (" - + " id integer PRIMARY KEY," - + " name text NOT NULL," - + " capacity real" - + ");"; + private static final String SQL_TABLE = + "CREATE TABLE IF NOT EXISTS warehouses (" + + " id integer PRIMARY KEY," + + " name text NOT NULL," + + " capacity real" + + ");"; public String createFile() throws IOException { File tmpFile = File.createTempFile("tmp-sqlite", ".db"); @@ -23,19 +23,20 @@ public String createFile() throws IOException { return tmpFile.getAbsolutePath(); } - public boolean databaseIsReadable(Connection connection) { - if (connection == null) - return false; + if (connection == null) return false; try { Statement st = connection.createStatement(); ResultSet resultSet = st.executeQuery("SELECT count(*) as nb FROM sqlite_master"); resultSet.next(); - //System.out.println("The out is : " + resultSet.getString("nb")); - assertEquals("1", resultSet.getString("nb"), "When reading the database, the result should contain the number 1"); + // System.out.println("The out is : " + resultSet.getString("nb")); + assertEquals( + "1", + resultSet.getString("nb"), + "When reading the database, the result should contain the number 1"); return true; } catch (SQLException e) { - //System.out.println(e.getMessage()); + // System.out.println(e.getMessage()); return false; } } @@ -51,8 +52,12 @@ public void plainDatabaseCreate(String dbPath) throws IOException, SQLException conn.close(); } - public void cipherDatabaseCreate(SQLiteMCConfig config, String dbPath, String key) throws SQLException { - Connection connection = config.withKey(key).useSQLInterface(true).createConnection("jdbc:sqlite:file:" + dbPath); + public void cipherDatabaseCreate(SQLiteMCConfig config, String dbPath, String key) + throws SQLException { + Connection connection = + config.withKey(key) + .useSQLInterface(true) + .createConnection("jdbc:sqlite:file:" + dbPath); applySchema(connection); connection.close(); } @@ -71,17 +76,17 @@ public void plainDatabaseTest() throws IOException, SQLException { Connection c = plainDatabaseOpen(path); assertTrue(databaseIsReadable(c), "The plain database should be always readable"); c.close(); - } - - public Connection cipherDatabaseOpen(SQLiteMCConfig config, String dbPath, String key) throws SQLException { + public Connection cipherDatabaseOpen(SQLiteMCConfig config, String dbPath, String key) + throws SQLException { try { - return config.withKey(key).useSQLInterface(true).createConnection("jdbc:sqlite:file:" + dbPath); + return config.withKey(key) + .useSQLInterface(true) + .createConnection("jdbc:sqlite:file:" + dbPath); } catch (SQLiteException e) { return null; } - } public void genericDatabaseTest(SQLiteMCConfig config) throws IOException, SQLException { @@ -92,28 +97,42 @@ public void genericDatabaseTest(SQLiteMCConfig config) throws IOException, SQLEx cipherDatabaseCreate(config, path, Key1); - //2. Ensure db is readable with good Password + // 2. Ensure db is readable with good Password Connection c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), - String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1) - ); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); c.close(); - //3. Ensure db is not readable without the good password (Using Key2 as password) + // 3. Ensure db is not readable without the good password (Using Key2 as password) c = cipherDatabaseOpen(config, path, Key2); - assertNull(c, - String.format("2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", config.getClass().getSimpleName(), Key2, Key1)); + assertNull( + c, + String.format( + "2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", + config.getClass().getSimpleName(), Key2, Key1)); - //4. Rekey the database + // 4. Rekey the database c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), String.format("3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", config.getClass().getSimpleName(), Key2, Key1, Key2)); + assertTrue( + databaseIsReadable(c), + String.format( + "3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", + config.getClass().getSimpleName(), Key2, Key1, Key2)); c.createStatement().execute(String.format("PRAGMA rekey='%s'", Key2)); - assertTrue(databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); + assertTrue( + databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); c.close(); - //5. Should now be readable with Key2 + // 5. Should now be readable with Key2 c = cipherDatabaseOpen(config, path, Key2); - assertTrue(databaseIsReadable(c), String.format("5. Should now be able to open the database with config %s and the new key '%s'", config.getClass().getSimpleName(), Key2)); + assertTrue( + databaseIsReadable(c), + String.format( + "5. Should now be able to open the database with config %s and the new key '%s'", + config.getClass().getSimpleName(), Key2)); c.close(); } @@ -131,9 +150,13 @@ public void chacha20DatabaseHexKeyTest() throws SQLException, IOException { cipherDatabaseCreate(config, dbfile, Key1); - //2. Ensure db is readable with good Password + // 2. Ensure db is readable with good Password Connection c = cipherDatabaseOpen(config, dbfile, Key1); - assertTrue(databaseIsReadable(c), String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1)); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); c.close(); } @@ -145,12 +168,14 @@ public void sqlCipherDatabaseHexKeyTest() throws SQLException, IOException { String Key1 = "x'54686973206973206D792076657279207365637265742070617373776F72642E'"; cipherDatabaseCreate(config, dbfile, Key1); - //2. Ensure db is readable with good Password + // 2. Ensure db is readable with good Password Connection c = cipherDatabaseOpen(config, dbfile, Key1); - assertTrue(databaseIsReadable(c), - String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1)); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); c.close(); - } @Test @@ -163,7 +188,6 @@ public void aes256cbcDatabaseTest() throws IOException, SQLException { genericDatabaseTest(SQLiteMCWxAES256Config.getDefault()); } - @Test public void sqlCipherDatabaseTest() throws IOException, SQLException { genericDatabaseTest(SQLiteMCSqlCipherConfig.getDefault()); @@ -190,30 +214,42 @@ public void defaultCihperDatabaseWithSpecialKeyTest() throws IOException, SQLExc cipherDatabaseCreate(config, path, Key1); - //2. Ensure db is readable with good Password + // 2. Ensure db is readable with good Password Connection c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), - String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1) - ); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); c.close(); - //3. Ensure db is not readable without the good password (Using Key2 as password) + // 3. Ensure db is not readable without the good password (Using Key2 as password) c = cipherDatabaseOpen(config, path, Key2); - assertNull(c, - String.format("2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", config.getClass().getSimpleName(), Key2, Key1)); + assertNull( + c, + String.format( + "2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", + config.getClass().getSimpleName(), Key2, Key1)); - //4. Rekey the database + // 4. Rekey the database c = cipherDatabaseOpen(config, path, Key1); - assertTrue(databaseIsReadable(c), String.format("3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", config.getClass().getSimpleName(), Key2, Key1, Key2) - ); + assertTrue( + databaseIsReadable(c), + String.format( + "3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", + config.getClass().getSimpleName(), Key2, Key1, Key2)); c.createStatement().execute(String.format("PRAGMA rekey=%s", Key2)); - assertTrue(databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); + assertTrue( + databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); c.close(); - //5. Should now be readable with Key2 + // 5. Should now be readable with Key2 c = cipherDatabaseOpen(config, path, Key2); - assertTrue(databaseIsReadable(c), String.format("5. Should now be able to open the database with config %s and the new key '%s'", config.getClass().getSimpleName(), Key2) - ); + assertTrue( + databaseIsReadable(c), + String.format( + "5. Should now be able to open the database with config %s and the new key '%s'", + config.getClass().getSimpleName(), Key2)); c.close(); } @@ -229,24 +265,22 @@ public void crossCipherAlgorithmTest() throws IOException, SQLException { c = cipherDatabaseOpen(SQLiteMCRC4Config.getDefault(), dbfile, key); assertNull(c, "Should not be readable with RC4"); -// c.close(); + // c.close(); c = cipherDatabaseOpen(SQLiteMCSqlCipherConfig.getDefault(), dbfile, key); assertNull(c, "Should not be readable with SQLCipher"); -// c.close(); + // c.close(); c = cipherDatabaseOpen(SQLiteMCWxAES128Config.getDefault(), dbfile, key); assertNull(c, "Should not be readable with Wx128bit"); -// c.close(); + // c.close(); c = cipherDatabaseOpen(SQLiteMCWxAES256Config.getDefault(), dbfile, key); assertNull(c, "Should not be readable with Wx256"); -// c.close(); + // c.close(); c = cipherDatabaseOpen(SQLiteMCChacha20Config.getDefault(), dbfile, key); assertTrue(databaseIsReadable(c), "Should be readable with Chacha20 as it is default"); -// c.close(); + // c.close(); } - - } diff --git a/src/test/java/org/sqlite/SQLiteMCURIInterfaceTest.java b/src/test/java/org/sqlite/SQLiteMCURIInterfaceTest.java index 74ad1c65db..a2ea6e8f4d 100644 --- a/src/test/java/org/sqlite/SQLiteMCURIInterfaceTest.java +++ b/src/test/java/org/sqlite/SQLiteMCURIInterfaceTest.java @@ -1,23 +1,23 @@ package org.sqlite; -import org.junit.jupiter.api.Test; -import org.sqlite.mc.SQLiteMCConfig; -import org.sqlite.mc.SQLiteMCSqlCipherConfig; +import static org.junit.jupiter.api.Assertions.*; import java.io.File; import java.io.IOException; import java.net.URLEncoder; import java.sql.*; - -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.sqlite.mc.SQLiteMCConfig; +import org.sqlite.mc.SQLiteMCSqlCipherConfig; public class SQLiteMCURIInterfaceTest { - private static final String SQL_TABLE = "CREATE TABLE IF NOT EXISTS warehouses (" - + " id integer PRIMARY KEY," - + " name text NOT NULL," - + " capacity real" - + ");"; + private static final String SQL_TABLE = + "CREATE TABLE IF NOT EXISTS warehouses (" + + " id integer PRIMARY KEY," + + " name text NOT NULL," + + " capacity real" + + ");"; public String createFile() throws IOException { File tmpFile = File.createTempFile("tmp-sqlite", ".db"); @@ -25,19 +25,20 @@ public String createFile() throws IOException { return tmpFile.getAbsolutePath(); } - public boolean databaseIsReadable(Connection connection) { - if (connection == null) - return false; + if (connection == null) return false; try { Statement st = connection.createStatement(); ResultSet resultSet = st.executeQuery("SELECT count(*) as nb FROM sqlite_master"); resultSet.next(); - //System.out.println("The out is : " + resultSet.getString("nb")); - assertEquals("1", resultSet.getString("nb"), "When reading the database, the result should contain the number 1"); + // System.out.println("The out is : " + resultSet.getString("nb")); + assertEquals( + "1", + resultSet.getString("nb"), + "When reading the database, the result should contain the number 1"); return true; } catch (SQLException e) { - //System.out.println(e.getMessage()); + // System.out.println(e.getMessage()); return false; } } @@ -54,7 +55,12 @@ public void plainDatabaseCreate(String dbPath) throws SQLException { } public void cipherDatabaseCreate(String dbPath, String key) throws SQLException { - Connection connection = DriverManager.getConnection("jdbc:sqlite:file:" + dbPath + "?cipher=sqlcipher&legacy=1&kdf_iter=4000&key=" + key); + Connection connection = + DriverManager.getConnection( + "jdbc:sqlite:file:" + + dbPath + + "?cipher=sqlcipher&legacy=1&kdf_iter=4000&key=" + + key); applySchema(connection); connection.close(); } @@ -73,17 +79,18 @@ public void plainDatabaseTest() throws IOException, SQLException { Connection c = plainDatabaseOpen(path); assertTrue(databaseIsReadable(c), "The plain database should be always readable"); c.close(); - } - public Connection cipherDatabaseOpen(String dbPath, String key) throws SQLException { try { - return DriverManager.getConnection("jdbc:sqlite:file:" + dbPath + "?cipher=sqlcipher&legacy=1&kdf_iter=4000&key=" + key); + return DriverManager.getConnection( + "jdbc:sqlite:file:" + + dbPath + + "?cipher=sqlcipher&legacy=1&kdf_iter=4000&key=" + + key); } catch (SQLiteException e) { return null; } - } public void genericDatabaseTest(SQLiteMCConfig config) throws IOException, SQLException { @@ -94,29 +101,45 @@ public void genericDatabaseTest(SQLiteMCConfig config) throws IOException, SQLEx cipherDatabaseCreate(path, Key1); - //2. Ensure db is readable with good Password + // 2. Ensure db is readable with good Password Connection c = cipherDatabaseOpen(path, Key1); - assertTrue(databaseIsReadable(c), String.format("1. Be sure the database with config %s can be read with the key '%s'", config.getClass().getSimpleName(), Key1)); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + config.getClass().getSimpleName(), Key1)); c.close(); - //3. Ensure db is not readable without the good password (Using Key2 as password) + // 3. Ensure db is not readable without the good password (Using Key2 as password) c = cipherDatabaseOpen(path, Key2); - assertNull(c, String.format("2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", config.getClass().getSimpleName(), Key2, Key1)); + assertNull( + c, + String.format( + "2 Be sure the database with config %s cannot be read with the key '%s' (good key is %s)", + config.getClass().getSimpleName(), Key2, Key1)); - //4. Rekey the database + // 4. Rekey the database c = cipherDatabaseOpen(path, Key1); - assertTrue(databaseIsReadable(c), String.format("3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", config.getClass().getSimpleName(), Key2, Key1, Key2)); + assertTrue( + databaseIsReadable(c), + String.format( + "3. Be sure the database with config %s can be read before rekeying with the key '%s' (replacing %s with %s)", + config.getClass().getSimpleName(), Key2, Key1, Key2)); c.createStatement().execute(String.format("PRAGMA rekey=%s", Key2)); - assertTrue(databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); + assertTrue( + databaseIsReadable(c), "4. Be sure the database is still readable after rekeying"); c.close(); - //5. Should now be readable with Key2 + // 5. Should now be readable with Key2 c = cipherDatabaseOpen(path, Key2); - assertTrue(databaseIsReadable(c), String.format("5. Should now be able to open the database with config %s and the new key '%s'", config.getClass().getSimpleName(), Key2)); + assertTrue( + databaseIsReadable(c), + String.format( + "5. Should now be able to open the database with config %s and the new key '%s'", + config.getClass().getSimpleName(), Key2)); c.close(); } - @Test public void sqlCipherDatabaseHexKeyTest() throws SQLException, IOException { @@ -125,16 +148,23 @@ public void sqlCipherDatabaseHexKeyTest() throws SQLException, IOException { String Key2 = "x'66086973206973206D792076657279207365637265742070617373776F72642E'"; cipherDatabaseCreate(dbfile, Key1); - //2. Ensure db is readable with good Password + // 2. Ensure db is readable with good Password Connection c = cipherDatabaseOpen(dbfile, Key1); - assertTrue(databaseIsReadable(c), String.format("1. Be sure the database with config %s can be read with the key '%s'", "SQLCipher", Key1)); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + "SQLCipher", Key1)); c.close(); - //3. Ensure not readable with wrong key + // 3. Ensure not readable with wrong key Connection c2 = cipherDatabaseOpen(dbfile, Key2); - assertFalse(databaseIsReadable(c2), String.format("2. Be sure the database with config %s cannot be read with the key '%s'", "SQLCipher", Key2)); + assertFalse( + databaseIsReadable(c2), + String.format( + "2. Be sure the database with config %s cannot be read with the key '%s'", + "SQLCipher", Key2)); c.close(); - } @Test @@ -144,18 +174,37 @@ public void sqlCipherDatabaseSpecialKeyTest() throws SQLException, IOException { String Key2 = "Key2&2ax"; cipherDatabaseCreate(dbfile, Key1); - //2. Ensure db is readable with Key1 Password URL access + // 2. Ensure db is readable with Key1 Password URL access Connection c = cipherDatabaseOpen(dbfile, Key1); - assertTrue(databaseIsReadable(c), String.format("1. Be sure the database with config %s can be read with the key '%s'", "SQLCipher", Key1)); + assertTrue( + databaseIsReadable(c), + String.format( + "1. Be sure the database with config %s can be read with the key '%s'", + "SQLCipher", Key1)); c.close(); - //3. Make sure we can read the database using the SQL interface - c = new SQLiteMCSqlCipherConfig().setLegacy(1).setKdfIter(4000).withKey(Key2).createConnection("jdbc:sqlite:file:" + dbfile); - assertTrue(databaseIsReadable(c), "2. Be sure the database is readable using PRAGMA method and key containing special characters"); + // 3. Make sure we can read the database using the SQL interface + c = + new SQLiteMCSqlCipherConfig() + .setLegacy(1) + .setKdfIter(4000) + .withKey(Key2) + .createConnection("jdbc:sqlite:file:" + dbfile); + assertTrue( + databaseIsReadable(c), + "2. Be sure the database is readable using PRAGMA method and key containing special characters"); c.close(); - c = new SQLiteMCSqlCipherConfig().setLegacy(1).setKdfIter(4000).withKey(Key2).useSQLInterface(true).createConnection("jdbc:sqlite:file:" + dbfile); - assertTrue(databaseIsReadable(c), "3. Be sure the database is readable using SQL method and key containing special characters"); + c = + new SQLiteMCSqlCipherConfig() + .setLegacy(1) + .setKdfIter(4000) + .withKey(Key2) + .useSQLInterface(true) + .createConnection("jdbc:sqlite:file:" + dbfile); + assertTrue( + databaseIsReadable(c), + "3. Be sure the database is readable using SQL method and key containing special characters"); c.close(); } diff --git a/src/test/java/org/sqlite/util/OSInfoTest.java b/src/test/java/org/sqlite/util/OSInfoTest.java index ad6ddafa40..3e60291b70 100644 --- a/src/test/java/org/sqlite/util/OSInfoTest.java +++ b/src/test/java/org/sqlite/util/OSInfoTest.java @@ -10,11 +10,18 @@ package org.sqlite.util; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintStream; import java.util.logging.Logger; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetSystemProperty; public class OSInfoTest { private static final Logger logger = Logger.getLogger(OSInfoTest.class.getName()); @@ -95,4 +102,48 @@ public void testGetHardwareName() throws Exception { String hardware = OSInfo.getHardwareName(); logger.info("Hardware name: " + hardware); } + + // it's unlikely we run tests on an Android device + @Test + void testIsNotAndroid() { + assertFalse(OSInfo.isAndroid()); + } + + @Nested + @SetSystemProperty(key = "java.runtime.name", value = "Java for Android") + @SetSystemProperty(key = "os.name", value = "Linux for Android") + class Android { + @Test + public void testIsAndroid() { + assertTrue(OSInfo.isAndroid()); + } + + @Test + @SetSystemProperty(key = "os.arch", value = "arm") + public void testArmvNativePath() throws IOException, InterruptedException { + try { + ProcessRunner mockRunner = mock(ProcessRunner.class); + OSInfo.processRunner = mockRunner; + when(mockRunner.runAndWaitFor("uname -m")).thenReturn("armv7l"); + + assertEquals("Linux-Android/arm", OSInfo.getNativeLibFolderPathForCurrentOS()); + } finally { + OSInfo.processRunner = new ProcessRunner(); + } + } + + @Test + @SetSystemProperty(key = "os.arch", value = "arm64") + public void testArm64NativePath() throws IOException, InterruptedException { + try { + ProcessRunner mockRunner = mock(ProcessRunner.class); + OSInfo.processRunner = mockRunner; + when(mockRunner.runAndWaitFor("uname -m")).thenReturn("aarch64"); + + assertEquals("Linux-Android/aarch64", OSInfo.getNativeLibFolderPathForCurrentOS()); + } finally { + OSInfo.processRunner = new ProcessRunner(); + } + } + } }