Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad9533679a |
+2
-28
@@ -57,7 +57,6 @@ LIBTCL = @TCL_LIB_SPEC@
|
||||
#
|
||||
READLINE_FLAGS = -DHAVE_READLINE=@TARGET_HAVE_READLINE@ @TARGET_READLINE_INC@
|
||||
READLINE_FLAGS += -DHAVE_EDITLINE=@TARGET_HAVE_EDITLINE@
|
||||
READLINE_FLAGS += -DHAVE_LINENOISE=@TARGET_HAVE_LINENOISE@
|
||||
|
||||
# The library that programs using readline() must link against.
|
||||
#
|
||||
@@ -1292,17 +1291,11 @@ fulltestonly: $(TESTPROGS) fuzztest
|
||||
./testfixture$(TEXE) $(TOP)/test/full.test
|
||||
|
||||
# Fuzz testing
|
||||
#
|
||||
# WARNING: When the "fuzztest" target is run by the testrunner.tcl script,
|
||||
# it does not actually run this code. Instead, it schedules equivalent
|
||||
# commands. Therefore, if this target is updated, then code in
|
||||
# testrunner_data.tcl (search for "trd_fuzztest_data") must also be updated.
|
||||
#
|
||||
fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE)
|
||||
fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
|
||||
./fuzzcheck$(TEXE) $(FUZZDATA)
|
||||
./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
|
||||
|
||||
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE)
|
||||
valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db
|
||||
valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA)
|
||||
valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db
|
||||
|
||||
@@ -1321,12 +1314,6 @@ testrunner: testfixture$(TEXE)
|
||||
#
|
||||
devtest: testfixture$(TEXE) fuzztest testrunner
|
||||
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
|
||||
|
||||
sdevtest:
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture$(TEXE)
|
||||
@@ -1469,11 +1456,6 @@ amalgamation-tarball: sqlite3.c sqlite3rc.h
|
||||
snapshot-tarball: sqlite3.c sqlite3rc.h
|
||||
TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh --snapshot
|
||||
|
||||
# Build a ZIP archive containing various command-line tools.
|
||||
#
|
||||
tool-zip: testfixture sqlite3 sqldiff sqlite3_analyzer tool/mktoolzip.tcl
|
||||
./testfixture $(TOP)/tool/mktoolzip.tcl
|
||||
|
||||
# The next two rules are used to support the "threadtest" target. Building
|
||||
# threadtest runs a few thread-safety tests that are implemented in C. This
|
||||
# target is invoked by the releasetest.tcl script.
|
||||
@@ -1531,7 +1513,6 @@ clean:
|
||||
rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
|
||||
rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
|
||||
rm -f wordcount$(TEXE) changeset$(TEXE)
|
||||
rm -f version-info$(TEXT)
|
||||
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
|
||||
rm -f sqlite3.c
|
||||
rm -f sqlite3rc.h
|
||||
@@ -1595,10 +1576,3 @@ fiddle: sqlite3.c shell.c
|
||||
|
||||
misspell: ./custom.rws
|
||||
$(TCLSH_CMD) ./tool/spellsift.tcl ./src/*.c ./src/*.h ./src/*.in
|
||||
|
||||
#
|
||||
# tool/version-info: a utility for emitting sqlite3 version info
|
||||
# in various forms.
|
||||
#
|
||||
version-info$(TEXE): $(TOP)/tool/version-info.c Makefile sqlite3.h
|
||||
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/tool/version-info.c
|
||||
|
||||
@@ -22,6 +22,12 @@ TOP = ../sqlite
|
||||
BCC = gcc -g -O0
|
||||
#BCC = /opt/ancic/bin/c89 -0
|
||||
|
||||
#### If the target operating system supports the "usleep()" system
|
||||
# call, then define the HAVE_USLEEP macro for all C modules.
|
||||
#
|
||||
#USLEEP =
|
||||
USLEEP = -DHAVE_USLEEP=1
|
||||
|
||||
#### If you want the SQLite library to be safe for use within a
|
||||
# multi-threaded program, then define the following macro
|
||||
# appropriately:
|
||||
|
||||
@@ -52,13 +52,6 @@ MINIMAL_AMALGAMATION = 0
|
||||
USE_STDCALL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use structured exception handling (SEH) for WAL mode
|
||||
# in the core library.
|
||||
#
|
||||
!IFNDEF USE_SEH
|
||||
USE_SEH = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to have the shell executable link against the core dynamic
|
||||
# link library.
|
||||
#
|
||||
@@ -225,12 +218,6 @@ WIN32HEAP = 0
|
||||
OSTRACE = 0
|
||||
!ENDIF
|
||||
|
||||
# enable address sanitizer using ASAN=1 on the command-line.
|
||||
#
|
||||
!IFNDEF ASAN
|
||||
ASAN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this to one of the following values to enable various debugging
|
||||
# features. Each level includes the debugging options from the previous
|
||||
# levels. Currently, the recognized values for DEBUG are:
|
||||
@@ -402,13 +389,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
|
||||
!ENDIF
|
||||
|
||||
# Should structured exception handling (SEH) be enabled for WAL mode in
|
||||
# the core library?
|
||||
#
|
||||
!IF $(USE_SEH)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
# the Windows 10 platform.
|
||||
#
|
||||
@@ -897,13 +877,6 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
|
||||
# Address sanitizer if ASAN=1
|
||||
#
|
||||
!IF $(ASAN)>0
|
||||
TCC = $(TCC) /fsanitize=address
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
# The locations of the Tcl header and library files. Also, the library that
|
||||
# non-stubs enabled programs using Tcl must link against. These variables
|
||||
@@ -2466,9 +2439,6 @@ extensiontest: testfixture.exe testloadext.dll
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS)
|
||||
|
||||
tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe tool\mktoolzip.tcl
|
||||
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl
|
||||
|
||||
coretestprogs: $(TESTPROGS)
|
||||
|
||||
testprogs: coretestprogs srcck1.exe fuzzcheck.exe sessionfuzz.exe
|
||||
@@ -2521,9 +2491,6 @@ testrunner: testfixture.exe
|
||||
#
|
||||
devtest: testfixture.exe fuzztest testrunner
|
||||
|
||||
mdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl mdevtest
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture.exe fuzztest
|
||||
|
||||
@@ -41,10 +41,11 @@ email to drh at sqlite dot org.
|
||||
The SQLite source code is in the public domain. See
|
||||
<https://sqlite.org/copyright.html> for details.
|
||||
|
||||
Because SQLite is in the public domain, we do not normally accept pull
|
||||
requests, because if we did take a pull request, the changes in that
|
||||
pull request might carry a copyright and the SQLite source code would
|
||||
then no longer be fully in the public domain.
|
||||
Because SQLite is in the public domain,
|
||||
we cannot accept pull requests, because
|
||||
if we did accept a pull request, the changes in that pull request would
|
||||
carry a copyright and the SQLite source code would no longer be fully in
|
||||
the public domain.
|
||||
|
||||
## Obtaining The SQLite Source Code
|
||||
|
||||
@@ -103,9 +104,9 @@ For example:
|
||||
mkdir bld ;# Build will occur in a sibling directory
|
||||
cd bld ;# Change to the build directory
|
||||
../sqlite/configure ;# Run the configure script
|
||||
make ;# Builds the "sqlite3" command-line tool
|
||||
make ;# Run the makefile.
|
||||
make sqlite3.c ;# Build the "amalgamation" source file
|
||||
make devtest ;# Run some tests (requires Tcl)
|
||||
make test ;# Run some tests (requires Tcl)
|
||||
|
||||
See the makefile for additional targets.
|
||||
|
||||
@@ -115,33 +116,32 @@ script does not work out for you, there is a generic makefile named
|
||||
can copy and edit to suit your needs. Comments on the generic makefile
|
||||
show what changes are needed.
|
||||
|
||||
## Compiling for Windows Using MSVC
|
||||
## Using MSVC for Windows systems
|
||||
|
||||
On Windows, all applicable build products can be compiled with MSVC.
|
||||
You will also need a working installation of TCL.
|
||||
See the [compile-for-windows.md](doc/compile-for-windows.md) document for
|
||||
additional information about how to install MSVC and TCL and configure your
|
||||
build environment.
|
||||
First open the command prompt window associated with the desired compiler
|
||||
version (e.g. "Developer Command Prompt for VS2013"). Next, use NMAKE
|
||||
with the provided "Makefile.msc" to build one of the supported targets.
|
||||
|
||||
If you want to run tests, you need to let SQLite know the location of your
|
||||
TCL library, using a command like this:
|
||||
For example, from the parent directory of the source subtree named "sqlite":
|
||||
|
||||
set TCLDIR=c:\Tcl
|
||||
mkdir bld
|
||||
cd bld
|
||||
nmake /f ..\sqlite\Makefile.msc TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc sqlite3.c TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc sqlite3.dll TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc sqlite3.exe TOP=..\sqlite
|
||||
nmake /f ..\sqlite\Makefile.msc test TOP=..\sqlite
|
||||
|
||||
SQLite uses "tclsh.exe" as part of the build process, and so that utility
|
||||
program will need to be somewhere on your %PATH%. The finished SQLite library
|
||||
does not contain any TCL code, but it does use TCL to help with the build process
|
||||
and to run tests.
|
||||
There are several build options that can be set via the NMAKE command
|
||||
line. For example, to build for WinRT, simply add "FOR_WINRT=1" argument
|
||||
to the "sqlite3.dll" command line above. When debugging into the SQLite
|
||||
code, adding the "DEBUG=1" argument to one of the above command lines is
|
||||
recommended.
|
||||
|
||||
Build using Makefile.msc. Example:
|
||||
|
||||
nmake /f Makefile.msc
|
||||
nmake /f Makefile.msc sqlite3.c
|
||||
nmake /f Makefile.msc devtest
|
||||
nmake /f Makefile.msc releasetest
|
||||
|
||||
There are many other makefile targets. See comments in Makefile.msc for
|
||||
details.
|
||||
SQLite does not require [Tcl](http://www.tcl.tk/) to run, but a Tcl installation
|
||||
is required by the makefiles (including those for MSVC). SQLite contains
|
||||
a lot of generated code and Tcl is used to do much of that code generation.
|
||||
|
||||
## Source Code Tour
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ sqlite3_SOURCES = shell.c sqlite3.h
|
||||
EXTRA_sqlite3_SOURCES = sqlite3.c
|
||||
sqlite3_LDADD = @EXTRA_SHELL_OBJ@ @READLINE_LIBS@
|
||||
sqlite3_DEPENDENCIES = @EXTRA_SHELL_OBJ@
|
||||
sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_DQS=0 -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
||||
sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_ENABLE_STMTVTAB -DSQLITE_ENABLE_DBSTAT_VTAB $(SHELL_CFLAGS)
|
||||
|
||||
include_HEADERS = sqlite3.h sqlite3ext.h
|
||||
|
||||
|
||||
@@ -52,13 +52,6 @@ MINIMAL_AMALGAMATION = 0
|
||||
USE_STDCALL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use structured exception handling (SEH) for WAL mode
|
||||
# in the core library.
|
||||
#
|
||||
!IFNDEF USE_SEH
|
||||
USE_SEH = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to have the shell executable link against the core dynamic
|
||||
# link library.
|
||||
#
|
||||
@@ -187,12 +180,6 @@ WIN32HEAP = 0
|
||||
OSTRACE = 0
|
||||
!ENDIF
|
||||
|
||||
# enable address sanitizer using ASAN=1 on the command-line.
|
||||
#
|
||||
!IFNDEF ASAN
|
||||
ASAN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this to one of the following values to enable various debugging
|
||||
# features. Each level includes the debugging options from the previous
|
||||
# levels. Currently, the recognized values for DEBUG are:
|
||||
@@ -324,13 +311,6 @@ OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RBU=1
|
||||
!ENDIF
|
||||
|
||||
# Should structured exception handling (SEH) be enabled for WAL mode in
|
||||
# the core library?
|
||||
#
|
||||
!IF $(USE_SEH)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
# the Windows 10 platform.
|
||||
#
|
||||
@@ -738,13 +718,6 @@ RCC = $(RCC) -DSQLITE_WIN32_MALLOC_VALIDATE=1
|
||||
!ENDIF
|
||||
|
||||
|
||||
# Address sanitizer if ASAN=1
|
||||
#
|
||||
!IF $(ASAN)>0
|
||||
TCC = $(TCC) /fsanitize=address
|
||||
!ENDIF
|
||||
|
||||
|
||||
# Compiler options needed for programs that use the readline() library.
|
||||
#
|
||||
!IFNDEF READLINE_FLAGS
|
||||
|
||||
@@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
|
||||
# so that we create the export library with the dll.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
AC_INIT([sqlite],[3.44.0])
|
||||
AC_INIT([sqlite],[3.42.0])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.44.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.43.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.44.0'
|
||||
PACKAGE_STRING='sqlite 3.44.0'
|
||||
PACKAGE_VERSION='3.43.0'
|
||||
PACKAGE_STRING='sqlite 3.43.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -776,7 +776,6 @@ OPT_FEATURE_FLAGS
|
||||
HAVE_ZLIB
|
||||
USE_AMALGAMATION
|
||||
TARGET_DEBUG
|
||||
TARGET_HAVE_LINENOISE
|
||||
TARGET_HAVE_EDITLINE
|
||||
TARGET_HAVE_READLINE
|
||||
TARGET_READLINE_INC
|
||||
@@ -904,7 +903,6 @@ enable_editline
|
||||
enable_readline
|
||||
with_readline_lib
|
||||
with_readline_inc
|
||||
with_linenoise
|
||||
enable_debug
|
||||
enable_amalgamation
|
||||
enable_load_extension
|
||||
@@ -1472,7 +1470,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.44.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.43.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1537,7 +1535,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.44.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.43.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1589,7 +1587,6 @@ Optional Packages:
|
||||
(tclConfig.sh)
|
||||
--with-readline-lib specify readline library
|
||||
--with-readline-inc specify readline include paths
|
||||
--with-linenoise=DIR source directory for linenoise library
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
@@ -1668,7 +1665,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.44.0
|
||||
sqlite configure 3.43.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2087,7 +2084,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.44.0, which was
|
||||
It was created by sqlite $as_me 3.43.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3945,13 +3942,13 @@ if ${lt_cv_nm_interface+:} false; then :
|
||||
else
|
||||
lt_cv_nm_interface="BSD nm"
|
||||
echo "int some_variable = 0;" > conftest.$ac_ext
|
||||
(eval echo "\"\$as_me:3948: $ac_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:3945: $ac_compile\"" >&5)
|
||||
(eval "$ac_compile" 2>conftest.err)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3951: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval echo "\"\$as_me:3948: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
|
||||
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
|
||||
cat conftest.err >&5
|
||||
(eval echo "\"\$as_me:3954: output\"" >&5)
|
||||
(eval echo "\"\$as_me:3951: output\"" >&5)
|
||||
cat conftest.out >&5
|
||||
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
|
||||
lt_cv_nm_interface="MS dumpbin"
|
||||
@@ -5157,7 +5154,7 @@ ia64-*-hpux*)
|
||||
;;
|
||||
*-*-irix6*)
|
||||
# Find out which ABI we are using.
|
||||
echo '#line 5160 "configure"' > conftest.$ac_ext
|
||||
echo '#line 5157 "configure"' > conftest.$ac_ext
|
||||
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
|
||||
(eval $ac_compile) 2>&5
|
||||
ac_status=$?
|
||||
@@ -6682,11 +6679,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:6685: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:6682: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:6689: \$? = $ac_status" >&5
|
||||
echo "$as_me:6686: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7021,11 +7018,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7024: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7021: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>conftest.err)
|
||||
ac_status=$?
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:7028: \$? = $ac_status" >&5
|
||||
echo "$as_me:7025: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s "$ac_outfile"; then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
# So say no if there are warnings other than the usual output.
|
||||
@@ -7126,11 +7123,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7129: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7126: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7133: \$? = $ac_status" >&5
|
||||
echo "$as_me:7130: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -7181,11 +7178,11 @@ else
|
||||
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
|
||||
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
|
||||
-e 's:$: $lt_compiler_flag:'`
|
||||
(eval echo "\"\$as_me:7184: $lt_compile\"" >&5)
|
||||
(eval echo "\"\$as_me:7181: $lt_compile\"" >&5)
|
||||
(eval "$lt_compile" 2>out/conftest.err)
|
||||
ac_status=$?
|
||||
cat out/conftest.err >&5
|
||||
echo "$as_me:7188: \$? = $ac_status" >&5
|
||||
echo "$as_me:7185: \$? = $ac_status" >&5
|
||||
if (exit $ac_status) && test -s out/conftest2.$ac_objext
|
||||
then
|
||||
# The compiler can only warn and ignore the option if not recognized
|
||||
@@ -9561,7 +9558,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9564 "configure"
|
||||
#line 9561 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -9657,7 +9654,7 @@ else
|
||||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 9660 "configure"
|
||||
#line 9657 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
@@ -11248,27 +11245,6 @@ fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check whether --with-linenoise was given.
|
||||
if test "${with_linenoise+set}" = set; then :
|
||||
withval=$with_linenoise; with_linenoise=$withval
|
||||
else
|
||||
with_linenoise="no"
|
||||
fi
|
||||
|
||||
if test "x$with_linenoise" != "xno"; then
|
||||
TARGET_HAVE_READLINE=0
|
||||
TARGET_HAVE_EDITLINE=0
|
||||
TARGET_HAVE_LINENOISE=1
|
||||
TARGET_READLINE_INC="-I${with_linenoise}"
|
||||
TARGET_READLINE_LIBS="${with_linenoise}/linenoise.c"
|
||||
echo "using linenoise source code at ${with_linenoise}"
|
||||
else
|
||||
TARGET_HAVE_LINENOISE=0
|
||||
echo "not using linenoise"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11345,7 +11321,7 @@ fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build type" >&5
|
||||
$as_echo_n "checking build type... " >&6; }
|
||||
if test "${enable_debug}" = "yes" ; then
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug" >&5
|
||||
$as_echo "debug" >&6; }
|
||||
else
|
||||
@@ -12481,7 +12457,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.44.0, which was
|
||||
This file was extended by sqlite $as_me 3.43.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12547,7 +12523,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.44.0
|
||||
sqlite config.status 3.43.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+1
-18
@@ -598,28 +598,11 @@ if test x"$with_readline" != xno; then
|
||||
TARGET_HAVE_READLINE=1
|
||||
fi
|
||||
fi
|
||||
AC_ARG_WITH([linenoise],
|
||||
[AS_HELP_STRING([--with-linenoise=DIR],[source directory for linenoise library])],
|
||||
[with_linenoise=$withval],
|
||||
[with_linenoise="no"])
|
||||
if test "x$with_linenoise" != "xno"; then
|
||||
TARGET_HAVE_READLINE=0
|
||||
TARGET_HAVE_EDITLINE=0
|
||||
TARGET_HAVE_LINENOISE=1
|
||||
TARGET_READLINE_INC="-I${with_linenoise}"
|
||||
TARGET_READLINE_LIBS="${with_linenoise}/linenoise.c"
|
||||
echo "using linenoise source code at ${with_linenoise}"
|
||||
else
|
||||
TARGET_HAVE_LINENOISE=0
|
||||
echo "not using linenoise"
|
||||
fi
|
||||
|
||||
AC_SUBST(TARGET_READLINE_LIBS)
|
||||
AC_SUBST(TARGET_READLINE_INC)
|
||||
AC_SUBST(TARGET_HAVE_READLINE)
|
||||
AC_SUBST(TARGET_HAVE_EDITLINE)
|
||||
AC_SUBST(TARGET_HAVE_LINENOISE)
|
||||
|
||||
|
||||
##########
|
||||
# Figure out what C libraries are required to compile programs
|
||||
@@ -632,7 +615,7 @@ AC_SEARCH_LIBS(fdatasync, [rt])
|
||||
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[enable debugging & verbose explain]))
|
||||
AC_MSG_CHECKING([build type])
|
||||
if test "${enable_debug}" = "yes" ; then
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall"
|
||||
TARGET_DEBUG="-DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0"
|
||||
AC_MSG_RESULT([debug])
|
||||
else
|
||||
TARGET_DEBUG="-DNDEBUG"
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
# Notes On Compiling SQLite On Windows 11
|
||||
|
||||
Here are step-by-step instructions on how to build SQLite from
|
||||
canonical source on a new Windows 11 PC, as of 2023-08-16:
|
||||
|
||||
1. Install Microsoft Visual Studio. The free "community edition"
|
||||
will work fine. Do a standard install for C++ development.
|
||||
SQLite only needs the
|
||||
"cl" compiler and the "nmake" build tool.
|
||||
|
||||
2. Under the "Start" menu, find "All Apps" then go to "Visual Studio 20XX"
|
||||
and find "x64 Native Tools Command Prompt for VS 20XX". Pin that
|
||||
application to your task bar, as you will use it a lot. Bring up
|
||||
an instance of this command prompt and do all of the subsequent steps
|
||||
in that "x64 Native Tools" command prompt. (Or use "x86" if you want
|
||||
a 32-bit build.) The subsequent steps will not work in a vanilla
|
||||
DOS prompt. Nor will they work in PowerShell.
|
||||
|
||||
3. Install TCL development libraries. This note assumes that you wil
|
||||
install the TCL development libraries in the "`c:\Tcl`" directory.
|
||||
Make adjustments
|
||||
if you want TCL installed somewhere else. SQLite needs both the
|
||||
"tclsh.exe" command-line tool as part of the build process, and
|
||||
the "tcl86.lib" library in order to run tests. You will need
|
||||
TCL version 8.6 or later.
|
||||
<ol type="a">
|
||||
<li>Get the TCL source archive, perhaps from
|
||||
[https://www.tcl.tk/software/tcltk/download.html](https://www.tcl.tk/software/tcltk/download.html).
|
||||
<li>Untar or unzip the source archive. CD into the "win/" subfolder
|
||||
of the source tree.
|
||||
<li>Run: `nmake /f makefile.vc release`
|
||||
<li>Run: `nmake /f makefile.vc INSTALLDIR=c:\Tcl install`
|
||||
<li>CD to `c:\Tcl\lib`. In that subfolder make a copy of the
|
||||
"`tcl86t.lib`" file to the alternative name "`tcl86.lib`"
|
||||
(omitting the second 't'). Leave the copy in the same directory
|
||||
as the original.
|
||||
<li>CD to `c:\Tcl\bin`. Make a copy of the "`tclsh86t.exe`"
|
||||
file into "`tclsh.exe`" (without the "86t") in the same directory.
|
||||
<li>Add `c:\Tcl\bin` to your %PATH%. To do this, go to Settings
|
||||
and search for "path". Select "edit environment variables for
|
||||
your account" and modify your default PATH accordingly.
|
||||
You will need to close and reopen your command prompts after
|
||||
making this change.
|
||||
</ol>
|
||||
|
||||
4. Download the SQLite source tree and unpack it. CD into the
|
||||
toplevel directory of the source tree.
|
||||
|
||||
5. Set the TCLDIR environment variable to point to your TCL installation.
|
||||
Like this:
|
||||
<ul>
|
||||
<li> `set TCLDIR=c:\Tcl`
|
||||
</ul>
|
||||
|
||||
6. Run the "`Makefile.msc`" makefile with an appropriate target.
|
||||
Examples:
|
||||
<ul>
|
||||
<li> `nmake /f makefile.msc`
|
||||
<li> `nmake /f makefile.msc sqlite3.c`
|
||||
<li> `nmake /f makefile.msc devtest`
|
||||
<li> `nmake /f makefile.msc releasetest`
|
||||
</ul>
|
||||
|
||||
## 32-bit Builds
|
||||
|
||||
Doing a 32-bit build is just like doing a 64-bit build with the
|
||||
following minor changes:
|
||||
|
||||
1. Use the "x86 Native Tools Command Prompt" instead of
|
||||
"x64 Native Tools Command Prompt". "**x86**" instead of "**x64**".
|
||||
|
||||
2. Use a different installation directory for TCL.
|
||||
The recommended directory is `c:\tcl32`. Thus you end up
|
||||
with two TCL builds:
|
||||
<ul>
|
||||
<li> `c:\tcl` ← 64-bit (the default)
|
||||
<li> `c:\tcl32` ← 32-bit
|
||||
</ul>
|
||||
|
||||
3. Ensure that `c:\tcl32\bin` comes before `c:\tcl\bin` on
|
||||
your PATH environment variable. You can achieve this using
|
||||
a command like:
|
||||
<ul>
|
||||
<li> `set PATH=c:\tcl32\bin;%PATH%`
|
||||
</ul>
|
||||
+2
-2
@@ -1229,8 +1229,8 @@ first syntax error, of course, if there are no instances of the
|
||||
|
||||
<p>Lemon was originally written by Richard Hipp sometime in the late
|
||||
1980s on a Sun4 Workstation using K&R C.
|
||||
There was a companion LL(1) parser generator program named "Lime".
|
||||
The Lime source code has been lost.</p>
|
||||
There was a companion LL(1) parser generator program named "Lime", the
|
||||
source code to which as been lost.</p>
|
||||
|
||||
<p>The lemon.c source file was originally many separate files that were
|
||||
compiled together to generate the "lemon" executable. Sometime in the
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
|
||||
|
||||
# The testrunner.tcl Script
|
||||
|
||||
# 1. Overview
|
||||
|
||||
testrunner.tcl is a Tcl script used to run multiple SQLite tests using
|
||||
multiple jobs. It supports the following types of tests:
|
||||
|
||||
* Tcl test scripts.
|
||||
|
||||
* Tests run with [make] commands. Specifically, at time of writing,
|
||||
[make fuzztest], [make mptest], [make sourcetest] and [make threadtest].
|
||||
|
||||
testrunner.tcl pipes the output of all tests and builds run into log file
|
||||
**testrunner.log**, created in the cwd directory. Searching this file for
|
||||
"failed" is a good way to find the output of a failed test.
|
||||
|
||||
testrunner.tcl also populates SQLite database **testrunner.db**. This database
|
||||
contains details of all tests run, running and to be run. A useful query
|
||||
might be:
|
||||
|
||||
```
|
||||
SELECT * FROM script WHERE state='failed'
|
||||
```
|
||||
|
||||
Running the command:
|
||||
|
||||
```
|
||||
./testfixture $(TESTDIR)/testrunner.tcl status
|
||||
```
|
||||
|
||||
in the directory containing the testrunner.db database runs various queries
|
||||
to produce a succinct report on the state of a running testrunner.tcl script.
|
||||
Running:
|
||||
|
||||
```
|
||||
watch ./testfixture $(TESTDIR)/testrunner.tcl status
|
||||
```
|
||||
|
||||
in another terminal is a good way to keep an eye on a long running test.
|
||||
|
||||
Sometimes testrunner.tcl uses the [testfixture] binary that it is run with
|
||||
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
|
||||
other binaries in specific configurations to test (see "Source Tests").
|
||||
|
||||
# 2. Binary Tests
|
||||
|
||||
The commands described in this section all run various combinations of the Tcl
|
||||
test scripts using the [testfixture] binary used to run the testrunner.tcl
|
||||
script (i.e. they do not invoke the compiler to build new binaries, or the
|
||||
[make] command to run tests that are not Tcl scripts). The procedure to run
|
||||
these tests is therefore:
|
||||
|
||||
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
|
||||
whatever method seems convenient.
|
||||
|
||||
2. Test the binary built in step 1 by running testrunner.tcl with it,
|
||||
perhaps with various options.
|
||||
|
||||
The following sub-sections describe the various options that can be
|
||||
passed to testrunner.tcl to test binary testfixture builds.
|
||||
|
||||
## 2.1. Organization of Tcl Tests
|
||||
|
||||
Tcl tests are stored in files that match the pattern *\*.test*. They are
|
||||
found in both the $TOP/test/ directory, and in the various sub-directories
|
||||
of the $TOP/ext/ directory of the source tree. Not all *\*.test* files
|
||||
contain Tcl tests - a handful are Tcl scripts designed to invoke other
|
||||
*\*.test* files.
|
||||
|
||||
The **veryquick** set of tests is a subset of all Tcl test scripts in the
|
||||
source tree. In includes most tests, but excludes some that are very slow.
|
||||
Almost all fault-injection tests (those that test the response of the library
|
||||
to OOM or IO errors) are excluded. It is defined in source file
|
||||
*test/permutations.test*.
|
||||
|
||||
The **full** set of tests includes all Tcl test scripts in the source tree.
|
||||
To run a "full" test is to run all Tcl test scripts that can be found in the
|
||||
source tree.
|
||||
|
||||
File *permutations.test* defines various test "permutations". A permutation
|
||||
consists of:
|
||||
|
||||
* A subset of Tcl test scripts, and
|
||||
|
||||
* Runtime configuration to apply before running each test script
|
||||
(e.g. enabling auto-vacuum, or disable lookaside).
|
||||
|
||||
Running **all** tests is to run all tests in the full test set, plus a dozen
|
||||
or so permutations. The specific permutations that are run as part of "all"
|
||||
are defined in file *testrunner_data.tcl*.
|
||||
|
||||
## 2.2. Commands to Run Tests
|
||||
|
||||
To run the "veryquick" test set, use either of the following:
|
||||
|
||||
```
|
||||
./testfixture $TESTDIR/testrunner.tcl
|
||||
./testfixture $TESTDIR/testrunner.tcl veryquick
|
||||
```
|
||||
|
||||
To run the "full" test suite:
|
||||
|
||||
```
|
||||
./testfixture $TESTDIR/testrunner.tcl full
|
||||
```
|
||||
|
||||
To run the subset of the "full" test suite for which the test file name matches
|
||||
a specified pattern (e.g. all tests that start with "fts5"), either of:
|
||||
|
||||
```
|
||||
./testfixture $TESTDIR/testrunner.tcl fts5%
|
||||
./testfixture $TESTDIR/testrunner.tcl 'fts5*'
|
||||
```
|
||||
|
||||
To run "all" tests (full + permutations):
|
||||
|
||||
```
|
||||
./testfixture $TESTDIR/testrunner.tcl all
|
||||
```
|
||||
|
||||
<a name=binary_test_failures></a>
|
||||
## 2.3. Investigating Binary Test Failures
|
||||
|
||||
If a test fails, testrunner.tcl reports name of the Tcl test script and, if
|
||||
applicable, the name of the permutation, to stdout. This information can also
|
||||
be retrieved from either *testrunner.log* or *testrunner.db*.
|
||||
|
||||
If there is no permutation, the individual test script may be run with:
|
||||
|
||||
```
|
||||
./testfixture $PATH_TO_SCRIPT
|
||||
```
|
||||
|
||||
Or, if the failure occured as part of a permutation:
|
||||
|
||||
```
|
||||
./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT
|
||||
```
|
||||
|
||||
TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT?
|
||||
|
||||
# 3. Source Code Tests
|
||||
|
||||
The commands described in this section invoke the C compiler to build
|
||||
binaries from the source tree, then use those binaries to run Tcl and
|
||||
other tests. The advantages of this are that:
|
||||
|
||||
* it is possible to test multiple build configurations with a single
|
||||
command, and
|
||||
|
||||
* it ensures that tests are always run using binaries created with the
|
||||
same set of compiler options.
|
||||
|
||||
The testrunner.tcl commands described in this section may be run using
|
||||
either a *testfixture* (or testfixture.exe) build, or with any other Tcl
|
||||
shell that supports SQLite 3.31.1 or newer via "package require sqlite3".
|
||||
|
||||
TODO: ./configure + Makefile.msc build systems.
|
||||
|
||||
## Commands to Run SQLite Tests
|
||||
|
||||
The **mdevtest** command is equivalent to running the veryquick tests and
|
||||
the [make fuzztest] target once for each of two --enable-all builds - one
|
||||
with debugging enabled and one without:
|
||||
|
||||
```
|
||||
tclsh $TESTDIR/testrunner.tcl mdevtest
|
||||
```
|
||||
|
||||
In other words, it is equivalent to running:
|
||||
|
||||
```
|
||||
$TOP/configure --enable-all --enable-debug
|
||||
make fuzztest
|
||||
make testfixture
|
||||
./testfixture $TOP/test/testrunner.tcl veryquick
|
||||
|
||||
# Then, after removing files created by the tests above:
|
||||
$TOP/configure --enable-all OPTS="-O0"
|
||||
make fuzztest
|
||||
make testfixture
|
||||
./testfixture $TOP/test/testrunner.tcl veryquick
|
||||
```
|
||||
|
||||
The **sdevtest** command is identical to the mdevtest command, except that the
|
||||
second of the two builds is a sanitizer build. Specifically, this means that
|
||||
OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0":
|
||||
|
||||
```
|
||||
tclsh $TESTDIR/testrunner.tcl sdevtest
|
||||
```
|
||||
|
||||
The **release** command runs lots of tests under lots of builds. It runs
|
||||
different combinations of builds and tests depending on whether it is run
|
||||
on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details
|
||||
of the specific tests run.
|
||||
|
||||
```
|
||||
tclsh $TESTDIR/testrunner.tcl release
|
||||
```
|
||||
|
||||
## Running ZipVFS Tests
|
||||
|
||||
testrunner.tcl can build a zipvfs-enabled testfixture and use it to run
|
||||
tests from the Zipvfs project with the following command:
|
||||
|
||||
```
|
||||
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS
|
||||
```
|
||||
|
||||
This can be combined with any of "mdevtest", "sdevtest" or "release" to
|
||||
test both SQLite and Zipvfs with a single command:
|
||||
|
||||
```
|
||||
tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest
|
||||
```
|
||||
|
||||
## Investigating Source Code Test Failures
|
||||
|
||||
Investigating a test failure that occurs during source code testing is a
|
||||
two step process:
|
||||
|
||||
1. Recreating the build configuration in which the test failed, and
|
||||
|
||||
2. Re-running the actual test.
|
||||
|
||||
To recreate a build configuration, use the testrunner.tcl **script** command
|
||||
to create a build script. A build script is a bash script on Linux or OSX, or
|
||||
a dos \*.bat file on windows. For example:
|
||||
|
||||
```
|
||||
# Create a script that recreates build configuration "Device-One" on
|
||||
# Linux or OSX:
|
||||
tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh
|
||||
|
||||
# Create a script that recreates build configuration "Have-Not" on Windows:
|
||||
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
|
||||
```
|
||||
|
||||
The generated bash or \*.bat file script accepts a single argument - a makefile
|
||||
target to build. This may be used either to run a [make] command test directly,
|
||||
or else to build a testfixture (or testfixture.exe) binary with which to
|
||||
run a Tcl test script, as <a href=#binary_test_failures>described above</a>.
|
||||
|
||||
|
||||
|
||||
# 4. Controlling CPU Core Utilization
|
||||
|
||||
When running either binary or source code tests, testrunner.tcl reports the
|
||||
number of jobs it intends to use to stdout. e.g.
|
||||
|
||||
```
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl
|
||||
splitting work across 16 jobs
|
||||
... more output ...
|
||||
```
|
||||
|
||||
By default, testfixture.tcl attempts to set the number of jobs to the number
|
||||
of real cores on the machine. This can be overridden using the "--jobs" (or -j)
|
||||
switch:
|
||||
|
||||
```
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl --jobs 8
|
||||
splitting work across 8 jobs
|
||||
... more output ...
|
||||
```
|
||||
|
||||
The number of jobs may also be changed while an instance of testrunner.tcl is
|
||||
running by exucuting the following command from the directory containing the
|
||||
testrunner.log and testrunner.db files:
|
||||
|
||||
```
|
||||
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-26
@@ -3980,32 +3980,8 @@ static int fts3ShadowName(const char *zName){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
|
||||
** table.
|
||||
*/
|
||||
static int fts3Integrity(sqlite3_vtab *pVtab, char **pzErr){
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
char *zSql;
|
||||
int rc;
|
||||
|
||||
zSql = sqlite3_mprintf(
|
||||
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
|
||||
p->zDb, p->zName, p->zName);
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
|
||||
p->bFts4 ? 4 : 3, p->zDb, p->zName);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const sqlite3_module fts3Module = {
|
||||
/* iVersion */ 4,
|
||||
/* iVersion */ 3,
|
||||
/* xCreate */ fts3CreateMethod,
|
||||
/* xConnect */ fts3ConnectMethod,
|
||||
/* xBestIndex */ fts3BestIndexMethod,
|
||||
@@ -4029,7 +4005,6 @@ static const sqlite3_module fts3Module = {
|
||||
/* xRelease */ fts3ReleaseMethod,
|
||||
/* xRollbackTo */ fts3RollbackToMethod,
|
||||
/* xShadowName */ fts3ShadowName,
|
||||
/* xIntegrity */ fts3Integrity,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -5218,7 +5218,7 @@ static u64 fts3ChecksumIndex(
|
||||
int rc;
|
||||
u64 cksum = 0;
|
||||
|
||||
if( *pRc ) return 0;
|
||||
assert( *pRc==SQLITE_OK );
|
||||
|
||||
memset(&filter, 0, sizeof(filter));
|
||||
memset(&csr, 0, sizeof(csr));
|
||||
|
||||
+6
-6
@@ -263,7 +263,7 @@ struct Fts5PhraseIter {
|
||||
** See xPhraseFirstColumn above.
|
||||
*/
|
||||
struct Fts5ExtensionApi {
|
||||
int iVersion; /* Currently always set to 2 */
|
||||
int iVersion; /* Currently always set to 3 */
|
||||
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
|
||||
@@ -492,8 +492,8 @@ struct Fts5ExtensionApi {
|
||||
** as separate queries of the FTS index are required for each synonym.
|
||||
**
|
||||
** When using methods (2) or (3), it is important that the tokenizer only
|
||||
** provide synonyms when tokenizing document text (method (3)) or query
|
||||
** text (method (2)), not both. Doing so will not cause any errors, but is
|
||||
** provide synonyms when tokenizing document text (method (2)) or query
|
||||
** text (method (3)), not both. Doing so will not cause any errors, but is
|
||||
** inefficient.
|
||||
*/
|
||||
typedef struct Fts5Tokenizer Fts5Tokenizer;
|
||||
@@ -541,7 +541,7 @@ struct fts5_api {
|
||||
int (*xCreateTokenizer)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pUserData,
|
||||
void *pContext,
|
||||
fts5_tokenizer *pTokenizer,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
@@ -550,7 +550,7 @@ struct fts5_api {
|
||||
int (*xFindTokenizer)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void **ppUserData,
|
||||
void **ppContext,
|
||||
fts5_tokenizer *pTokenizer
|
||||
);
|
||||
|
||||
@@ -558,7 +558,7 @@ struct fts5_api {
|
||||
int (*xCreateFunction)(
|
||||
fts5_api *pApi,
|
||||
const char *zName,
|
||||
void *pUserData,
|
||||
void *pContext,
|
||||
fts5_extension_function xFunction,
|
||||
void (*xDestroy)(void*)
|
||||
);
|
||||
|
||||
@@ -154,10 +154,6 @@ typedef struct Fts5Config Fts5Config;
|
||||
** attempt to merge together. A value of 1 sets the object to use the
|
||||
** compile time default. Zero disables auto-merge altogether.
|
||||
**
|
||||
** bContentlessDelete:
|
||||
** True if the contentless_delete option was present in the CREATE
|
||||
** VIRTUAL TABLE statement.
|
||||
**
|
||||
** zContent:
|
||||
**
|
||||
** zContentRowid:
|
||||
@@ -192,7 +188,6 @@ struct Fts5Config {
|
||||
int nPrefix; /* Number of prefix indexes */
|
||||
int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
|
||||
int eContent; /* An FTS5_CONTENT value */
|
||||
int bContentlessDelete; /* "contentless_delete=" option (dflt==0) */
|
||||
char *zContent; /* content table */
|
||||
char *zContentRowid; /* "content_rowid=" option value */
|
||||
int bColumnsize; /* "columnsize=" option value (dflt==1) */
|
||||
@@ -214,7 +209,6 @@ struct Fts5Config {
|
||||
char *zRank; /* Name of rank function */
|
||||
char *zRankArgs; /* Arguments to rank function */
|
||||
int bSecureDelete; /* 'secure-delete' */
|
||||
int nDeleteMerge; /* 'deletemerge' */
|
||||
|
||||
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
|
||||
char **pzErrmsg;
|
||||
@@ -537,9 +531,6 @@ int sqlite3Fts5IndexReset(Fts5Index *p);
|
||||
|
||||
int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
|
||||
|
||||
int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
|
||||
int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_index.c.
|
||||
**************************************************************************/
|
||||
@@ -624,11 +615,6 @@ int sqlite3Fts5HashWrite(
|
||||
*/
|
||||
void sqlite3Fts5HashClear(Fts5Hash*);
|
||||
|
||||
/*
|
||||
** Return true if the hash is empty, false otherwise.
|
||||
*/
|
||||
int sqlite3Fts5HashIsEmpty(Fts5Hash*);
|
||||
|
||||
int sqlite3Fts5HashQuery(
|
||||
Fts5Hash*, /* Hash table to query */
|
||||
int nPre,
|
||||
@@ -650,7 +636,6 @@ void sqlite3Fts5HashScanEntry(Fts5Hash *,
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_hash.c.
|
||||
**************************************************************************/
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#define FTS5_DEFAULT_CRISISMERGE 16
|
||||
#define FTS5_DEFAULT_HASHSIZE (1024*1024)
|
||||
|
||||
#define FTS5_DEFAULT_DELETE_AUTOMERGE 10 /* default 10% */
|
||||
|
||||
/* Maximum allowed page size */
|
||||
#define FTS5_MAX_PAGE_SIZE (64*1024)
|
||||
|
||||
@@ -354,16 +352,6 @@ static int fts5ConfigParseSpecial(
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("contentless_delete", zCmd, nCmd)==0 ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
|
||||
*pzErr = sqlite3_mprintf("malformed contentless_delete=... directive");
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pConfig->bContentlessDelete = (zArg[0]=='1');
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
|
||||
if( pConfig->zContentRowid ){
|
||||
*pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
|
||||
@@ -608,28 +596,6 @@ int sqlite3Fts5ConfigParse(
|
||||
sqlite3_free(zTwo);
|
||||
}
|
||||
|
||||
/* We only allow contentless_delete=1 if the table is indeed contentless. */
|
||||
if( rc==SQLITE_OK
|
||||
&& pRet->bContentlessDelete
|
||||
&& pRet->eContent!=FTS5_CONTENT_NONE
|
||||
){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"contentless_delete=1 requires a contentless table"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* We only allow contentless_delete=1 if columnsize=0 is not present.
|
||||
**
|
||||
** This restriction may be removed at some point.
|
||||
*/
|
||||
if( rc==SQLITE_OK && pRet->bContentlessDelete && pRet->bColumnsize==0 ){
|
||||
*pzErr = sqlite3_mprintf(
|
||||
"contentless_delete=1 is incompatible with columnsize=0"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* If a tokenizer= option was successfully parsed, the tokenizer has
|
||||
** already been allocated. Otherwise, allocate an instance of the default
|
||||
** tokenizer (unicode61) now. */
|
||||
@@ -924,18 +890,6 @@ int sqlite3Fts5ConfigSetValue(
|
||||
}
|
||||
}
|
||||
|
||||
else if( 0==sqlite3_stricmp(zKey, "deletemerge") ){
|
||||
int nVal = -1;
|
||||
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
|
||||
nVal = sqlite3_value_int(pVal);
|
||||
}else{
|
||||
*pbBadkey = 1;
|
||||
}
|
||||
if( nVal<0 ) nVal = FTS5_DEFAULT_DELETE_AUTOMERGE;
|
||||
if( nVal>100 ) nVal = 0;
|
||||
pConfig->nDeleteMerge = nVal;
|
||||
}
|
||||
|
||||
else if( 0==sqlite3_stricmp(zKey, "rank") ){
|
||||
const char *zIn = (const char*)sqlite3_value_text(pVal);
|
||||
char *zRank;
|
||||
@@ -984,7 +938,6 @@ int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
|
||||
pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
|
||||
pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
|
||||
pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
|
||||
pConfig->nDeleteMerge = FTS5_DEFAULT_DELETE_AUTOMERGE;
|
||||
|
||||
zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
|
||||
if( zSql ){
|
||||
|
||||
@@ -2477,7 +2477,7 @@ Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
|
||||
return pRet;
|
||||
}
|
||||
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
|
||||
#ifdef SQLITE_TEST
|
||||
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
|
||||
sqlite3_int64 nByte = 0;
|
||||
Fts5ExprTerm *p;
|
||||
@@ -2583,8 +2583,6 @@ static char *fts5ExprPrintTcl(
|
||||
if( zRet==0 ) return 0;
|
||||
}
|
||||
|
||||
}else if( pExpr->eType==0 ){
|
||||
zRet = sqlite3_mprintf("{}");
|
||||
}else{
|
||||
char const *zOp = 0;
|
||||
int i;
|
||||
@@ -2846,14 +2844,14 @@ static void fts5ExprFold(
|
||||
sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
|
||||
}
|
||||
}
|
||||
#endif /* if SQLITE_TEST || SQLITE_FTS5_DEBUG */
|
||||
#endif /* ifdef SQLITE_TEST */
|
||||
|
||||
/*
|
||||
** This is called during initialization to register the fts5_expr() scalar
|
||||
** UDF with the SQLite handle passed as the only argument.
|
||||
*/
|
||||
int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
|
||||
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
|
||||
#ifdef SQLITE_TEST
|
||||
struct Fts5ExprFunc {
|
||||
const char *z;
|
||||
void (*x)(sqlite3_context*,int,sqlite3_value**);
|
||||
|
||||
+1
-22
@@ -475,6 +475,7 @@ static int fts5HashEntrySort(
|
||||
pList = fts5HashEntryMerge(pList, ap[i]);
|
||||
}
|
||||
|
||||
pHash->nEntry = 0;
|
||||
sqlite3_free(ap);
|
||||
*ppSorted = pList;
|
||||
return SQLITE_OK;
|
||||
@@ -528,28 +529,6 @@ int sqlite3Fts5HashScanInit(
|
||||
return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
static int fts5HashCount(Fts5Hash *pHash){
|
||||
int nEntry = 0;
|
||||
int ii;
|
||||
for(ii=0; ii<pHash->nSlot; ii++){
|
||||
Fts5HashEntry *p = 0;
|
||||
for(p=pHash->aSlot[ii]; p; p=p->pHashNext){
|
||||
nEntry++;
|
||||
}
|
||||
}
|
||||
return nEntry;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Return true if the hash table is empty, false otherwise.
|
||||
*/
|
||||
int sqlite3Fts5HashIsEmpty(Fts5Hash *pHash){
|
||||
assert( pHash->nEntry==fts5HashCount(pHash) );
|
||||
return pHash->nEntry==0;
|
||||
}
|
||||
|
||||
void sqlite3Fts5HashScanNext(Fts5Hash *p){
|
||||
assert( !sqlite3Fts5HashScanEof(p) );
|
||||
p->pScan = p->pScan->pScanNext;
|
||||
|
||||
+300
-1336
File diff suppressed because it is too large
Load Diff
+10
-59
@@ -405,10 +405,6 @@ static int fts5InitVtab(
|
||||
pConfig->pzErrmsg = 0;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5FreeVtab(pTab);
|
||||
pTab = 0;
|
||||
@@ -1333,9 +1329,6 @@ static int fts5FilterMethod(
|
||||
pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
|
||||
}
|
||||
|
||||
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
|
||||
if( rc!=SQLITE_OK ) goto filter_out;
|
||||
|
||||
if( pTab->pSortCsr ){
|
||||
/* If pSortCsr is non-NULL, then this call is being made as part of
|
||||
** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
|
||||
@@ -1358,7 +1351,6 @@ static int fts5FilterMethod(
|
||||
pCsr->pExpr = pTab->pSortCsr->pExpr;
|
||||
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
||||
}else if( pCsr->pExpr ){
|
||||
assert( rc==SQLITE_OK );
|
||||
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( bOrderByRank ){
|
||||
@@ -1632,6 +1624,7 @@ static int fts5UpdateMethod(
|
||||
int eType0; /* value_type() of apVal[0] */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int bUpdateOrDelete = 0;
|
||||
|
||||
|
||||
/* A transaction must be open when this is called. */
|
||||
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
|
||||
@@ -1661,14 +1654,7 @@ static int fts5UpdateMethod(
|
||||
if( pConfig->eContent!=FTS5_CONTENT_NORMAL
|
||||
&& 0==sqlite3_stricmp("delete", z)
|
||||
){
|
||||
if( pConfig->bContentlessDelete ){
|
||||
fts5SetVtabError(pTab,
|
||||
"'delete' may not be used with a contentless_delete=1 table"
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = fts5SpecialDelete(pTab, apVal);
|
||||
}
|
||||
rc = fts5SpecialDelete(pTab, apVal);
|
||||
}else{
|
||||
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
|
||||
}
|
||||
@@ -1685,7 +1671,7 @@ static int fts5UpdateMethod(
|
||||
** Cases 3 and 4 may violate the rowid constraint.
|
||||
*/
|
||||
int eConflict = SQLITE_ABORT;
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
|
||||
}
|
||||
|
||||
@@ -1693,12 +1679,8 @@ static int fts5UpdateMethod(
|
||||
assert( nArg!=1 || eType0==SQLITE_INTEGER );
|
||||
|
||||
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
|
||||
** This is not suported. Except - they are both supported if the CREATE
|
||||
** VIRTUAL TABLE statement contained "contentless_delete=1". */
|
||||
if( eType0==SQLITE_INTEGER
|
||||
&& pConfig->eContent==FTS5_CONTENT_NONE
|
||||
&& pConfig->bContentlessDelete==0
|
||||
){
|
||||
** This is not suported. */
|
||||
if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
|
||||
pTab->p.base.zErrMsg = sqlite3_mprintf(
|
||||
"cannot %s contentless fts5 table: %s",
|
||||
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
|
||||
@@ -1722,8 +1704,7 @@ static int fts5UpdateMethod(
|
||||
}
|
||||
|
||||
else if( eType0!=SQLITE_INTEGER ){
|
||||
/* An INSERT statement. If the conflict-mode is REPLACE, first remove
|
||||
** the current entry (if any). */
|
||||
/* If this is a REPLACE, first remove the current entry (if any) */
|
||||
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
|
||||
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
|
||||
@@ -1786,7 +1767,8 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
|
||||
pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
|
||||
rc = sqlite3Fts5FlushToDisk(&pTab->p);
|
||||
fts5TripCursors(pTab);
|
||||
rc = sqlite3Fts5StorageSync(pTab->pStorage);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
return rc;
|
||||
}
|
||||
@@ -2553,12 +2535,6 @@ static int fts5ColumnMethod(
|
||||
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
|
||||
}
|
||||
pConfig->pzErrmsg = 0;
|
||||
}else if( pConfig->bContentlessDelete && sqlite3_vtab_nochange(pCtx) ){
|
||||
char *zErr = sqlite3_mprintf("cannot UPDATE a subset of "
|
||||
"columns on fts5 contentless-delete table: %s", pConfig->zName
|
||||
);
|
||||
sqlite3_result_error(pCtx, zErr, -1);
|
||||
sqlite3_free(zErr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -2859,33 +2835,9 @@ static int fts5ShadowName(const char *zName){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Run an integrity check on the FTS5 data structures. Return a string
|
||||
** if anything is found amiss. Return a NULL pointer if everything is
|
||||
** OK.
|
||||
*/
|
||||
static int fts5Integrity(sqlite3_vtab *pVtab, char **pzErr){
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
Fts5Config *pConfig = pTab->p.pConfig;
|
||||
char *zSql;
|
||||
int rc;
|
||||
zSql = sqlite3_mprintf(
|
||||
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
|
||||
pConfig->zDb, pConfig->zName, pConfig->zName);
|
||||
rc = sqlite3_exec(pConfig->db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
|
||||
pConfig->zDb, pConfig->zName);
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
static int fts5Init(sqlite3 *db){
|
||||
static const sqlite3_module fts5Mod = {
|
||||
/* iVersion */ 4,
|
||||
/* iVersion */ 3,
|
||||
/* xCreate */ fts5CreateMethod,
|
||||
/* xConnect */ fts5ConnectMethod,
|
||||
/* xBestIndex */ fts5BestIndexMethod,
|
||||
@@ -2908,8 +2860,7 @@ static int fts5Init(sqlite3 *db){
|
||||
/* xSavepoint */ fts5SavepointMethod,
|
||||
/* xRelease */ fts5ReleaseMethod,
|
||||
/* xRollbackTo */ fts5RollbackToMethod,
|
||||
/* xShadowName */ fts5ShadowName,
|
||||
/* xIntegrity */ fts5Integrity
|
||||
/* xShadowName */ fts5ShadowName
|
||||
};
|
||||
|
||||
int rc;
|
||||
|
||||
+12
-72
@@ -77,10 +77,10 @@ static int fts5StorageGetStmt(
|
||||
"INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
|
||||
"REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
|
||||
"DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
|
||||
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?%s)", /* REPLACE_DOCSIZE */
|
||||
"REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
|
||||
"DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
|
||||
|
||||
"SELECT sz%s FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
|
||||
"SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
|
||||
|
||||
"REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
|
||||
"SELECT %s FROM %s AS T", /* SCAN */
|
||||
@@ -128,19 +128,6 @@ static int fts5StorageGetStmt(
|
||||
break;
|
||||
}
|
||||
|
||||
case FTS5_STMT_REPLACE_DOCSIZE:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName,
|
||||
(pC->bContentlessDelete ? ",?" : "")
|
||||
);
|
||||
break;
|
||||
|
||||
case FTS5_STMT_LOOKUP_DOCSIZE:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt],
|
||||
(pC->bContentlessDelete ? ",origin" : ""),
|
||||
pC->zDb, pC->zName
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
|
||||
break;
|
||||
@@ -330,11 +317,9 @@ int sqlite3Fts5StorageOpen(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize ){
|
||||
const char *zCols = "id INTEGER PRIMARY KEY, sz BLOB";
|
||||
if( pConfig->bContentlessDelete ){
|
||||
zCols = "id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER";
|
||||
}
|
||||
rc = sqlite3Fts5CreateTable(pConfig, "docsize", zCols, 0, pzErr);
|
||||
rc = sqlite3Fts5CreateTable(
|
||||
pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5CreateTable(
|
||||
@@ -411,7 +396,7 @@ static int fts5StorageDeleteFromIndex(
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
int rc; /* Return code */
|
||||
int rc2; /* sqlite3_reset() return code */
|
||||
int iCol;
|
||||
Fts5InsertCtx ctx;
|
||||
@@ -427,6 +412,7 @@ static int fts5StorageDeleteFromIndex(
|
||||
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1]==0 ){
|
||||
const char *zText;
|
||||
@@ -463,37 +449,6 @@ static int fts5StorageDeleteFromIndex(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to process a DELETE on a contentless_delete=1
|
||||
** table. It adds the tombstone required to delete the entry with rowid
|
||||
** iDel. If successful, SQLITE_OK is returned. Or, if an error occurs,
|
||||
** an SQLite error code.
|
||||
*/
|
||||
static int fts5StorageContentlessDelete(Fts5Storage *p, i64 iDel){
|
||||
i64 iOrigin = 0;
|
||||
sqlite3_stmt *pLookup = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( p->pConfig->bContentlessDelete );
|
||||
assert( p->pConfig->eContent==FTS5_CONTENT_NONE );
|
||||
|
||||
/* Look up the origin of the document in the %_docsize table. Store
|
||||
** this in stack variable iOrigin. */
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pLookup, 1, iDel);
|
||||
if( SQLITE_ROW==sqlite3_step(pLookup) ){
|
||||
iOrigin = sqlite3_column_int64(pLookup, 1);
|
||||
}
|
||||
rc = sqlite3_reset(pLookup);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && iOrigin!=0 ){
|
||||
rc = sqlite3Fts5IndexContentlessDelete(p->pIndex, iOrigin, iDel);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a record into the %_docsize table. Specifically, do:
|
||||
@@ -514,17 +469,10 @@ static int fts5StorageInsertDocsize(
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int64(pReplace, 1, iRowid);
|
||||
if( p->pConfig->bContentlessDelete ){
|
||||
i64 iOrigin = 0;
|
||||
rc = sqlite3Fts5IndexGetOrigin(p->pIndex, &iOrigin);
|
||||
sqlite3_bind_int64(pReplace, 3, iOrigin);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
||||
sqlite3_step(pReplace);
|
||||
rc = sqlite3_reset(pReplace);
|
||||
sqlite3_bind_null(pReplace, 2);
|
||||
}
|
||||
sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
|
||||
sqlite3_step(pReplace);
|
||||
rc = sqlite3_reset(pReplace);
|
||||
sqlite3_bind_null(pReplace, 2);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
@@ -588,15 +536,7 @@ int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
|
||||
|
||||
/* Delete the index records */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( p->pConfig->bContentlessDelete ){
|
||||
rc = fts5StorageContentlessDelete(p, iDel);
|
||||
}else{
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
|
||||
}
|
||||
rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
|
||||
}
|
||||
|
||||
/* Delete the %_docsize record */
|
||||
|
||||
@@ -65,9 +65,7 @@ foreach w {a b c d e f} {
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
PRAGMA integrity_check;
|
||||
PRAGMA integrity_check(t1);
|
||||
} {ok ok}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -90,7 +88,6 @@ foreach {i x y} {
|
||||
} {
|
||||
do_execsql_test 3.$i.1 { INSERT INTO t1 VALUES($x, $y) }
|
||||
do_execsql_test 3.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
do_execsql_test 3.$i.3 { PRAGMA integrity_check(t1) } ok
|
||||
if {[set_test_counter errors]} break
|
||||
}
|
||||
|
||||
@@ -138,7 +135,7 @@ foreach {i x y} {
|
||||
10 {ddd abcde dddd dd c} {dddd c c d abcde}
|
||||
} {
|
||||
do_execsql_test 5.$i.1 { INSERT INTO t1 VALUES($x, $y) }
|
||||
do_execsql_test 5.$i.2 { PRAGMA integrity_check(t1) } ok
|
||||
do_execsql_test 5.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
if {[set_test_counter errors]} break
|
||||
}
|
||||
|
||||
|
||||
@@ -307,31 +307,5 @@ do_catchsql_test 10.1.4 {
|
||||
SELECT group_concat(firstcol(t1), '.') FROM t1 GROUP BY rowid
|
||||
} {1 {unable to use function firstcol in the requested context}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that xInstCount() works from within an xPhraseQuery() callback.
|
||||
#
|
||||
reset_db
|
||||
|
||||
proc xCallback {cmd} {
|
||||
incr ::hitcount [$cmd xInstCount]
|
||||
return SQLITE_OK
|
||||
}
|
||||
proc fts5_hitcount {cmd} {
|
||||
set ::hitcount 0
|
||||
$cmd xQueryPhrase 0 xCallback
|
||||
return $::hitcount
|
||||
}
|
||||
sqlite3_fts5_create_function db fts5_hitcount fts5_hitcount
|
||||
|
||||
do_execsql_test 11.1 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(z);
|
||||
INSERT INTO x1 VALUES('one two three');
|
||||
INSERT INTO x1 VALUES('one two one three one');
|
||||
INSERT INTO x1 VALUES('one two three');
|
||||
}
|
||||
|
||||
do_execsql_test 11.2 {
|
||||
SELECT fts5_hitcount(x1) FROM x1('one') LIMIT 1;
|
||||
} {5}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -65,44 +65,4 @@ do_execsql_test 2.1 {
|
||||
INSERT INTO fts_idx(fts_idx) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests for OR IGNORE conflict handling.
|
||||
#
|
||||
reset_db
|
||||
foreach_detail_mode $::testprefix {
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(xyz, detail=%DETAIL%);
|
||||
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, xyz) VALUES(13, 'thirteen documents');
|
||||
INSERT INTO t1(rowid, xyz) VALUES(14, 'fourteen documents');
|
||||
INSERT INTO t1(rowid, xyz) VALUES(15, 'fifteen documents');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
set db_cksum [cksum]
|
||||
foreach {tn sql} {
|
||||
1 {
|
||||
INSERT OR IGNORE INTO t1(rowid, xyz) VALUES(14, 'new text');
|
||||
}
|
||||
2 {
|
||||
UPDATE OR IGNORE t1 SET rowid=13 WHERE rowid=15;
|
||||
}
|
||||
3 {
|
||||
INSERT OR IGNORE INTO t1(rowid, xyz)
|
||||
SELECT 13, 'some text'
|
||||
UNION ALL
|
||||
SELECT 14, 'some text'
|
||||
UNION ALL
|
||||
SELECT 15, 'some text'
|
||||
}
|
||||
} {
|
||||
do_execsql_test 3.1.$tn.1 $sql
|
||||
do_test 3.1.$tn.2 { cksum } $db_cksum
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -294,3 +294,4 @@ do_catchsql_test 7.2.5 {
|
||||
} {1 {recursively defined fts5 content table}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
# 2014 Dec 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Check that it is not possible to specify "contentless_delete=1" for
|
||||
# anything other than a contentless table.
|
||||
#
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {contentless_delete=1 requires a contentless table}}
|
||||
foreach {tn sql bError} {
|
||||
1 "(a, b, contentless_delete=1)" 1
|
||||
2 "(a, b, contentless_delete=1, content=abc)" 1
|
||||
3 "(a, b, contentless_delete=1, content=)" 0
|
||||
4 "(content=, contentless_delete=1, a)" 0
|
||||
5 "(content='', contentless_delete=1, hello)" 0
|
||||
} {
|
||||
execsql { BEGIN }
|
||||
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
|
||||
execsql { ROLLBACK }
|
||||
}
|
||||
|
||||
# Check that it is not possible to specify "contentless_delete=1"
|
||||
# along with columnsize=1.
|
||||
#
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {contentless_delete=1 is incompatible with columnsize=0}}
|
||||
foreach {tn sql bError} {
|
||||
2 "(a, b, content='', contentless_delete=1, columnsize=0)" 1
|
||||
} {
|
||||
execsql { BEGIN }
|
||||
do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts5 $sql" $res($bError)
|
||||
execsql { ROLLBACK }
|
||||
}
|
||||
|
||||
# Check that if contentless_delete=1 is specified, then the "origin"
|
||||
# column is added to the %_docsize table.
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(c, content='');
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(c, content='', contentless_delete=1);
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
SELECT sql FROM sqlite_schema WHERE name IN ('x1_docsize', 'x2_docsize');
|
||||
} {
|
||||
{CREATE TABLE 'x1_docsize'(id INTEGER PRIMARY KEY, sz BLOB)}
|
||||
{CREATE TABLE 'x2_docsize'(id INTEGER PRIMARY KEY, sz BLOB, origin INTEGER)}
|
||||
}
|
||||
|
||||
do_execsql_test 3.2.1 {
|
||||
SELECT hex(block) FROM x1_data WHERE id=10
|
||||
} {00000000000000}
|
||||
do_execsql_test 3.2.2 {
|
||||
SELECT hex(block) FROM x2_data WHERE id=10
|
||||
} {00000000FF000001000000}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO x2 VALUES('first text');
|
||||
INSERT INTO x2 VALUES('second text');
|
||||
}
|
||||
do_execsql_test 3.4 {
|
||||
SELECT id, origin FROM x2_docsize
|
||||
} {1 1 2 2}
|
||||
do_execsql_test 3.5 {
|
||||
SELECT level, segment, loc1, loc2 FROM fts5_structure(
|
||||
(SELECT block FROM x2_data WHERE id=10)
|
||||
)
|
||||
} {
|
||||
0 0 1 1
|
||||
0 1 2 2
|
||||
}
|
||||
do_execsql_test 3.6 {
|
||||
INSERT INTO x2(x2) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 3.7 {
|
||||
SELECT level, segment, loc1, loc2 FROM fts5_structure(
|
||||
(SELECT block FROM x2_data WHERE id=10)
|
||||
)
|
||||
} {
|
||||
1 0 1 2
|
||||
}
|
||||
|
||||
do_execsql_test 3.8 {
|
||||
DELETE FROM x2 WHERE rowid=2;
|
||||
}
|
||||
|
||||
do_execsql_test 3.9 {
|
||||
SELECT rowid FROM x2('text')
|
||||
} {1}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
proc document {n} {
|
||||
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
|
||||
set ret [list]
|
||||
for {set ii 0} {$ii < $n} {incr ii} {
|
||||
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
set nRow 1000
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE TABLE t1(x);
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(ft, rank) VALUES('pgsz', 100);
|
||||
}
|
||||
do_test 4.1 {
|
||||
for {set ii 0} {$ii < $nRow} {incr ii} {
|
||||
set doc [document 6]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES($doc);
|
||||
INSERT INTO ft VALUES($doc);
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
|
||||
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
|
||||
set L2 [execsql {SELECT rowid FROM ft($v)}]
|
||||
do_test 4.2.$v { set L1 } $L2
|
||||
}
|
||||
|
||||
do_test 4.3 {
|
||||
for {set ii 1} {$ii < $nRow} {incr ii 2} {
|
||||
execsql {
|
||||
DELETE FROM ft WHERE rowid=$ii;
|
||||
DELETE FROM t1 WHERE rowid=$ii;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
|
||||
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
|
||||
set L2 [execsql {SELECT rowid FROM ft($v)}]
|
||||
do_test 4.4.$v { set L1 } $L2
|
||||
}
|
||||
|
||||
do_execsql_test 4.5 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
} {}
|
||||
|
||||
foreach v {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} {
|
||||
set L1 [execsql {SELECT rowid FROM t1 WHERE x LIKE '%'||$v||'%'}]
|
||||
set L2 [execsql {SELECT rowid FROM ft($v)}]
|
||||
do_test 4.6.$v { set L1 } $L2
|
||||
}
|
||||
|
||||
#execsql_pp { SELECT fts5_decode(id, block) FROM ft_data }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
|
||||
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
|
||||
INSERT INTO ft(rowid, x) VALUES(3, 'one two five');
|
||||
INSERT INTO ft(rowid, x) VALUES(4, 'one two seven');
|
||||
INSERT INTO ft(rowid, x) VALUES(5, 'one two eight');
|
||||
}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
DELETE FROM ft WHERE rowid=2
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT rowid FROM ft
|
||||
} {1 3 4 5}
|
||||
|
||||
do_catchsql_test 5.3 {
|
||||
UPDATE ft SET x='four six' WHERE rowid=3
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
SELECT rowid FROM ft('one');
|
||||
} {1 4 5}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
REPLACE INTO ft(rowid, x) VALUES(3, 'four six');
|
||||
SELECT rowid FROM ft('one');
|
||||
} {1 4 5}
|
||||
|
||||
do_execsql_test 5.6 {
|
||||
REPLACE INTO ft(rowid, x) VALUES(6, 'one two eleven');
|
||||
SELECT rowid FROM ft('one');
|
||||
} {1 4 5 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(rowid, x) VALUES(1, 'one two three');
|
||||
INSERT INTO ft(rowid, x) VALUES(2, 'one two four');
|
||||
}
|
||||
|
||||
do_test 6.1 {
|
||||
db eval { SELECT rowid FROM ft('one two') } {
|
||||
if {$rowid==1} {
|
||||
db eval { INSERT INTO ft(rowid, x) VALUES(3, 'one two four') }
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
}
|
||||
|
||||
set lRowid [list -450 0 1 2 42]
|
||||
|
||||
do_test 7.1 {
|
||||
execsql BEGIN
|
||||
foreach r $lRowid {
|
||||
execsql { INSERT INTO ft(rowid, x) VALUES($r, 'one one one'); }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test 7.2 {
|
||||
execsql BEGIN
|
||||
foreach r $lRowid {
|
||||
execsql { REPLACE INTO ft(rowid, x) VALUES($r, 'two two two'); }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_execsql_test 7.3 { SELECT rowid FROM ft('one'); } {}
|
||||
do_execsql_test 7.4 { SELECT rowid FROM ft('two'); } $lRowid
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft VALUES('hello world');
|
||||
INSERT INTO ft VALUES('one two three');
|
||||
}
|
||||
|
||||
do_catchsql_test 8.1 {
|
||||
INSERT INTO ft(ft, rowid, x) VALUES('delete', 1, 'hello world');
|
||||
} {1 {'delete' may not be used with a contentless_delete=1 table}}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
BEGIN;
|
||||
INSERT INTO ft(rowid, x) VALUES(3, 'four four four');
|
||||
DELETE FROM ft WHERE rowid=3;
|
||||
COMMIT;
|
||||
SELECT rowid FROM ft('four');
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
@@ -1,207 +0,0 @@
|
||||
# 2023 July 19
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc vocab {} {
|
||||
list aaa bbb ccc ddd eee fff ggg hhh iii jjj kkk lll mmm nnn ooo ppp
|
||||
}
|
||||
|
||||
proc document {nToken} {
|
||||
set doc [list]
|
||||
set vocab [vocab]
|
||||
for {set ii 0} {$ii < $nToken} {incr ii} {
|
||||
lappend doc [lindex $vocab [expr int(rand()*[llength $vocab])]]
|
||||
}
|
||||
set doc
|
||||
}
|
||||
db func document document
|
||||
|
||||
proc contains {doc token} {
|
||||
expr {[lsearch $doc $token]>=0}
|
||||
}
|
||||
db func contains contains
|
||||
|
||||
proc do_compare_tables_test {tn} {
|
||||
uplevel [list do_test $tn {
|
||||
foreach v [vocab] {
|
||||
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $v) }]
|
||||
set l2 [execsql { SELECT rowid FROM t2($v) }]
|
||||
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
|
||||
|
||||
set w "[string range $v 0 1]*"
|
||||
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
|
||||
set l2 [execsql { SELECT rowid FROM t2($w) }]
|
||||
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
|
||||
|
||||
set w "[string range $v 0 0]*"
|
||||
set l1 [execsql { SELECT rowid FROM t1 WHERE contains(doc, $w) }]
|
||||
set l2 [execsql { SELECT rowid FROM t2($w) }]
|
||||
if {$l1!=$l2} { error "2: query mismatch ($l1) ($l2)" }
|
||||
|
||||
set l1 [execsql {
|
||||
SELECT rowid FROM t1 WHERE contains(doc, $v) ORDER BY rowid DESC
|
||||
}]
|
||||
set l2 [execsql { SELECT rowid FROM t2($v) ORDER BY rowid DESC }]
|
||||
if {$l1!=$l2} { error "1: query mismatch ($l1) ($l2)" }
|
||||
}
|
||||
set {} {}
|
||||
} {}]
|
||||
}
|
||||
|
||||
proc lshuffle {in} {
|
||||
set L [list]
|
||||
set ret [list]
|
||||
foreach elem $in { lappend L [list [expr rand()] $elem] }
|
||||
foreach pair [lsort -index 0 $L] { lappend ret [lindex $pair 1] }
|
||||
set ret
|
||||
}
|
||||
|
||||
expr srand(0)
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(
|
||||
doc, prefix=2, content=, contentless_delete=1
|
||||
);
|
||||
|
||||
CREATE TABLE t1(doc);
|
||||
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
|
||||
DELETE FROM t2 WHERE rowid = old.rowid;
|
||||
END;
|
||||
}
|
||||
|
||||
set SMALLEST64 -9223372036854775808
|
||||
set LARGEST64 9223372036854775807
|
||||
|
||||
foreach {tn r1 r2} {
|
||||
1 0 50
|
||||
2 $SMALLEST64 $SMALLEST64+50
|
||||
3 $LARGEST64-50 $LARGEST64
|
||||
4 -50 -1
|
||||
} {
|
||||
set r1 [expr $r1]
|
||||
set r2 [expr $r2]
|
||||
|
||||
do_test 1.1.$tn {
|
||||
execsql BEGIN
|
||||
for {set ii $r1} {$ii <= $r2} {incr ii} {
|
||||
execsql { INSERT INTO t1(rowid, doc) VALUES ($ii, document(8)); }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
}
|
||||
do_test 1.2 {
|
||||
db eval { SELECT rowid, doc FROM t1 } {
|
||||
execsql { INSERT INTO t2(rowid, doc) VALUES($rowid, $doc) }
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach {tn rowid} {
|
||||
1 $SMALLEST64
|
||||
2 0
|
||||
3 -5
|
||||
4 -30
|
||||
5 $LARGEST64
|
||||
6 $LARGEST64-1
|
||||
} {
|
||||
set rowid [expr $rowid]
|
||||
do_execsql_test 1.3.$tn.1 {
|
||||
DELETE FROM t1 WHERE rowid=$rowid
|
||||
}
|
||||
do_compare_tables_test 1.3.$tn.2
|
||||
}
|
||||
|
||||
set iTest 1
|
||||
foreach r [lshuffle [execsql {SELECT rowid FROM t1}]] {
|
||||
if {($iTest % 50)==0} {
|
||||
execsql { INSERT INTO t2(t2) VALUES('optimize') }
|
||||
}
|
||||
if {($iTest % 5)==0} {
|
||||
execsql { INSERT INTO t2(t2, rank) VALUES('merge', 5) }
|
||||
}
|
||||
do_execsql_test 1.4.$iTest.1($r) {
|
||||
DELETE FROM t1 WHERE rowid=$r
|
||||
}
|
||||
do_compare_tables_test 1.4.$iTest.2
|
||||
incr iTest
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT * FROM t1
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
BEGIN;
|
||||
DELETE FROM t2 WHERE rowid=32;
|
||||
DELETE FROM t2 WHERE rowid=64;
|
||||
DELETE FROM t2 WHERE rowid=96;
|
||||
DELETE FROM t2 WHERE rowid=128;
|
||||
DELETE FROM t2 WHERE rowid=160;
|
||||
DELETE FROM t2 WHERE rowid=192;
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT * FROM t2('128');
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn step} {
|
||||
1 3
|
||||
2 7
|
||||
3 15
|
||||
} {
|
||||
set step [expr $step]
|
||||
|
||||
reset_db
|
||||
db func document document
|
||||
do_execsql_test 3.$tn.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(doc, content=, contentless_delete=1);
|
||||
INSERT INTO t2(t2, rank) VALUES('pgsz', 100);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO t2(rowid, doc) SELECT i, i || ' ' || i FROM s;
|
||||
}
|
||||
do_execsql_test 3.$tn.1 {
|
||||
DELETE FROM t2 WHERE (rowid % $step)==0
|
||||
}
|
||||
do_execsql_test 3.$tn.2 {
|
||||
SELECT * FROM t2( $step * 5 )
|
||||
} {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -1,195 +0,0 @@
|
||||
# 2023 July 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless3
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO ft VALUES('one one one');
|
||||
INSERT INTO ft VALUES('two two two');
|
||||
INSERT INTO ft VALUES('three three three');
|
||||
INSERT INTO ft VALUES('four four four');
|
||||
INSERT INTO ft VALUES('five five five');
|
||||
INSERT INTO ft VALUES('six six six');
|
||||
INSERT INTO ft VALUES('seven seven seven');
|
||||
INSERT INTO ft VALUES('eight eight eight');
|
||||
INSERT INTO ft VALUES('nine nine nine');
|
||||
COMMIT;
|
||||
|
||||
DELETE FROM ft WHERE rowid=3;
|
||||
}
|
||||
|
||||
proc myhex {hex} { binary decode hex $hex }
|
||||
db func myhex myhex
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
UPDATE ft_data SET block =
|
||||
myhex('04000000 00000001' ||
|
||||
'01020304 01020304 01020304 01020304' ||
|
||||
'01020304 01020304 01020304 01020304'
|
||||
)
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
DELETE FROM ft WHERE rowid=1
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT rowid FROM ft('two');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
UPDATE ft_data SET block =
|
||||
myhex('08000000 00000001' ||
|
||||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304' ||
|
||||
'0000000001020304 0000000001020304 0000000001020304 0000000001020304'
|
||||
)
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT rowid FROM ft('two');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
DELETE FROM ft WHERE rowid=4
|
||||
}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
UPDATE ft_data SET block = myhex('04000000 00000000')
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
do_execsql_test 1.7 {
|
||||
SELECT rowid FROM ft('two');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
UPDATE ft_data SET block = myhex('04000000 00000000')
|
||||
WHERE id = (SELECT max(id) FROM ft_data);
|
||||
}
|
||||
do_execsql_test 1.9 {
|
||||
DELETE FROM ft WHERE rowid=8
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
|
||||
INSERT INTO ft VALUES('one one one');
|
||||
INSERT INTO ft VALUES('two two two');
|
||||
INSERT INTO ft VALUES('three three three');
|
||||
INSERT INTO ft VALUES('four four four');
|
||||
INSERT INTO ft VALUES('five five five');
|
||||
INSERT INTO ft VALUES('six six six');
|
||||
INSERT INTO ft VALUES('seven seven seven');
|
||||
INSERT INTO ft VALUES('eight eight eight');
|
||||
INSERT INTO ft VALUES('nine nine nine');
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 2.2 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {3}
|
||||
do_execsql_test 2.3 {
|
||||
DELETE FROM ft WHERE rowid=5
|
||||
}
|
||||
do_execsql_test 2.4 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {4}
|
||||
|
||||
# Check that an 'optimize' works (rewrites the index) if there is a single
|
||||
# segment with one or more tombstone hash pages.
|
||||
do_execsql_test 2.5 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 2.6 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {3}
|
||||
|
||||
# Check that an 'optimize' is a no-op if there is a single segment
|
||||
# and no tombstone hash pages.
|
||||
do_execsql_test 2.7 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
SELECT rowid FROM ft_data;
|
||||
} [db eval {SELECT rowid FROM ft_data}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content=, contentless_delete=1);
|
||||
INSERT INTO ft(ft, rank) VALUES('pgsz', 64);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO ft(rowid, x) SELECT i, i||' '||i||' '||i||' '||i FROM s;
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
SELECT count(*) FROM ft_data
|
||||
} {200}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
DELETE FROM ft WHERE (rowid % 50)==0;
|
||||
SELECT count(*) FROM ft_data;
|
||||
} {203}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
INSERT INTO ft(ft, rank) VALUES('merge', 500);
|
||||
SELECT rowid FROM ft_data;
|
||||
} [db eval {SELECT rowid FROM ft_data}]
|
||||
|
||||
do_execsql_test 3.4 {
|
||||
INSERT INTO ft(ft, rank) VALUES('merge', -1000);
|
||||
SELECT count(*) FROM ft_data;
|
||||
} {197}
|
||||
|
||||
do_execsql_test 3.5 {
|
||||
DELETE FROM ft WHERE (rowid % 50)==1;
|
||||
SELECT count(*) FROM ft_data;
|
||||
} {200}
|
||||
|
||||
do_execsql_test 3.6 {
|
||||
SELECT level, segment, npgtombstone FROM fts5_structure(
|
||||
(SELECT block FROM ft_data WHERE id=10)
|
||||
)
|
||||
} {1 0 3}
|
||||
|
||||
do_test 3.6 {
|
||||
while 1 {
|
||||
set nChange [db total_changes]
|
||||
execsql { INSERT INTO ft(ft, rank) VALUES('merge', -5) }
|
||||
if {([db total_changes] - $nChange)<2} break
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 3.7 {
|
||||
SELECT level, segment, npgtombstone FROM fts5_structure(
|
||||
(SELECT block FROM ft_data WHERE id=10)
|
||||
)
|
||||
} {2 0 0}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -1,247 +0,0 @@
|
||||
# 2023 July 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless4
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc document {n} {
|
||||
set vocab [list A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
|
||||
set ret [list]
|
||||
for {set ii 0} {$ii < $n} {incr ii} {
|
||||
lappend ret [lindex $vocab [expr int(rand()*[llength $vocab])]]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
db func document document
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO ft(ft, rank) VALUES('pgsz', 240);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO ft SELECT document(12) FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO ft(ft) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {0 0 1000 0}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
DELETE FROM ft WHERE rowid < 50
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {0 0 1000 49}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
DELETE FROM ft WHERE rowid < 1000
|
||||
}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT level, segment, nentry, nentrytombstone FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {1 0 1 0}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
}
|
||||
|
||||
do_test 2.1 {
|
||||
for {set ii 0} {$ii < 5000} {incr ii} {
|
||||
execsql { INSERT INTO ft VALUES( document(12) ) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT sum(nentry) - sum(nentrytombstone) FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {5000}
|
||||
|
||||
for {set ii 5000} {$ii >= 0} {incr ii -100} {
|
||||
do_execsql_test 2.3.$ii {
|
||||
DELETE FROM ft WHERE rowid > $ii
|
||||
}
|
||||
do_execsql_test 2.3.$ii.2 {
|
||||
SELECT
|
||||
CAST((total(nentry) - total(nentrytombstone)) AS integer)
|
||||
FROM
|
||||
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
|
||||
} $ii
|
||||
}
|
||||
|
||||
execsql_pp {
|
||||
SELECT * FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
}
|
||||
|
||||
do_test 2.4 {
|
||||
for {set ii 0} {$ii < 5000} {incr ii} {
|
||||
execsql { INSERT INTO ft VALUES( document(12) ) }
|
||||
}
|
||||
} {}
|
||||
|
||||
for {set ii 1} {$ii <= 5000} {incr ii 10} {
|
||||
do_execsql_test 2.3.$ii {
|
||||
DELETE FROM ft WHERE rowid = $ii;
|
||||
INSERT INTO ft VALUES( document(12) );
|
||||
INSERT INTO ft(ft, rank) VALUES('merge', -10);
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.$ii.2 {
|
||||
SELECT
|
||||
CAST((total(nentry) - total(nentrytombstone)) AS integer)
|
||||
FROM
|
||||
fts5_structure( (SELECT block FROM ft_data WHERE id=10) )
|
||||
} 5000
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(x, content='', contentless_delete=1);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO ft SELECT document(12) FROM s;
|
||||
}
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 'text');
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 3.2 {
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
|
||||
} {0 {}}
|
||||
do_execsql_test 3.3 {
|
||||
SELECT * FROM ft_config WHERE k='deletemerge'
|
||||
} {deletemerge 50}
|
||||
do_catchsql_test 3.4 {
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 101);
|
||||
} {0 {}}
|
||||
do_execsql_test 3.5 {
|
||||
SELECT * FROM ft_config WHERE k='deletemerge'
|
||||
} {deletemerge 101}
|
||||
|
||||
do_execsql_test 3.6 {
|
||||
DELETE FROM ft WHERE rowid<95
|
||||
}
|
||||
|
||||
do_execsql_test 3.7 {
|
||||
SELECT nentrytombstone, nentry FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {94 100}
|
||||
|
||||
do_execsql_test 3.8 {
|
||||
DELETE FROM ft WHERE rowid=95
|
||||
}
|
||||
|
||||
do_execsql_test 3.9 {
|
||||
SELECT nentrytombstone, nentry FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {95 100}
|
||||
|
||||
do_execsql_test 3.10 {
|
||||
DELETE FROM ft;
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
|
||||
)
|
||||
INSERT INTO ft SELECT document(12) FROM s;
|
||||
INSERT INTO ft(ft, rank) VALUES('deletemerge', 50);
|
||||
}
|
||||
|
||||
do_execsql_test 3.11 {
|
||||
DELETE FROM ft WHERE rowid<95
|
||||
}
|
||||
|
||||
do_execsql_test 3.12 {
|
||||
SELECT nentrytombstone, nentry FROM fts5_structure((
|
||||
SELECT block FROM ft_data WHERE id=10
|
||||
))
|
||||
} {0 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db func document document
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
|
||||
INSERT INTO x1(x1, rank) VALUES('usermerge', 16);
|
||||
INSERT INTO x1(x1, rank) VALUES('deletemerge', 40);
|
||||
INSERT INTO x1 VALUES('one');
|
||||
INSERT INTO x1 VALUES('two');
|
||||
INSERT INTO x1 VALUES('three');
|
||||
INSERT INTO x1 VALUES('four');
|
||||
INSERT INTO x1 VALUES('five');
|
||||
INSERT INTO x1 VALUES('six');
|
||||
INSERT INTO x1 VALUES('seven');
|
||||
INSERT INTO x1 VALUES('eight');
|
||||
INSERT INTO x1 VALUES('nine');
|
||||
INSERT INTO x1 VALUES('ten');
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
SELECT level, segment FROM fts5_structure((
|
||||
SELECT block FROM x1_data WHERE id=10
|
||||
))
|
||||
} {
|
||||
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
|
||||
}
|
||||
|
||||
for {set ii 1} {$ii < 4} {incr ii} {
|
||||
do_execsql_test 4.2.$ii {
|
||||
DELETE FROM x1 WHERE rowid = $ii;
|
||||
INSERT INTO x1(x1, rank) VALUES('merge', 5);
|
||||
SELECT level, segment FROM fts5_structure((
|
||||
SELECT block FROM x1_data WHERE id=10
|
||||
))
|
||||
} {
|
||||
0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
DELETE FROM x1 WHERE rowid = $ii;
|
||||
INSERT INTO x1(x1, rank) VALUES('merge', 5);
|
||||
SELECT level, segment, nentry FROM fts5_structure((
|
||||
SELECT block FROM x1_data WHERE id=10
|
||||
))
|
||||
} {
|
||||
1 0 6
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -1,58 +0,0 @@
|
||||
# 2023 August 7
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the content= and content_rowid= options.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5contentless5
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, content='', contentless_delete=1);
|
||||
INSERT INTO t1 VALUES('A', 'B', 'C');
|
||||
INSERT INTO t1 VALUES('D', 'E', 'F');
|
||||
INSERT INTO t1 VALUES('G', 'H', 'I');
|
||||
}
|
||||
|
||||
do_execsql_test 1.01 {
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t2 VALUES('x', 'y');
|
||||
}
|
||||
|
||||
# explain_i "UPDATE t1 SET a='a' WHERE t1.rowid=1"
|
||||
breakpoint
|
||||
explain_i "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1 AND b IS NULL"
|
||||
|
||||
#breakpoint
|
||||
#explain_i "UPDATE t1 SET a='a' WHERE b IS NULL AND rowid=?"
|
||||
|
||||
foreach {tn up err} {
|
||||
1 "UPDATE t1 SET a='a', b='b', c='c' WHERE rowid=1" 0
|
||||
2 "UPDATE t1 SET a='a', b='b' WHERE rowid=1" 1
|
||||
3 "UPDATE t1 SET b='b', c='c' WHERE rowid=1" 1
|
||||
4 "UPDATE t1 SET a='a', c='c' WHERE rowid=1" 1
|
||||
5 "UPDATE t1 SET a='a', c='c' WHERE t1.rowid=1 AND b IS NULL" 1
|
||||
6 "UPDATE t1 SET a='a' FROM t2 WHERE t1.rowid=1" 1
|
||||
7 "UPDATE t1 SET a='a', b='b', c='c' FROM t2 WHERE t1.rowid=1" 0
|
||||
} {
|
||||
|
||||
set res(0) {0 {}}
|
||||
set res(1) {1 {cannot UPDATE a subset of columns on fts5 contentless-delete table: t1}}
|
||||
do_catchsql_test 1.$tn $up $res($err)
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -48,10 +48,6 @@ do_test 1.3 {
|
||||
}
|
||||
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 1.3b {
|
||||
PRAGMA integrity_check(t1);
|
||||
} {{malformed inverted index for FTS5 table main.t1}}
|
||||
|
||||
|
||||
do_test 1.4 {
|
||||
db_restore_and_reopen
|
||||
|
||||
@@ -167,9 +167,6 @@ foreach {tn hdr} {
|
||||
do_test 3.$tn.$tn2.2 {
|
||||
catchsql { INSERT INTO x3(x3) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 3.$tn.$tn2.3 {
|
||||
PRAGMA integrity_check(x3);
|
||||
} {{malformed inverted index for FTS5 table main.x3}}
|
||||
}
|
||||
|
||||
execsql ROLLBACK
|
||||
|
||||
@@ -95,9 +95,6 @@ do_execsql_test 3.3 {
|
||||
SELECT rowid, bm25(e1) FROM e1 WHERE e1 MATCH '"/" OR "just"' ORDER BY rank;
|
||||
} {1 -1e-06}
|
||||
|
||||
do_execsql_test 3.4 "
|
||||
SELECT fts5_expr_tcl('e AND \" \"');
|
||||
" {{AND [nearset -- {e}] [{}]}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
# 2023 July 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# This file is focused on OOM errors. Particularly those that may occur
|
||||
# when using contentless_delete=1 databases.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5faultF
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, content=, contentless_delete=1)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 {vtable constructor failed: t1}}
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
|
||||
COMMIT;
|
||||
DELETE FROM t1 WHERE rowid IN (2, 4);
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -prep {
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT rowid FROM t1 }
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT rowid FROM t1('b c');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 3}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, doc) VALUES(1, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(2, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(3, 'a b c d');
|
||||
INSERT INTO t1(rowid, doc) VALUES(4, 'a b c d');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT rowid FROM t1 }
|
||||
} -body {
|
||||
execsql {
|
||||
INSERT INTO t1(rowid, doc) VALUES(5, 'a b c d');
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(doc, content=, contentless_delete=1);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<1000
|
||||
)
|
||||
INSERT INTO t1(rowid, doc) SELECT i, 'a b c d' FROM s;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 { DELETE FROM t1 WHERE rowid <= 25 }
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 4 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT rowid FROM t1 }
|
||||
} -body {
|
||||
execsql {
|
||||
DELETE FROM t1 WHERE rowid < 100
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -44,12 +44,12 @@ do_catchsql_test 1.2.2 {
|
||||
|
||||
do_catchsql_test 1.3.1 {
|
||||
SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads');
|
||||
} {1 {no such cursor: 2}}
|
||||
} {1 {no such cursor: 1}}
|
||||
|
||||
do_catchsql_test 1.3.2 {
|
||||
SELECT a FROM t1
|
||||
WHERE rank = (SELECT highlight(t1, 4, '<b>', '</b>') FROM t1('*reads'));
|
||||
} {1 {no such cursor: 2}}
|
||||
} {1 {no such cursor: 1}}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# TESTRUNNER: superslow
|
||||
# TESTRUNNER: slow
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
@@ -42,4 +42,23 @@ do_execsql_test 1.2 {
|
||||
SELECT count(*) FROM t1('mno')
|
||||
} $nLoop
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x);
|
||||
INSERT INTO t2(t2, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
do_test 2.1 {
|
||||
for {set ii 0} {$ii < $nLoop} {incr ii} {
|
||||
execsql {
|
||||
INSERT INTO t2 VALUES('abc def ghi');
|
||||
INSERT INTO t2 VALUES('jkl mno pqr');
|
||||
INSERT INTO t2(t2, rank) VALUES('merge', -1);
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT count(*) FROM t2('mno')
|
||||
} $nLoop
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
# 2023 Aug 27
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# TESTRUNNER: superslow
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5optimize2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set nLoop 2500
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x);
|
||||
INSERT INTO t2(t2, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
for {set ii 0} {$ii < $nLoop} {incr ii} {
|
||||
execsql {
|
||||
INSERT INTO t2 VALUES('abc def ghi');
|
||||
INSERT INTO t2 VALUES('jkl mno pqr');
|
||||
INSERT INTO t2(t2, rank) VALUES('merge', -1);
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT count(*) FROM t2('mno')
|
||||
} $nLoop
|
||||
|
||||
finish_test
|
||||
@@ -180,28 +180,4 @@ do_execsql_test 6.1 {
|
||||
{table table table} {the table names.} {rank on an fts5 table}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# forum post: https://sqlite.org/forum/forumpost/a2dd636330
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t USING fts5 (a, b);
|
||||
INSERT INTO t (a, b) VALUES ('data1', 'sentence1'), ('data2', 'sentence2');
|
||||
INSERT INTO t(t, rank) VALUES ('rank', 'bm25(10.0,1.0)');
|
||||
}
|
||||
|
||||
sqlite3 db2 test.db
|
||||
do_execsql_test -db db2 1.1 {
|
||||
SELECT *, rank<0.0 FROM t('data*') ORDER BY RANK;
|
||||
} {data1 sentence1 1 data2 sentence2 1}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t(t, rank) VALUES ('rank', 'bm25(10.0,1.0)');
|
||||
}
|
||||
do_execsql_test -db db2 1.3 {
|
||||
SELECT *, rank<0.0 FROM t('data*') ORDER BY RANK;
|
||||
} {data1 sentence1 1 data2 sentence2 1}
|
||||
db2 close
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -273,76 +273,6 @@ do_execsql_test 5.3 {
|
||||
do_execsql_test 5.4 { SELECT rowid FROM t1('abc'); } 2
|
||||
do_execsql_test 5.5 { SELECT rowid FROM t1('aa'); } 2
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests for the bug fixed by https://sqlite.org/src/info/4b60a1c3
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE fts USING fts5(content);
|
||||
INSERT INTO fts(fts, rank) VALUES ('secure-delete', 1);
|
||||
INSERT INTO fts(rowid, content) VALUES
|
||||
(3407, 'profile profile profile profile profile profile profile profile pull pulling pulling really');
|
||||
DELETE FROM fts WHERE rowid IS 3407;
|
||||
INSERT INTO fts(fts) VALUES ('integrity-check');
|
||||
}
|
||||
|
||||
foreach {tn detail} {
|
||||
1 full
|
||||
2 column
|
||||
3 none
|
||||
} {
|
||||
do_execsql_test 6.1.$detail "
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=$detail);
|
||||
"
|
||||
|
||||
do_execsql_test 6.2.$detail {
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
|
||||
}
|
||||
|
||||
for {set ii 1} {$ii < 100} {incr ii} {
|
||||
do_execsql_test 6.3.$detail.$ii.1 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x) VALUES(10, 'word1');
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<CAST($ii AS integer)
|
||||
)
|
||||
INSERT INTO t1(x) SELECT 'word3' FROM s;
|
||||
COMMIT;
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 6.3.$detail.$ii.2 {
|
||||
DELETE FROM t1 WHERE rowid=10;
|
||||
INSERT INTO t1(t1) VALUES ('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 6.3.$detail.$ii.3 {
|
||||
DELETE FROM t1;
|
||||
}
|
||||
|
||||
do_execsql_test 6.3.$detail.$ii.4 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x) VALUES(10, 'tokenA');
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<CAST($ii AS integer)
|
||||
)
|
||||
INSERT INTO t1(x) SELECT group_concat('tokenB ') FROM s;
|
||||
COMMIT;
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
|
||||
do_execsql_test 6.3.$detail.$ii.5 {
|
||||
DELETE FROM t1 WHERE rowid=10;
|
||||
INSERT INTO t1(t1) VALUES ('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 6.3.$detail.$ii.6 {
|
||||
DELETE FROM t1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -50,46 +50,6 @@ do_test 1.3 {
|
||||
expr $phc(1)*5 < $phc(2)
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x) VALUES(-100000, 'abc def ghi');
|
||||
INSERT INTO t1(rowid, x) VALUES(-99999, 'abc def ghi');
|
||||
INSERT INTO t1(rowid, x) VALUES(9223372036854775800, 'abc def ghi');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT rowid FROM t1('def')
|
||||
} {-100000 -99999 9223372036854775800}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x)
|
||||
VALUES(51869, 'when whenever where weress what turn'),
|
||||
(51871, 'to were');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
DELETE FROM t1 WHERE rowid=51871;
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
# 2023 Feb 17
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# TESTRUNNER: slow
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
ifcapable !fts5 { finish_test ; return }
|
||||
set ::testprefix fts5secure7
|
||||
|
||||
|
||||
set NVOCAB 500
|
||||
set NDOC [expr 1000]
|
||||
|
||||
set NREP 100
|
||||
set nDeletePerRep [expr 5]
|
||||
|
||||
set VOCAB [list]
|
||||
|
||||
proc select_one {list} {
|
||||
set n [llength $list]
|
||||
lindex $list [expr {abs(int(rand()*$n))}]
|
||||
}
|
||||
|
||||
proc init_vocab {} {
|
||||
set L [split "abcdefghijklmnopqrstuvwxyz" {}]
|
||||
set nL [llength $L]
|
||||
for {set i 0} {$i < $::NVOCAB} {incr i} {
|
||||
set n [expr {6 + int(rand()*8)}]
|
||||
set word ""
|
||||
for {set j 0} {$j < $n} {incr j} {
|
||||
append word [select_one $L]
|
||||
}
|
||||
lappend ::VOCAB $word
|
||||
}
|
||||
}
|
||||
|
||||
proc get_word {} {
|
||||
select_one $::VOCAB
|
||||
}
|
||||
|
||||
proc get_document {nWord} {
|
||||
set ret [list]
|
||||
for {set i 0} {$i < $nWord} {incr i} {
|
||||
lappend ret [get_word]
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
init_vocab
|
||||
|
||||
db func document [list get_document 12]
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(body);
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
|
||||
}
|
||||
do_execsql_test 1.1 {
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$NDOC
|
||||
)
|
||||
INSERT INTO t1 SELECT document() FROM s;
|
||||
}
|
||||
|
||||
for {set iRep 0} {$iRep < $NREP} {incr iRep} {
|
||||
set lRowid [db eval {SELECT rowid FROM t1}]
|
||||
for {set iDel 0} {$iDel < $nDeletePerRep} {incr iDel} {
|
||||
set idx [select_one $lRowid]
|
||||
db eval {
|
||||
DELETE FROM t1 WHERE rowid=$idx
|
||||
}
|
||||
}
|
||||
db eval {
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$nDeletePerRep
|
||||
)
|
||||
INSERT INTO t1 SELECT document() FROM s;
|
||||
}
|
||||
do_execsql_test 1.2.$iRep {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
}
|
||||
|
||||
reset_db
|
||||
db func document [list get_document 12]
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(body);
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 128);
|
||||
}
|
||||
do_execsql_test 2.1 {
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$NDOC
|
||||
)
|
||||
INSERT INTO t1 SELECT document() FROM s;
|
||||
}
|
||||
for {set ii 0} {$ii < $NDOC} {incr ii} {
|
||||
set lRowid [db eval {SELECT rowid FROM t1}]
|
||||
set idx [select_one $lRowid]
|
||||
db eval { DELETE FROM t1 WHERE rowid=$idx }
|
||||
do_execsql_test 2.2.$ii {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -122,9 +122,6 @@ foreach {tn expr} {
|
||||
4.1 "NEAR(one two, 2)"
|
||||
4.2 "NEAR(one two three, 2)"
|
||||
4.3 "NEAR(eight nine, 1) OR NEAR(six seven, 1)"
|
||||
|
||||
5.1 "one + two"
|
||||
5.2 "1 + two"
|
||||
} {
|
||||
if {[fts5_expr_ok $expr ss]==0} {
|
||||
do_test 1.$tok.$tn.OMITTED { list } [list]
|
||||
|
||||
@@ -1,482 +0,0 @@
|
||||
# Quick-and-dirty makefile to bootstrap the sqlite3-jni project. This
|
||||
# build assumes a Linux-like system.
|
||||
default: all
|
||||
|
||||
JAVA_HOME ?= $(HOME)/jdk/current
|
||||
# e.g. /usr/lib/jvm/default-javajava-19-openjdk-amd64
|
||||
JDK_HOME ?= $(JAVA_HOME)
|
||||
# ^^^ JDK_HOME is not as widely used as JAVA_HOME
|
||||
bin.jar := $(JDK_HOME)/bin/jar
|
||||
bin.java := $(JDK_HOME)/bin/java
|
||||
bin.javac := $(JDK_HOME)/bin/javac
|
||||
bin.javadoc := $(JDK_HOME)/bin/javadoc
|
||||
ifeq (,$(wildcard $(JDK_HOME)))
|
||||
$(error set JDK_HOME to the top-most dir of your JDK installation.)
|
||||
endif
|
||||
MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
$(MAKEFILE):
|
||||
|
||||
package.jar := sqlite3-jni.jar
|
||||
|
||||
dir.top := ../..
|
||||
dir.tool := ../../tool
|
||||
dir.jni := $(patsubst %/,%,$(dir $(MAKEFILE)))
|
||||
dir.src := $(dir.jni)/src
|
||||
dir.src.c := $(dir.src)/c
|
||||
dir.bld := $(dir.jni)/bld
|
||||
dir.bld.c := $(dir.bld)
|
||||
dir.src.jni := $(dir.src)/org/sqlite/jni
|
||||
dir.src.fts5 := $(dir.src.jni)/fts5
|
||||
dir.tests := $(dir.src)/tests
|
||||
mkdir ?= mkdir -p
|
||||
$(dir.bld.c):
|
||||
$(mkdir) $@
|
||||
|
||||
javac.flags ?= -Xlint:unchecked -Xlint:deprecation
|
||||
java.flags ?=
|
||||
jnicheck ?= 1
|
||||
ifeq (1,$(jnicheck))
|
||||
java.flags += -Xcheck:jni
|
||||
endif
|
||||
|
||||
classpath := $(dir.src)
|
||||
CLEAN_FILES := $(package.jar)
|
||||
DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~
|
||||
|
||||
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
|
||||
.NOTPARALLEL: $(sqlite3-jni.h)
|
||||
SQLite3Jni.java := src/org/sqlite/jni/SQLite3Jni.java
|
||||
SQLTester.java := src/org/sqlite/jni/SQLTester.java
|
||||
SQLite3Jni.class := $(SQLite3Jni.java:.java=.class)
|
||||
SQLTester.class := $(SQLTester.java:.java=.class)
|
||||
|
||||
########################################################################
|
||||
# The future of FTS5 customization in this API is as yet unclear.
|
||||
# The pieces are all in place, and are all thin proxies so not much
|
||||
# complexity, but some semantic changes were required in porting
|
||||
# which are largely untested.
|
||||
#
|
||||
# Reminder: this flag influences the contents of $(sqlite3-jni.h),
|
||||
# which is checked in. Please do not check in changes to that file in
|
||||
# which the fts5 APIs have been stripped unless that feature is
|
||||
# intended to be stripped for good.
|
||||
enable.fts5 ?= 1
|
||||
|
||||
ifeq (,$(wildcard $(dir.tests)/*))
|
||||
enable.tester := 0
|
||||
else
|
||||
enable.tester := 1
|
||||
endif
|
||||
|
||||
# bin.version-info = binary to output various sqlite3 version info
|
||||
# building the distribution zip file.
|
||||
bin.version-info := $(dir.top)/version-info
|
||||
.NOTPARALLEL: $(bin.version-info)
|
||||
$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
|
||||
$(MAKE) -C $(dir.top) version-info
|
||||
|
||||
# Be explicit about which Java files to compile so that we can work on
|
||||
# in-progress files without requiring them to be in a compilable statae.
|
||||
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
|
||||
annotation/Canonical.java \
|
||||
annotation/NotNull.java \
|
||||
annotation/Nullable.java \
|
||||
AbstractCollationCallback.java \
|
||||
AggregateFunction.java \
|
||||
AuthorizerCallback.java \
|
||||
AutoExtensionCallback.java \
|
||||
BusyHandlerCallback.java \
|
||||
CollationCallback.java \
|
||||
CollationNeededCallback.java \
|
||||
CommitHookCallback.java \
|
||||
ConfigLogCallback.java \
|
||||
ConfigSqllogCallback.java \
|
||||
NativePointerHolder.java \
|
||||
OutputPointer.java \
|
||||
PrepareMultiCallback.java \
|
||||
PreupdateHookCallback.java \
|
||||
ProgressHandlerCallback.java \
|
||||
ResultCode.java \
|
||||
RollbackHookCallback.java \
|
||||
ScalarFunction.java \
|
||||
SQLFunction.java \
|
||||
CallbackProxy.java \
|
||||
SQLite3Jni.java \
|
||||
TableColumnMetadata.java \
|
||||
TraceV2Callback.java \
|
||||
UpdateHookCallback.java \
|
||||
WindowFunction.java \
|
||||
XDestroyCallback.java \
|
||||
sqlite3.java \
|
||||
sqlite3_context.java \
|
||||
sqlite3_stmt.java \
|
||||
sqlite3_value.java \
|
||||
)
|
||||
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
|
||||
Tester1.java \
|
||||
)
|
||||
ifeq (1,$(enable.fts5))
|
||||
JAVA_FILES.unittest += $(patsubst %,$(dir.src.jni)/%,\
|
||||
TesterFts5.java \
|
||||
)
|
||||
JAVA_FILES.main += $(patsubst %,$(dir.src.fts5)/%,\
|
||||
fts5_api.java \
|
||||
fts5_extension_function.java \
|
||||
fts5_tokenizer.java \
|
||||
Fts5.java \
|
||||
Fts5Context.java \
|
||||
Fts5ExtensionApi.java \
|
||||
Fts5PhraseIter.java \
|
||||
Fts5Tokenizer.java \
|
||||
XTokenizeCallback.java \
|
||||
)
|
||||
endif
|
||||
JAVA_FILES.tester := $(SQLTester.java)
|
||||
JAVA_FILES.package.info := \
|
||||
$(dir.src.jni)/package-info.java \
|
||||
$(dir.src.jni)/annotation/package-info.java
|
||||
|
||||
CLASS_FILES.main := $(JAVA_FILES.main:.java=.class)
|
||||
CLASS_FILES.unittest := $(JAVA_FILES.unittest:.java=.class)
|
||||
CLASS_FILES.tester := $(JAVA_FILES.tester:.java=.class)
|
||||
|
||||
JAVA_FILES += $(JAVA_FILES.main) $(JAVA_FILES.unittest)
|
||||
ifeq (1,$(enable.tester))
|
||||
JAVA_FILES += $(JAVA_FILES.tester)
|
||||
endif
|
||||
|
||||
CLASS_FILES :=
|
||||
define CLASSFILE_DEPS
|
||||
all: $(1).class
|
||||
CLASS_FILES += $(1).class
|
||||
endef
|
||||
$(foreach B,$(basename \
|
||||
$(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\
|
||||
$(eval $(call CLASSFILE_DEPS,$(B))))
|
||||
$(CLASS_FILES): $(JAVA_FILES) $(MAKEFILE)
|
||||
$(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES)
|
||||
|
||||
#.PHONY: classfiles
|
||||
|
||||
########################################################################
|
||||
# Set up sqlite3.c and sqlite3.h...
|
||||
#
|
||||
# To build with SEE (https://sqlite.org/see), either put sqlite3-see.c
|
||||
# in the top of this build tree or pass
|
||||
# sqlite3.c=PATH_TO_sqlite3-see.c to the build. Note that only
|
||||
# encryption modules with no 3rd-party dependencies will currently
|
||||
# work here: AES256-OFB, AES128-OFB, and AES128-CCM. Not
|
||||
# coincidentally, those 3 modules are included in the sqlite3-see.c
|
||||
# bundle.
|
||||
#
|
||||
# A custom sqlite3.c must not have any spaces in its name.
|
||||
# $(sqlite3.canonical.c) must point to the sqlite3.c in
|
||||
# the sqlite3 canonical source tree, as that source file
|
||||
# is required for certain utility and test code.
|
||||
sqlite3.canonical.c := $(firstword $(wildcard $(dir.src.c)/sqlite3.c) $(dir.top)/sqlite3.c)
|
||||
sqlite3.canonical.h := $(firstword $(wildcard $(dir.src.c)/sqlite3.h) $(dir.top)/sqlite3.h)
|
||||
sqlite3.c := $(sqlite3.canonical.c)
|
||||
sqlite3.h := $(sqlite3.canonical.h)
|
||||
#ifeq (,$(shell grep sqlite3_activate_see $(sqlite3.c) 2>/dev/null))
|
||||
# SQLITE_C_IS_SEE := 0
|
||||
#else
|
||||
# SQLITE_C_IS_SEE := 1
|
||||
# $(info This is an SEE build.)
|
||||
#endif
|
||||
|
||||
.NOTPARALLEL: $(sqlite3.h)
|
||||
$(sqlite3.h):
|
||||
$(MAKE) -C $(dir.top) sqlite3.c
|
||||
$(sqlite3.c): $(sqlite3.h)
|
||||
|
||||
opt.threadsafe ?= 1
|
||||
opt.fatal-oom ?= 1
|
||||
opt.debug ?= 1
|
||||
opt.metrics ?= 1
|
||||
SQLITE_OPT = \
|
||||
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_C=$(sqlite3.c) \
|
||||
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
|
||||
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
|
||||
|
||||
opt.extras ?= 0
|
||||
ifeq (1,$(opt.extras))
|
||||
SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
-DSQLITE_ENABLE_STMTVTAB \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB \
|
||||
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
|
||||
-DSQLITE_ENABLE_PREUPDATE_HOOK \
|
||||
-DSQLITE_ENABLE_NORMALIZE \
|
||||
-DSQLITE_ENABLE_SQLLOG
|
||||
endif
|
||||
|
||||
ifeq (1,$(opt.debug))
|
||||
SQLITE_OPT += -DSQLITE_DEBUG -g -DDEBUG -UNDEBUG
|
||||
else
|
||||
SQLITE_OPT += -Os
|
||||
endif
|
||||
|
||||
ifeq (1,$(enable.fts5))
|
||||
SQLITE_OPT += -DSQLITE_ENABLE_FTS5
|
||||
endif
|
||||
|
||||
sqlite3-jni.c := $(dir.src.c)/sqlite3-jni.c
|
||||
sqlite3-jni.o := $(dir.bld.c)/sqlite3-jni.o
|
||||
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
|
||||
package.dll := $(dir.bld.c)/libsqlite3-jni.so
|
||||
# All javac-generated .h files must be listed in $(sqlite3-jni.h.in):
|
||||
sqlite3-jni.h.in :=
|
||||
# $(java.with.jni) lists all Java files which contain JNI decls:
|
||||
java.with.jni :=
|
||||
define ADD_JNI_H
|
||||
sqlite3-jni.h.in += $$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h
|
||||
java.with.jni += $(1)/$(2).java
|
||||
$$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h: $(1)/$(2).java
|
||||
endef
|
||||
# Invoke ADD_JNI_H once for each Java file which includes JNI
|
||||
# declarations:
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.jni),SQLite3Jni,))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.jni),SQLTester,))
|
||||
ifeq (1,$(enable.fts5))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.fts5),Fts5ExtensionApi,_fts5))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_api,_fts5))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_tokenizer,_fts5))
|
||||
endif
|
||||
$(sqlite3-jni.h.in): $(dir.bld.c)
|
||||
|
||||
#package.dll.cfiles :=
|
||||
package.dll.cflags = \
|
||||
-std=c99 \
|
||||
-fPIC \
|
||||
-I. \
|
||||
-I$(dir $(sqlite3.h)) \
|
||||
-I$(dir.src.c) \
|
||||
-I$(JDK_HOME)/include \
|
||||
$(patsubst %,-I%,$(patsubst %.h,,$(wildcard $(JDK_HOME)/include/*))) \
|
||||
-Wall
|
||||
# The gross $(patsubst...) above is to include the platform-specific
|
||||
# subdir which lives under $(JDK_HOME)/include and is a required
|
||||
# include path for client-level code.
|
||||
#
|
||||
# Using (-Wall -Wextra) triggers an untennable number of
|
||||
# gcc warnings from sqlite3.c for mundane things like
|
||||
# unused parameters.
|
||||
########################################################################
|
||||
ifeq (1,$(enable.tester))
|
||||
package.dll.cflags += -DSQLITE_JNI_ENABLE_SQLTester
|
||||
endif
|
||||
|
||||
$(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
|
||||
@cat $(sqlite3-jni.h.in) > $@.tmp
|
||||
@if cmp $@ $@.tmp >/dev/null; then \
|
||||
rm -f $@.tmp; \
|
||||
echo "$@ not modified"; \
|
||||
else \
|
||||
mv $@.tmp $@; \
|
||||
echo "Updated $@"; \
|
||||
fi
|
||||
@if [ x1 != x$(enable.fts5) ]; then \
|
||||
echo "*** REMINDER:"; \
|
||||
echo "*** enable.fts5=0, so please do not check in changes to $@."; \
|
||||
fi
|
||||
|
||||
$(package.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h)
|
||||
$(package.dll): $(sqlite3-jni.c) $(MAKEFILE)
|
||||
$(CC) $(package.dll.cflags) $(SQLITE_OPT) \
|
||||
$(sqlite3-jni.c) -shared -o $@
|
||||
all: $(package.dll)
|
||||
|
||||
.PHONY: test test-one
|
||||
test.flags ?=
|
||||
test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.Tester1
|
||||
test.deps := $(CLASS_FILES) $(package.dll)
|
||||
test-one: $(test.deps)
|
||||
$(bin.java) $(test.main.flags) $(test.flags)
|
||||
test-sqllog: $(test.deps)
|
||||
@echo "Testing with -sqllog..."
|
||||
$(bin.java) $(test.main.flags) -sqllog
|
||||
test-mt: $(test.deps)
|
||||
@echo "Testing in multi-threaded mode:";
|
||||
$(bin.java) $(test.main.flags) -t 7 -r 50 -shuffle $(test.flags)
|
||||
|
||||
test: test-one test-mt
|
||||
tests: test test-sqllog
|
||||
|
||||
tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test))
|
||||
tester.flags ?= # --verbose
|
||||
.PHONY: tester tester-local tester-ext
|
||||
ifeq (1,$(enable.tester))
|
||||
tester-local: $(CLASS_FILES.tester) $(package.dll)
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.SQLTester $(tester.flags) $(tester.scripts)
|
||||
tester: tester-local
|
||||
else
|
||||
tester:
|
||||
@echo "SQLTester support is disabled."
|
||||
endif
|
||||
|
||||
tester.extdir.default := $(dir.tests)/ext
|
||||
tester.extdir ?= $(tester.extdir.default)
|
||||
tester.extern-scripts := $(wildcard $(tester.extdir)/*.test)
|
||||
ifneq (,$(tester.extern-scripts))
|
||||
tester-ext:
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.SQLTester $(tester.flags) $(tester.extern-scripts)
|
||||
else
|
||||
tester-ext:
|
||||
@echo "******************************************************"; \
|
||||
echo "*** Include the out-of-tree test suite in the 'tester'"; \
|
||||
echo "*** target by either symlinking its directory to"; \
|
||||
echo "*** $(tester.extdir.default) or passing it to make"; \
|
||||
echo "*** as tester.extdir=/path/to/that/dir."; \
|
||||
echo "******************************************************";
|
||||
endif
|
||||
|
||||
tester-ext: tester-local
|
||||
tester: tester-ext
|
||||
tests: tester
|
||||
########################################################################
|
||||
# Build each SQLITE_THREADMODE variant and run all tests against them.
|
||||
multitest: clean
|
||||
define MULTIOPT
|
||||
multitest: multitest-$(1)
|
||||
multitest-$(1):
|
||||
$$(MAKE) opt.debug=$$(opt.debug) $(patsubst %,opt.%,$(2)) \
|
||||
tests clean enable.fts5=1
|
||||
endef
|
||||
|
||||
$(eval $(call MULTIOPT,01,threadsafe=0 oom=1))
|
||||
$(eval $(call MULTIOPT,00,threadsafe=0 oom=0))
|
||||
$(eval $(call MULTIOPT,11,threadsafe=1 oom=1))
|
||||
$(eval $(call MULTIOPT,10,threadsafe=1 oom=0))
|
||||
$(eval $(call MULTIOPT,21,threadsafe=2 oom=1))
|
||||
$(eval $(call MULTIOPT,20,threadsafe=2 oom=0))
|
||||
|
||||
|
||||
########################################################################
|
||||
# jar bundle...
|
||||
package.jar.in := $(abspath $(dir.src)/jar.in)
|
||||
CLEAN_FILES += $(package.jar.in)
|
||||
JAVA_FILES.jar := $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.package.info)
|
||||
CLASS_FILES.jar := $(filter-out %/package-info.class,$(JAVA_FILES.jar:.java=.class))
|
||||
$(package.jar.in): $(package.dll) $(MAKEFILE)
|
||||
ls -1 \
|
||||
$(dir.src.jni)/*.java $(dir.src.jni)/*.class \
|
||||
$(dir.src.jni)/annotation/*.java $(dir.src.jni)/annotation/*.class \
|
||||
| sed -e 's,^$(dir.src)/,,' | sort > $@
|
||||
|
||||
$(package.jar): $(CLASS_FILES.jar) $(MAKEFILE) $(package.jar.in)
|
||||
@rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~
|
||||
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.Tester1 @$(package.jar.in)
|
||||
@ls -la $@
|
||||
@echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag."
|
||||
@echo "e.g. java -Djava.library.path=bld -jar $@"
|
||||
|
||||
jar: $(package.jar)
|
||||
run-jar: $(package.jar) $(package.dll)
|
||||
$(bin.java) -Djava.library.path=$(dir.bld) -jar $(package.jar) $(run-jar.flags)
|
||||
|
||||
########################################################################
|
||||
# javadoc...
|
||||
dir.doc := $(dir.jni)/javadoc
|
||||
doc.index := $(dir.doc)/index.html
|
||||
javadoc.exclude := -exclude org.sqlite.jni.fts5
|
||||
# ^^^^ 2023-09-13: elide the fts5 parts from the public docs for
|
||||
# the time being, as it's not clear where the Java bindings for
|
||||
# those bits are going.
|
||||
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
|
||||
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
|
||||
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
|
||||
-subpackages org.sqlite.jni $(javadoc.exclude)
|
||||
@echo "javadoc output is in $@"
|
||||
|
||||
.PHONY: doc javadoc docserve
|
||||
.FORCE: doc
|
||||
doc: $(doc.index)
|
||||
javadoc: $(doc.index)
|
||||
# Force rebild of docs
|
||||
redoc:
|
||||
@rm -f $(doc.index)
|
||||
@$(MAKE) doc
|
||||
docserve: $(doc.index)
|
||||
cd $(dir.doc) && althttpd -max-age 1 -page index.html
|
||||
########################################################################
|
||||
# Clean up...
|
||||
CLEAN_FILES += $(dir.bld.c)/* \
|
||||
$(dir.src.jni)/*.class \
|
||||
$(dir.src.jni.tester)/*.class \
|
||||
$(package.dll) \
|
||||
hs_err_pid*.log
|
||||
|
||||
.PHONY: clean distclean
|
||||
clean:
|
||||
-rm -f $(CLEAN_FILES)
|
||||
distclean: clean
|
||||
-rm -f $(DISTCLEAN_FILES)
|
||||
-rm -fr $(dir.bld.c) $(dir.doc)
|
||||
|
||||
########################################################################
|
||||
# disttribution bundle rules...
|
||||
|
||||
ifeq (,$(filter snapshot,$(MAKECMDGOALS)))
|
||||
dist-name-prefix := sqlite-jni
|
||||
else
|
||||
dist-name-prefix := sqlite-jni-snapshot-$(shell /usr/bin/date +%Y%m%d)
|
||||
endif
|
||||
dist-name := $(dist-name-prefix)-TEMP
|
||||
|
||||
|
||||
dist-dir.top := $(dist-name)
|
||||
dist-dir.src := $(dist-dir.top)/src
|
||||
dist.top.extras := \
|
||||
README.md
|
||||
|
||||
.PHONY: dist snapshot
|
||||
|
||||
dist: \
|
||||
$(bin.version-info) $(sqlite3.canonical.c) \
|
||||
$(package.jar) $(MAKEFILE)
|
||||
@echo "Making end-user deliverables..."
|
||||
@echo "****************************************************************************"; \
|
||||
echo "*** WARNING: be sure to build this with JDK8 (javac 1.8) for compatibility."; \
|
||||
echo "*** reasons!"; $$($(bin.javac) -version); \
|
||||
echo "****************************************************************************"
|
||||
@rm -fr $(dist-dir.top)
|
||||
@mkdir -p $(dist-dir.src)
|
||||
@cp -p $(dist.top.extras) $(dist-dir.top)/.
|
||||
@cp -p jar-dist.make $(dist-dir.top)/Makefile
|
||||
@cp -p $(dir.src.c)/*.[ch] $(dist-dir.src)/.
|
||||
@cp -p $(sqlite3.canonical.c) $(sqlite3.canonical.h) $(dist-dir.src)/.
|
||||
@set -e; \
|
||||
vnum=$$($(bin.version-info) --download-version); \
|
||||
vjar=$$($(bin.version-info) --version); \
|
||||
vdir=$(dist-name-prefix)-$$vnum; \
|
||||
arczip=$$vdir.zip; \
|
||||
cp -p $(package.jar) $(dist-dir.top)/sqlite3-jni-$${vjar}.jar; \
|
||||
echo "Making $$arczip ..."; \
|
||||
rm -fr $$arczip $$vdir; \
|
||||
mv $(dist-dir.top) $$vdir; \
|
||||
zip -qr $$arczip $$vdir; \
|
||||
rm -fr $$vdir; \
|
||||
ls -la $$arczip; \
|
||||
set +e; \
|
||||
unzip -lv $$arczip || echo "Missing unzip app? Not fatal."
|
||||
|
||||
snapshot: dist
|
||||
|
||||
.PHONY: dist-clean
|
||||
clean: dist-clean
|
||||
dist-clean:
|
||||
rm -fr $(dist-name) $(wildcard sqlite-jni-*.zip)
|
||||
@@ -1,313 +0,0 @@
|
||||
SQLite3 via JNI
|
||||
========================================================================
|
||||
|
||||
This directory houses a Java Native Interface (JNI) binding for the
|
||||
sqlite3 API. If you are reading this from the distribution ZIP file,
|
||||
links to resources in the canonical source tree will note work. The
|
||||
canonical copy of this file can be browsed at:
|
||||
|
||||
<https://sqlite.org/src/doc/trunk/ext/jni/README.md>
|
||||
|
||||
Technical support is available in the forum:
|
||||
|
||||
<https://sqlite.org/forum>
|
||||
|
||||
|
||||
> **FOREWARNING:** this subproject is very much in development and
|
||||
subject to any number of changes. Please do not rely on any
|
||||
information about its API until this disclaimer is removed. The JNI
|
||||
bindings released with version 3.43 are a "tech preview" and 3.44
|
||||
will be "final," at which point strong backward compatibility
|
||||
guarantees will apply.
|
||||
|
||||
Project goals/requirements:
|
||||
|
||||
- A [1-to-1(-ish) mapping of the C API](#1to1ish) to Java via JNI,
|
||||
insofar as cross-language semantics allow for. A closely-related
|
||||
goal is that [the C documentation](https://sqlite.org/c3ref/intro.html)
|
||||
should be usable as-is, insofar as possible, for the JNI binding.
|
||||
|
||||
- Support Java as far back as version 8 (2014).
|
||||
|
||||
- Environment-independent. Should work everywhere both Java
|
||||
and SQLite3 do.
|
||||
|
||||
- No 3rd-party dependencies beyond the JDK. That includes no
|
||||
build-level dependencies for specific IDEs and toolchains. We
|
||||
welcome the addition of build files for arbitrary environments
|
||||
insofar as they neither interfere with each other nor become
|
||||
a maintenance burden for the sqlite developers.
|
||||
|
||||
Non-goals:
|
||||
|
||||
- Creation of high-level OO wrapper APIs. Clients are free to create
|
||||
them off of the C-style API.
|
||||
|
||||
- Support for mixed-mode operation, where client code accesses SQLite
|
||||
both via the Java-side API and the C API via their own native
|
||||
code. In such cases, proxy functionalities (primarily callback
|
||||
handler wrappers of all sorts) may fail because the C-side use of
|
||||
the SQLite APIs will bypass those proxies.
|
||||
|
||||
|
||||
Hello World
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
```java
|
||||
import org.sqlite.jni.*;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
|
||||
...
|
||||
|
||||
final sqlite3 db = sqlite3_open(":memory:");
|
||||
try {
|
||||
final int rc = sqlite3_errcode(db);
|
||||
if( 0 != rc ){
|
||||
if( null != db ){
|
||||
System.out.print("Error opening db: "+sqlite3_errmsg(db));
|
||||
}else{
|
||||
System.out.print("Error opening db: rc="+rc);
|
||||
}
|
||||
... handle error ...
|
||||
}
|
||||
// ... else use the db ...
|
||||
}finally{
|
||||
// ALWAYS close databases using sqlite3_close() or sqlite3_close_v2()
|
||||
// when done with them. All of their active statement handles must
|
||||
// first have been passed to sqlite3_finalize().
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Building
|
||||
========================================================================
|
||||
|
||||
The canonical builds assumes a Linux-like environment and requires:
|
||||
|
||||
- GNU Make
|
||||
- A JDK supporting Java 8 or higher
|
||||
- A modern C compiler. gcc and clang should both work.
|
||||
|
||||
Put simply:
|
||||
|
||||
```console
|
||||
$ export JAVA_HOME=/path/to/jdk/root
|
||||
$ make
|
||||
$ make test
|
||||
$ make clean
|
||||
```
|
||||
|
||||
The jar distribution can be created with `make jar`, but note that it
|
||||
does not contain the binary DLL file. A different DLL is needed for
|
||||
each target platform.
|
||||
|
||||
|
||||
<a id='1to1ish'></a>
|
||||
One-to-One(-ish) Mapping to C
|
||||
========================================================================
|
||||
|
||||
This JNI binding aims to provide as close to a 1-to-1 experience with
|
||||
the C API as cross-language semantics allow. Interface changes are
|
||||
necessarily made where cross-language semantics do not allow a 1-to-1,
|
||||
and judiciously made where a 1-to-1 mapping would be unduly cumbersome
|
||||
to use in Java. In all cases, this binding makes every effort to
|
||||
provide semantics compatible with the C API documentation even if the
|
||||
interface to those semantics is slightly different. Any cases which
|
||||
deviate from those semantics (either removing or adding semantics) are
|
||||
clearly documented.
|
||||
|
||||
Where it makes sense to do so for usability, Java-side overloads are
|
||||
provided which accept or return data in alternative forms or provide
|
||||
sensible default argument values. In all such cases they are thin
|
||||
proxies around the corresponding C APIs and do not introduce new
|
||||
semantics.
|
||||
|
||||
In some very few cases, Java-specific capabilities have been added in
|
||||
new APIs, all of which have "_java" somewhere in their names.
|
||||
Examples include:
|
||||
|
||||
- `sqlite3_result_java_object()`
|
||||
- `sqlite3_column_java_object()`
|
||||
- `sqlite3_column_java_casted()`
|
||||
- `sqlite3_value_java_object()`
|
||||
- `sqlite3_value_java_casted()`
|
||||
|
||||
which, as one might surmise, collectively enable the passing of
|
||||
arbitrary Java objects from user-defined SQL functions through to the
|
||||
caller.
|
||||
|
||||
|
||||
Golden Rule: Garbage Collection Cannot Free SQLite Resources
|
||||
------------------------------------------------------------------------
|
||||
|
||||
It is important that all databases and prepared statement handles get
|
||||
cleaned up by client code. A database cannot be closed if it has open
|
||||
statement handles. `sqlite3_close()` fails if the db cannot be closed
|
||||
whereas `sqlite3_close_v2()` recognizes that case and marks the db as
|
||||
a "zombie," pending finalization when the library detects that all
|
||||
pending statements have been closed. Be aware that Java garbage
|
||||
collection _cannot_ close a database or finalize a prepared statement.
|
||||
Those things require explicit API calls.
|
||||
|
||||
|
||||
Golden Rule #2: _Never_ Throw from Callbacks (Unless...)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
All routines in this API, barring explicitly documented exceptions,
|
||||
retain C-like semantics. For example, they are not permitted to throw
|
||||
or propagate exceptions and must return error information (if any) via
|
||||
result codes or `null`. The only cases where the C-style APIs may
|
||||
throw is through client-side misuse, e.g. passing in a null where it
|
||||
shouldn't be used. The APIs clearly mark function parameters which
|
||||
should not be null, but does not actively defend itself against such
|
||||
misuse. Some C-style APIs explicitly accept `null` as a no-op for
|
||||
usability's sake, and some of the JNI APIs deliberately return an
|
||||
error code, instead of segfaulting, when passed a `null`.
|
||||
|
||||
Client-defined callbacks _must never throw exceptions_ unless _very
|
||||
explicitly documented_ as being throw-safe. Exceptions are generally
|
||||
reserved for higher-level bindings which are constructed to
|
||||
specifically deal with them and ensure that they do not leak C-level
|
||||
resources. In some cases, callback handlers are permitted to throw, in
|
||||
which cases they get translated to C-level result codes and/or
|
||||
messages. If a callback which is not permitted to throw throws, its
|
||||
exception may trigger debug output but will otherwise be suppressed.
|
||||
|
||||
The reason some callbacks are permitted to throw and others not is
|
||||
because all such callbacks act as proxies for C function callback
|
||||
interfaces and some of those interfaces have no error-reporting
|
||||
mechanism. Those which are capable of propagating errors back through
|
||||
the library convert exceptions from callbacks into corresponding
|
||||
C-level error information. Those which cannot propagate errors
|
||||
necessarily suppress any exceptions in order to maintain the C-style
|
||||
semantics of the APIs.
|
||||
|
||||
|
||||
Unwieldy Constructs are Re-mapped
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Some constructs, when modelled 1-to-1 from C to Java, are unduly
|
||||
clumsy to work with in Java because they try to shoehorn C's way of
|
||||
doing certain things into Java's wildly different ways. The following
|
||||
subsections cover those, starting with a verbose explanation and
|
||||
demonstration of where such changes are "really necessary"...
|
||||
|
||||
### Custom Collations
|
||||
|
||||
A prime example of where interface changes for Java are necessary for
|
||||
usability is [registration of a custom
|
||||
collation](https://sqlite.org/c3ref/create_collation.html):
|
||||
|
||||
```c
|
||||
// C:
|
||||
int sqlite3_create_collation(sqlite3 * db, const char * name, int eTextRep,
|
||||
void *pUserData,
|
||||
int (*xCompare)(void*,int,void const *,int,void const *));
|
||||
|
||||
int sqlite3_create_collation_v2(sqlite3 * db, const char * name, int eTextRep,
|
||||
void *pUserData,
|
||||
int (*xCompare)(void*,int,void const *,int,void const *),
|
||||
void (*xDestroy)(void*));
|
||||
```
|
||||
|
||||
The `pUserData` object is optional client-defined state for the
|
||||
`xCompare()` and/or `xDestroy()` callback functions, both of which are
|
||||
passed that object as their first argument. That data is passed around
|
||||
"externally" in C because that's how C models the world. If we were to
|
||||
bind that part as-is to Java, the result would be awkward to use (^Yes,
|
||||
we tried this.):
|
||||
|
||||
```java
|
||||
// Java:
|
||||
int sqlite3_create_collation(sqlite3 db, String name, int eTextRep,
|
||||
Object pUserData, xCompareType xCompare);
|
||||
|
||||
int sqlite3_create_collation_v2(sqlite3 db, String name, int eTextRep,
|
||||
Object pUserData,
|
||||
xCompareType xCompare, xDestroyType xDestroy);
|
||||
```
|
||||
|
||||
The awkwardness comes from (A) having two distinctly different objects
|
||||
for callbacks and (B) having their internal state provided separately,
|
||||
which is ill-fitting in Java. For the sake of usability, C APIs which
|
||||
follow that pattern use a slightly different Java interface:
|
||||
|
||||
```java
|
||||
int sqlite3_create_collation(sqlite3 db, String name, int eTextRep,
|
||||
SomeCallbackType collation);
|
||||
```
|
||||
|
||||
Where the `Collation` class has an abstract `call()` method and
|
||||
no-op `xDestroy()` method which can be overridden if needed, leading to
|
||||
a much more Java-esque usage:
|
||||
|
||||
```java
|
||||
int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new SomeCallbackType(){
|
||||
|
||||
// Required comparison function:
|
||||
@Override public int call(byte[] lhs, byte[] rhs){ ... }
|
||||
|
||||
// Optional finalizer function:
|
||||
@Override public void xDestroy(){ ... }
|
||||
|
||||
// Optional local state:
|
||||
private String localState1 =
|
||||
"This is local state. There are many like it, but this one is mine.";
|
||||
private MyStateType localState2 = new MyStateType();
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
Noting that:
|
||||
|
||||
- It is possible to bind in call-scope-local state via closures, if
|
||||
desired, as opposed to packing it into the Collation object.
|
||||
|
||||
- No capabilities of the C API are lost or unduly obscured via the
|
||||
above API reshaping, so power users need not make any compromises.
|
||||
|
||||
- In the specific example above, `sqlite3_create_collation_v2()`
|
||||
becomes superfluous because the provided interface effectively
|
||||
provides both the v1 and v2 interfaces, the difference being that
|
||||
overriding the `xDestroy()` method effectively gives it v2
|
||||
semantics.
|
||||
|
||||
|
||||
### User-defined SQL Functions (a.k.a. UDFs)
|
||||
|
||||
The [`sqlite3_create_function()`](https://sqlite.org/c3ref/create_function.html)
|
||||
family of APIs make heavy use of function pointers to provide
|
||||
client-defined callbacks, necessitating interface changes in the JNI
|
||||
binding. The Java API has only one core function-registration function:
|
||||
|
||||
```java
|
||||
int sqlite3_create_function(sqlite3 db, String funcName, int nArgs,
|
||||
int encoding, SQLFunction func);
|
||||
```
|
||||
|
||||
> Design question: does the encoding argument serve any purpose in
|
||||
Java? That's as-yet undetermined. If not, it will be removed.
|
||||
|
||||
`SQLFunction` is not used directly, but is instead instantiated via
|
||||
one of its three subclasses:
|
||||
|
||||
- `SQLFunction.Scalar` implements simple scalar functions using but a
|
||||
single callback.
|
||||
- `SQLFunction.Aggregate` implements aggregate functions using two
|
||||
callbacks.
|
||||
- `SQLFunction.Window` implements window functions using four
|
||||
callbacks.
|
||||
|
||||
Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/Tester1.java) for
|
||||
`SQLFunction` for how it's used.
|
||||
|
||||
Reminder: see the disclaimer at the top of this document regarding the
|
||||
in-flux nature of this API.
|
||||
|
||||
### And so on...
|
||||
|
||||
Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and
|
||||
`sqlite3_update_hook()`, use interfaces similar to those shown above.
|
||||
Despite the changes in signature, the JNI layer makes every effort to
|
||||
provide the same semantics as the C API documentation suggests.
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/this/is/make
|
||||
#^^^^ help emacs out
|
||||
#
|
||||
# This is a POSIX-make-compatible makefile for building the sqlite3
|
||||
# JNI library from "dist" zip file. It must be edited to set the
|
||||
# proper top-level JDK directory and, depending on the platform, add a
|
||||
# platform-specific -I directory. It should build as-is with any
|
||||
# 2020s-era version of gcc or clang. It requires JDK version 8 or
|
||||
# higher and that JAVA_HOME points to the top-most installation
|
||||
# directory of that JDK. On Ubuntu-style systems the JDK is typically
|
||||
# installed under /usr/lib/jvm/java-VERSION-PLATFORM.
|
||||
|
||||
default: all
|
||||
|
||||
JAVA_HOME = /usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
CFLAGS = \
|
||||
-fPIC \
|
||||
-Isrc \
|
||||
-I$(JAVA_HOME)/include \
|
||||
-I$(JAVA_HOME)/include/linux \
|
||||
-I$(JAVA_HOME)/include/apple \
|
||||
-I$(JAVA_HOME)/include/bsd \
|
||||
-Wall
|
||||
|
||||
SQLITE_OPT = \
|
||||
-DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
-DSQLITE_ENABLE_STMTVTAB \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB \
|
||||
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_THREADSAFE=1 \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_ENABLE_FTS5 \
|
||||
-DSQLITE_DEBUG
|
||||
|
||||
sqlite3-jni.dll = libsqlite3-jni.so
|
||||
$(sqlite3-jni.dll):
|
||||
@echo "************************************************************************"; \
|
||||
echo "*** If this fails to build, be sure to edit this makefile ***"; \
|
||||
echo "*** to configure it for your system. ***"; \
|
||||
echo "************************************************************************"
|
||||
$(CC) $(CFLAGS) $(SQLITE_OPT) \
|
||||
src/sqlite3-jni.c -shared -o $@
|
||||
@echo "Now try running it with: make test"
|
||||
|
||||
test.flags = -Djava.library.path=. sqlite3-jni-*.jar
|
||||
test: $(sqlite3-jni.dll)
|
||||
java -jar $(test.flags)
|
||||
java -jar $(test.flags) -t 7 -r 10 -shuffle
|
||||
|
||||
clean:
|
||||
-rm -f $(sqlite3-jni.dll)
|
||||
|
||||
all: $(sqlite3-jni.dll)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import org.sqlite.jni.annotation.NotNull;
|
||||
|
||||
/**
|
||||
An implementation of {@link CollationCallback} which provides a
|
||||
no-op xDestroy() method.
|
||||
*/
|
||||
public abstract class AbstractCollationCallback
|
||||
implements CollationCallback, XDestroyCallback {
|
||||
/**
|
||||
Must compare the given byte arrays and return the result using
|
||||
{@code memcmp()} semantics.
|
||||
*/
|
||||
public abstract int call(@NotNull byte[] lhs, @NotNull byte[] rhs);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite. This implementation does nothing.
|
||||
*/
|
||||
public void xDestroy(){}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
|
||||
/**
|
||||
A SQLFunction implementation for aggregate functions. Its T is the
|
||||
data type of its "accumulator" state, an instance of which is
|
||||
intended to be be managed using the getAggregateState() and
|
||||
takeAggregateState() methods.
|
||||
*/
|
||||
public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
|
||||
/**
|
||||
As for the xStep() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, the
|
||||
exception is not propagated and a warning might be emitted to a
|
||||
debugging channel.
|
||||
*/
|
||||
public abstract void xStep(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
/**
|
||||
As for the xFinal() argument of the C API's sqlite3_create_function().
|
||||
If this function throws, it is translated into an sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xFinal(sqlite3_context cx);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final SQLFunction.PerContextState<T> map =
|
||||
new SQLFunction.PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
as the xValue() and xInverse() methods of the {@link WindowFunction}
|
||||
subclass, to fetch the current per-call UDF state. On the
|
||||
first call to this method for any given sqlite3_context
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see SQLFunction.PerContextState#getAggregateState
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
return map.getAggregateState(cx, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
To be called from the implementation's xFinal() method to fetch
|
||||
the final state of the UDF and remove its mapping.
|
||||
|
||||
see SQLFunction.PerContextState#takeAggregateState
|
||||
*/
|
||||
protected final T takeAggregateState(sqlite3_context cx){
|
||||
return map.takeAggregateState(cx);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_set_authorizer}.
|
||||
*/
|
||||
public interface AuthorizerCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level
|
||||
sqlite3_set_authorizer() callback.
|
||||
*/
|
||||
int call(int opId, @Nullable String s1, @Nullable String s2,
|
||||
@Nullable String s3, @Nullable String s4);
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with the {@link SQLite3Jni#sqlite3_auto_extension}
|
||||
family of APIs.
|
||||
*/
|
||||
public interface AutoExtensionCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for a C-level
|
||||
sqlite3_auto_extension() callback.
|
||||
|
||||
<p>This callback may throw and the exception's error message will
|
||||
be set as the db's error string.
|
||||
|
||||
<p>Tips for implementations:
|
||||
|
||||
<p>- Opening a database from an auto-extension handler will lead to
|
||||
an endless recursion of the auto-handler triggering itself
|
||||
indirectly for each newly-opened database.
|
||||
|
||||
<p>- If this routine is stateful, it may be useful to make the
|
||||
overridden method synchronized.
|
||||
|
||||
<p>- Results are undefined if the given db is closed by an auto-extension.
|
||||
*/
|
||||
int call(sqlite3 db);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_busy_handler}.
|
||||
*/
|
||||
public interface BusyHandlerCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as documented for the C-level
|
||||
sqlite3_busy_handler() callback argument, minus the (void*)
|
||||
argument the C-level function requires.
|
||||
*/
|
||||
int call(int n);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
/**
|
||||
This marker interface exists soley for use as a documentation and
|
||||
class-grouping tool. It should be applied to interfaces or
|
||||
classes which have a call() method implementing some specific
|
||||
callback interface on behalf of the C library.
|
||||
|
||||
<p>Unless very explicitely documented otherwise, callbacks must
|
||||
never throw. Any which do throw but should not might trigger debug
|
||||
output regarding the error, but the exception will not be
|
||||
propagated. For callback interfaces which support returning error
|
||||
info to the core, the JNI binding will convert any exceptions to
|
||||
C-level error information. For callback interfaces which do not
|
||||
support, all exceptions will necessarily be suppressed in order to
|
||||
retain the C-style no-throw semantics.
|
||||
|
||||
<p>Callbacks of this style follow a common naming convention:
|
||||
|
||||
<p>1) They use the UpperCamelCase form of the C function they're
|
||||
proxying for, minus the {@code sqlite3_} prefix, plus a {@code
|
||||
Callback} suffix. e.g. {@code sqlite3_busy_handler()}'s callback is
|
||||
named {@code BusyHandlerCallback}. Exceptions are made where that
|
||||
would potentially be ambiguous, e.g. {@link ConfigSqllogCallback}
|
||||
instead of {@code ConfigCallback} because the {@code
|
||||
sqlite3_config()} interface may need to support more callback types
|
||||
in the future.
|
||||
|
||||
<p>2) They all have a {@code call()} method but its signature is
|
||||
callback-specific.
|
||||
*/
|
||||
public interface CallbackProxy {}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import org.sqlite.jni.annotation.NotNull;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_create_collation}.
|
||||
|
||||
@see AbstractCollationCallback
|
||||
*/
|
||||
public interface CollationCallback
|
||||
extends CallbackProxy, XDestroyCallback {
|
||||
/**
|
||||
Must compare the given byte arrays and return the result using
|
||||
{@code memcmp()} semantics.
|
||||
*/
|
||||
int call(@NotNull byte[] lhs, @NotNull byte[] rhs);
|
||||
|
||||
/**
|
||||
Called by SQLite when the collation is destroyed. If a collation
|
||||
requires custom cleanup, override this method.
|
||||
*/
|
||||
void xDestroy();
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_collation_needed}.
|
||||
*/
|
||||
public interface CollationNeededCallback extends CallbackProxy {
|
||||
/**
|
||||
Has the same semantics as the C-level sqlite3_create_collation()
|
||||
callback.
|
||||
|
||||
<p>If it throws, the exception message is passed on to the db and
|
||||
the exception is suppressed.
|
||||
*/
|
||||
int call(sqlite3 db, int eTextRep, String collationName);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_commit_hook}.
|
||||
*/
|
||||
public interface CommitHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_commit_hook()
|
||||
callback. Must not throw.
|
||||
*/
|
||||
int call();
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
** 2023-08-23
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_config().
|
||||
*/
|
||||
public interface ConfigLogCallback {
|
||||
/**
|
||||
Must function as described for a C-level callback for
|
||||
{@link SQLite3Jni#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
|
||||
*/
|
||||
void call(int errCode, String msg);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
** 2023-08-23
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_config().
|
||||
*/
|
||||
public interface ConfigSqllogCallback {
|
||||
/**
|
||||
Must function as described for a C-level callback for
|
||||
{@link SQLite3Jni#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change.
|
||||
*/
|
||||
void call(sqlite3 db, String msg, int msgType );
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A helper for passing pointers between JNI C code and Java, in
|
||||
particular for output pointers of high-level object types in the
|
||||
sqlite3 C API, e.g. (sqlite3**) and (sqlite3_stmt**). This is
|
||||
intended to be subclassed and the ContextType is intended to be the
|
||||
class which is doing the subclassing. The intent of the ContextType
|
||||
is strictly to provide some level of type safety by avoiding that
|
||||
NativePointerHolder is not inadvertently passed to an incompatible
|
||||
function signature.
|
||||
|
||||
These objects do not own the pointer they refer to. They are
|
||||
intended simply to communicate that pointer between C and Java.
|
||||
*/
|
||||
public class NativePointerHolder<ContextType> {
|
||||
//! Only set from JNI, where access permissions don't matter.
|
||||
private volatile long nativePointer = 0;
|
||||
/**
|
||||
For use ONLY by package-level APIs which act as proxies for
|
||||
close/finalize operations. Such ops must call this to zero out
|
||||
the pointer so that this object is not carrying a stale
|
||||
pointer. This function returns the prior value of the pointer and
|
||||
sets it to 0.
|
||||
*/
|
||||
final long clearNativePointer() {
|
||||
final long rv = nativePointer;
|
||||
nativePointer= 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
public final long getNativePointer(){ return nativePointer; }
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Helper classes for handling JNI output pointers.
|
||||
|
||||
<p>We do not use a generic OutputPointer<T> because working with those
|
||||
from the native JNI code is unduly quirky due to a lack of
|
||||
autoboxing at that level.
|
||||
|
||||
<p>The usage is similar for all of thes types:
|
||||
|
||||
<pre>{@code
|
||||
OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
assert( null==out.get() );
|
||||
int rc = sqlite3_open(":memory:", out);
|
||||
if( 0!=rc ) ... error;
|
||||
assert( null!=out.get() );
|
||||
sqlite3 db = out.take();
|
||||
assert( null==out.get() );
|
||||
}</pre>
|
||||
|
||||
<p>With the minor exception that the primitive types permit direct
|
||||
access to the object's value via the `value` property, whereas the
|
||||
JNI-level opaque types do not permit client-level code to set that
|
||||
property.
|
||||
|
||||
<p>Warning: do not share instances of these classes across
|
||||
threads. Doing so may lead to corrupting sqlite3-internal state.
|
||||
*/
|
||||
public final class OutputPointer {
|
||||
|
||||
/**
|
||||
Output pointer for use with routines, such as sqlite3_open(),
|
||||
which return a database handle via an output pointer. These
|
||||
pointers can only be set by the JNI layer, not by client-level
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3 {
|
||||
private org.sqlite.jni.sqlite3 value;
|
||||
/** Initializes with a null value. */
|
||||
public sqlite3(){value = null;}
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.sqlite3 get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.sqlite3 take(){
|
||||
final org.sqlite.jni.sqlite3 v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for sqlite3_blob_open(). These
|
||||
pointers can only be set by the JNI layer, not by client-level
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_blob {
|
||||
private org.sqlite.jni.sqlite3_blob value;
|
||||
/** Initializes with a null value. */
|
||||
public sqlite3_blob(){value = null;}
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.sqlite3_blob get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.sqlite3_blob take(){
|
||||
final org.sqlite.jni.sqlite3_blob v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with routines, such as sqlite3_prepare(),
|
||||
which return a statement handle via an output pointer. These
|
||||
pointers can only be set by the JNI layer, not by client-level
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_stmt {
|
||||
private org.sqlite.jni.sqlite3_stmt value;
|
||||
/** Initializes with a null value. */
|
||||
public sqlite3_stmt(){value = null;}
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.sqlite3_stmt get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.sqlite3_stmt take(){
|
||||
final org.sqlite.jni.sqlite3_stmt v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with routines, such as sqlite3_prepupdate_new(),
|
||||
which return a sqlite3_value handle via an output pointer. These
|
||||
pointers can only be set by the JNI layer, not by client-level
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_value {
|
||||
private org.sqlite.jni.sqlite3_value value;
|
||||
/** Initializes with a null value. */
|
||||
public sqlite3_value(){value = null;}
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.sqlite3_value get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.sqlite3_value take(){
|
||||
final org.sqlite.jni.sqlite3_value v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return booleans
|
||||
via integer output pointers.
|
||||
*/
|
||||
public static final class Bool {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public boolean value;
|
||||
/** Initializes with the value 0. */
|
||||
public Bool(){this(false);}
|
||||
/** Initializes with the value v. */
|
||||
public Bool(boolean v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final boolean get(){return value;}
|
||||
/** Sets the current value to v. */
|
||||
public final void set(boolean v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return integers via
|
||||
output pointers.
|
||||
*/
|
||||
public static final class Int32 {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public int value;
|
||||
/** Initializes with the value 0. */
|
||||
public Int32(){this(0);}
|
||||
/** Initializes with the value v. */
|
||||
public Int32(int v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final int get(){return value;}
|
||||
/** Sets the current value to v. */
|
||||
public final void set(int v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return 64-bit integers
|
||||
via output pointers.
|
||||
*/
|
||||
public static final class Int64 {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public long value;
|
||||
/** Initializes with the value 0. */
|
||||
public Int64(){this(0);}
|
||||
/** Initializes with the value v. */
|
||||
public Int64(long v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final long get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(long v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return strings via
|
||||
output pointers.
|
||||
*/
|
||||
public static final class String {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public java.lang.String value;
|
||||
/** Initializes with a null value. */
|
||||
public String(){this(null);}
|
||||
/** Initializes with the value v. */
|
||||
public String(java.lang.String v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final java.lang.String get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(java.lang.String v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return byte
|
||||
arrays via output pointers.
|
||||
*/
|
||||
public static final class ByteArray {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public byte[] value;
|
||||
/** Initializes with the value null. */
|
||||
public ByteArray(){this(null);}
|
||||
/** Initializes with the value v. */
|
||||
public ByteArray(byte[] v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final byte[] get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(byte[] v){value = v;}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
** 2023-09-13
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_prepare_multi}.
|
||||
*/
|
||||
public interface PrepareMultiCallback extends CallbackProxy {
|
||||
|
||||
/**
|
||||
Gets passed a sqlite3_stmt which it may handle in arbitrary ways,
|
||||
transfering ownership of it to this function.
|
||||
|
||||
sqlite3_prepare_multi() will _not_ finalize st - it is up
|
||||
to the call() implementation how st is handled.
|
||||
|
||||
Must return 0 on success or an SQLITE_... code on error.
|
||||
|
||||
See the {@link Finalize} class for a wrapper which finalizes the
|
||||
statement after calling a proxy PrepareMultiCallback.
|
||||
*/
|
||||
int call(sqlite3_stmt st);
|
||||
|
||||
/**
|
||||
A PrepareMultiCallback impl which wraps a separate impl and finalizes
|
||||
any sqlite3_stmt passed to its callback.
|
||||
*/
|
||||
public static final class Finalize implements PrepareMultiCallback {
|
||||
private PrepareMultiCallback p;
|
||||
/**
|
||||
p is the proxy to call() when this.call() is called.
|
||||
*/
|
||||
public Finalize( PrepareMultiCallback p ){
|
||||
this.p = p;
|
||||
}
|
||||
/**
|
||||
Calls the call() method of the proxied callback and either returns its
|
||||
result or propagates an exception. Either way, it passes its argument to
|
||||
sqlite3_finalize() before returning.
|
||||
*/
|
||||
@Override public int call(sqlite3_stmt st){
|
||||
try {
|
||||
return this.p.call(st);
|
||||
}finally{
|
||||
SQLite3Jni.sqlite3_finalize(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A PrepareMultiCallback impl which steps entirely through a result set,
|
||||
ignoring all non-error results.
|
||||
*/
|
||||
public static final class StepAll implements PrepareMultiCallback {
|
||||
public StepAll(){}
|
||||
/**
|
||||
Calls sqlite3_step() on st until it returns something other than
|
||||
SQLITE_ROW. If the final result is SQLITE_DONE then 0 is returned,
|
||||
else the result of the final step is returned.
|
||||
*/
|
||||
@Override public int call(sqlite3_stmt st){
|
||||
int rc = SQLite3Jni.SQLITE_DONE;
|
||||
while( SQLite3Jni.SQLITE_ROW == (rc = SQLite3Jni.sqlite3_step(st)) ){}
|
||||
return SQLite3Jni.SQLITE_DONE==rc ? 0 : rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_preupdate_hook}.
|
||||
*/
|
||||
public interface PreupdateHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level sqlite3_preupdate_hook()
|
||||
callback.
|
||||
*/
|
||||
void call(sqlite3 db, int op, String dbName, String dbTable,
|
||||
long iKey1, long iKey2 );
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_progress_handler}.
|
||||
*/
|
||||
public interface ProgressHandlerCallback extends CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_progress_handler() callback.
|
||||
|
||||
<p>If it throws, the exception message is passed on to the db and
|
||||
the exception is suppressed.
|
||||
*/
|
||||
int call();
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
This enum contains all of the core and "extended" result codes used
|
||||
by the sqlite3 library. It is provided not for use with the C-style
|
||||
API (with which it won't work) but for higher-level code which may
|
||||
find it useful to map SQLite result codes to human-readable names.
|
||||
*/
|
||||
public enum ResultCode {
|
||||
SQLITE_OK(SQLite3Jni.SQLITE_OK),
|
||||
SQLITE_ERROR(SQLite3Jni.SQLITE_ERROR),
|
||||
SQLITE_INTERNAL(SQLite3Jni.SQLITE_INTERNAL),
|
||||
SQLITE_PERM(SQLite3Jni.SQLITE_PERM),
|
||||
SQLITE_ABORT(SQLite3Jni.SQLITE_ABORT),
|
||||
SQLITE_BUSY(SQLite3Jni.SQLITE_BUSY),
|
||||
SQLITE_LOCKED(SQLite3Jni.SQLITE_LOCKED),
|
||||
SQLITE_NOMEM(SQLite3Jni.SQLITE_NOMEM),
|
||||
SQLITE_READONLY(SQLite3Jni.SQLITE_READONLY),
|
||||
SQLITE_INTERRUPT(SQLite3Jni.SQLITE_INTERRUPT),
|
||||
SQLITE_IOERR(SQLite3Jni.SQLITE_IOERR),
|
||||
SQLITE_CORRUPT(SQLite3Jni.SQLITE_CORRUPT),
|
||||
SQLITE_NOTFOUND(SQLite3Jni.SQLITE_NOTFOUND),
|
||||
SQLITE_FULL(SQLite3Jni.SQLITE_FULL),
|
||||
SQLITE_CANTOPEN(SQLite3Jni.SQLITE_CANTOPEN),
|
||||
SQLITE_PROTOCOL(SQLite3Jni.SQLITE_PROTOCOL),
|
||||
SQLITE_EMPTY(SQLite3Jni.SQLITE_EMPTY),
|
||||
SQLITE_SCHEMA(SQLite3Jni.SQLITE_SCHEMA),
|
||||
SQLITE_TOOBIG(SQLite3Jni.SQLITE_TOOBIG),
|
||||
SQLITE_CONSTRAINT(SQLite3Jni.SQLITE_CONSTRAINT),
|
||||
SQLITE_MISMATCH(SQLite3Jni.SQLITE_MISMATCH),
|
||||
SQLITE_MISUSE(SQLite3Jni.SQLITE_MISUSE),
|
||||
SQLITE_NOLFS(SQLite3Jni.SQLITE_NOLFS),
|
||||
SQLITE_AUTH(SQLite3Jni.SQLITE_AUTH),
|
||||
SQLITE_FORMAT(SQLite3Jni.SQLITE_FORMAT),
|
||||
SQLITE_RANGE(SQLite3Jni.SQLITE_RANGE),
|
||||
SQLITE_NOTADB(SQLite3Jni.SQLITE_NOTADB),
|
||||
SQLITE_NOTICE(SQLite3Jni.SQLITE_NOTICE),
|
||||
SQLITE_WARNING(SQLite3Jni.SQLITE_WARNING),
|
||||
SQLITE_ROW(SQLite3Jni.SQLITE_ROW),
|
||||
SQLITE_DONE(SQLite3Jni.SQLITE_DONE),
|
||||
SQLITE_ERROR_MISSING_COLLSEQ(SQLite3Jni.SQLITE_ERROR_MISSING_COLLSEQ),
|
||||
SQLITE_ERROR_RETRY(SQLite3Jni.SQLITE_ERROR_RETRY),
|
||||
SQLITE_ERROR_SNAPSHOT(SQLite3Jni.SQLITE_ERROR_SNAPSHOT),
|
||||
SQLITE_IOERR_READ(SQLite3Jni.SQLITE_IOERR_READ),
|
||||
SQLITE_IOERR_SHORT_READ(SQLite3Jni.SQLITE_IOERR_SHORT_READ),
|
||||
SQLITE_IOERR_WRITE(SQLite3Jni.SQLITE_IOERR_WRITE),
|
||||
SQLITE_IOERR_FSYNC(SQLite3Jni.SQLITE_IOERR_FSYNC),
|
||||
SQLITE_IOERR_DIR_FSYNC(SQLite3Jni.SQLITE_IOERR_DIR_FSYNC),
|
||||
SQLITE_IOERR_TRUNCATE(SQLite3Jni.SQLITE_IOERR_TRUNCATE),
|
||||
SQLITE_IOERR_FSTAT(SQLite3Jni.SQLITE_IOERR_FSTAT),
|
||||
SQLITE_IOERR_UNLOCK(SQLite3Jni.SQLITE_IOERR_UNLOCK),
|
||||
SQLITE_IOERR_RDLOCK(SQLite3Jni.SQLITE_IOERR_RDLOCK),
|
||||
SQLITE_IOERR_DELETE(SQLite3Jni.SQLITE_IOERR_DELETE),
|
||||
SQLITE_IOERR_BLOCKED(SQLite3Jni.SQLITE_IOERR_BLOCKED),
|
||||
SQLITE_IOERR_NOMEM(SQLite3Jni.SQLITE_IOERR_NOMEM),
|
||||
SQLITE_IOERR_ACCESS(SQLite3Jni.SQLITE_IOERR_ACCESS),
|
||||
SQLITE_IOERR_CHECKRESERVEDLOCK(SQLite3Jni.SQLITE_IOERR_CHECKRESERVEDLOCK),
|
||||
SQLITE_IOERR_LOCK(SQLite3Jni.SQLITE_IOERR_LOCK),
|
||||
SQLITE_IOERR_CLOSE(SQLite3Jni.SQLITE_IOERR_CLOSE),
|
||||
SQLITE_IOERR_DIR_CLOSE(SQLite3Jni.SQLITE_IOERR_DIR_CLOSE),
|
||||
SQLITE_IOERR_SHMOPEN(SQLite3Jni.SQLITE_IOERR_SHMOPEN),
|
||||
SQLITE_IOERR_SHMSIZE(SQLite3Jni.SQLITE_IOERR_SHMSIZE),
|
||||
SQLITE_IOERR_SHMLOCK(SQLite3Jni.SQLITE_IOERR_SHMLOCK),
|
||||
SQLITE_IOERR_SHMMAP(SQLite3Jni.SQLITE_IOERR_SHMMAP),
|
||||
SQLITE_IOERR_SEEK(SQLite3Jni.SQLITE_IOERR_SEEK),
|
||||
SQLITE_IOERR_DELETE_NOENT(SQLite3Jni.SQLITE_IOERR_DELETE_NOENT),
|
||||
SQLITE_IOERR_MMAP(SQLite3Jni.SQLITE_IOERR_MMAP),
|
||||
SQLITE_IOERR_GETTEMPPATH(SQLite3Jni.SQLITE_IOERR_GETTEMPPATH),
|
||||
SQLITE_IOERR_CONVPATH(SQLite3Jni.SQLITE_IOERR_CONVPATH),
|
||||
SQLITE_IOERR_VNODE(SQLite3Jni.SQLITE_IOERR_VNODE),
|
||||
SQLITE_IOERR_AUTH(SQLite3Jni.SQLITE_IOERR_AUTH),
|
||||
SQLITE_IOERR_BEGIN_ATOMIC(SQLite3Jni.SQLITE_IOERR_BEGIN_ATOMIC),
|
||||
SQLITE_IOERR_COMMIT_ATOMIC(SQLite3Jni.SQLITE_IOERR_COMMIT_ATOMIC),
|
||||
SQLITE_IOERR_ROLLBACK_ATOMIC(SQLite3Jni.SQLITE_IOERR_ROLLBACK_ATOMIC),
|
||||
SQLITE_IOERR_DATA(SQLite3Jni.SQLITE_IOERR_DATA),
|
||||
SQLITE_IOERR_CORRUPTFS(SQLite3Jni.SQLITE_IOERR_CORRUPTFS),
|
||||
SQLITE_LOCKED_SHAREDCACHE(SQLite3Jni.SQLITE_LOCKED_SHAREDCACHE),
|
||||
SQLITE_LOCKED_VTAB(SQLite3Jni.SQLITE_LOCKED_VTAB),
|
||||
SQLITE_BUSY_RECOVERY(SQLite3Jni.SQLITE_BUSY_RECOVERY),
|
||||
SQLITE_BUSY_SNAPSHOT(SQLite3Jni.SQLITE_BUSY_SNAPSHOT),
|
||||
SQLITE_BUSY_TIMEOUT(SQLite3Jni.SQLITE_BUSY_TIMEOUT),
|
||||
SQLITE_CANTOPEN_NOTEMPDIR(SQLite3Jni.SQLITE_CANTOPEN_NOTEMPDIR),
|
||||
SQLITE_CANTOPEN_ISDIR(SQLite3Jni.SQLITE_CANTOPEN_ISDIR),
|
||||
SQLITE_CANTOPEN_FULLPATH(SQLite3Jni.SQLITE_CANTOPEN_FULLPATH),
|
||||
SQLITE_CANTOPEN_CONVPATH(SQLite3Jni.SQLITE_CANTOPEN_CONVPATH),
|
||||
SQLITE_CANTOPEN_SYMLINK(SQLite3Jni.SQLITE_CANTOPEN_SYMLINK),
|
||||
SQLITE_CORRUPT_VTAB(SQLite3Jni.SQLITE_CORRUPT_VTAB),
|
||||
SQLITE_CORRUPT_SEQUENCE(SQLite3Jni.SQLITE_CORRUPT_SEQUENCE),
|
||||
SQLITE_CORRUPT_INDEX(SQLite3Jni.SQLITE_CORRUPT_INDEX),
|
||||
SQLITE_READONLY_RECOVERY(SQLite3Jni.SQLITE_READONLY_RECOVERY),
|
||||
SQLITE_READONLY_CANTLOCK(SQLite3Jni.SQLITE_READONLY_CANTLOCK),
|
||||
SQLITE_READONLY_ROLLBACK(SQLite3Jni.SQLITE_READONLY_ROLLBACK),
|
||||
SQLITE_READONLY_DBMOVED(SQLite3Jni.SQLITE_READONLY_DBMOVED),
|
||||
SQLITE_READONLY_CANTINIT(SQLite3Jni.SQLITE_READONLY_CANTINIT),
|
||||
SQLITE_READONLY_DIRECTORY(SQLite3Jni.SQLITE_READONLY_DIRECTORY),
|
||||
SQLITE_ABORT_ROLLBACK(SQLite3Jni.SQLITE_ABORT_ROLLBACK),
|
||||
SQLITE_CONSTRAINT_CHECK(SQLite3Jni.SQLITE_CONSTRAINT_CHECK),
|
||||
SQLITE_CONSTRAINT_COMMITHOOK(SQLite3Jni.SQLITE_CONSTRAINT_COMMITHOOK),
|
||||
SQLITE_CONSTRAINT_FOREIGNKEY(SQLite3Jni.SQLITE_CONSTRAINT_FOREIGNKEY),
|
||||
SQLITE_CONSTRAINT_FUNCTION(SQLite3Jni.SQLITE_CONSTRAINT_FUNCTION),
|
||||
SQLITE_CONSTRAINT_NOTNULL(SQLite3Jni.SQLITE_CONSTRAINT_NOTNULL),
|
||||
SQLITE_CONSTRAINT_PRIMARYKEY(SQLite3Jni.SQLITE_CONSTRAINT_PRIMARYKEY),
|
||||
SQLITE_CONSTRAINT_TRIGGER(SQLite3Jni.SQLITE_CONSTRAINT_TRIGGER),
|
||||
SQLITE_CONSTRAINT_UNIQUE(SQLite3Jni.SQLITE_CONSTRAINT_UNIQUE),
|
||||
SQLITE_CONSTRAINT_VTAB(SQLite3Jni.SQLITE_CONSTRAINT_VTAB),
|
||||
SQLITE_CONSTRAINT_ROWID(SQLite3Jni.SQLITE_CONSTRAINT_ROWID),
|
||||
SQLITE_CONSTRAINT_PINNED(SQLite3Jni.SQLITE_CONSTRAINT_PINNED),
|
||||
SQLITE_CONSTRAINT_DATATYPE(SQLite3Jni.SQLITE_CONSTRAINT_DATATYPE),
|
||||
SQLITE_NOTICE_RECOVER_WAL(SQLite3Jni.SQLITE_NOTICE_RECOVER_WAL),
|
||||
SQLITE_NOTICE_RECOVER_ROLLBACK(SQLite3Jni.SQLITE_NOTICE_RECOVER_ROLLBACK),
|
||||
SQLITE_WARNING_AUTOINDEX(SQLite3Jni.SQLITE_WARNING_AUTOINDEX),
|
||||
SQLITE_AUTH_USER(SQLite3Jni.SQLITE_AUTH_USER),
|
||||
SQLITE_OK_LOAD_PERMANENTLY(SQLite3Jni.SQLITE_OK_LOAD_PERMANENTLY);
|
||||
|
||||
public final int value;
|
||||
|
||||
ResultCode(int rc){
|
||||
value = rc;
|
||||
ResultCodeMap.set(rc, this);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entry from this enum for the given result code, or
|
||||
null if no match is found.
|
||||
*/
|
||||
public static ResultCode getEntryForInt(int rc){
|
||||
return ResultCodeMap.get(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
Internal level of indirection required because we cannot initialize
|
||||
static enum members in an enum before the enum constructor is
|
||||
invoked.
|
||||
*/
|
||||
private static final class ResultCodeMap {
|
||||
private static final java.util.Map<Integer,ResultCode> i2e
|
||||
= new java.util.HashMap<>();
|
||||
private static void set(int rc, ResultCode e){ i2e.put(rc, e); }
|
||||
private static ResultCode get(int rc){ return i2e.get(rc); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_rollback_hook}.
|
||||
*/
|
||||
public interface RollbackHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_rollback_hook()
|
||||
callback.
|
||||
*/
|
||||
void call();
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
SQLFunction is used in conjunction with the
|
||||
sqlite3_create_function() JNI-bound API to give that native code
|
||||
access to the callback functions needed in order to implement SQL
|
||||
functions in Java.
|
||||
|
||||
<p>
|
||||
|
||||
This class is not used by itself, but is a marker base class. The
|
||||
three UDF types are modelled by the inner classes Scalar,
|
||||
Aggregate<T>, and Window<T>. Most simply, clients may subclass
|
||||
those, or create anonymous classes from them, to implement
|
||||
UDFs. Clients are free to create their own classes for use with
|
||||
UDFs, so long as they conform to the public interfaces defined by
|
||||
those three classes. The JNI layer only actively relies on the
|
||||
SQLFunction base class and the method names and signatures used by
|
||||
the UDF callback interfaces.
|
||||
*/
|
||||
public interface SQLFunction {
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managing their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
<p>T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
|
||||
<p>If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
<p>This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
<p>This class is a helper providing commonly-needed functionality
|
||||
- it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided {@link AggregateFunction} and {@link
|
||||
WindowFunction} classes use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for the given context within the map, one is created
|
||||
using the given initial value, else the existing one is used
|
||||
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||
which can be used to modify that state directly without
|
||||
requiring that the client update the underlying map's entry.
|
||||
|
||||
<p>The caller is obligated to eventually call
|
||||
takeAggregateState() to clear the mapping.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
final Long key = cx.getAggregateContext(true);
|
||||
ValueHolder<T> rc = null==key ? null : map.get(key);
|
||||
if( null==rc ){
|
||||
map.put(key, rc = new ValueHolder<>(initialValue));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xFinal() method and passed that
|
||||
method's first argument. This function removes the value
|
||||
associated with cx.getAggregateContext() from the map and
|
||||
returns it, returning null if no other UDF method has been
|
||||
called to set up such a mapping. The latter condition will be
|
||||
the case if a UDF is used in a statement which has no result
|
||||
rows.
|
||||
*/
|
||||
public T takeAggregateState(sqlite3_context cx){
|
||||
final ValueHolder<T> h = map.remove(cx.getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
|
||||
/**
|
||||
A SQLFunction implementation for scalar functions.
|
||||
*/
|
||||
public abstract class ScalarFunction implements SQLFunction {
|
||||
/**
|
||||
As for the xFunc() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, it is
|
||||
translated into an sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xFunc(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite. This implementation does nothing.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper object for use with sqlite3_table_column_metadata().
|
||||
They are populated only via that interface.
|
||||
*/
|
||||
public final class TableColumnMetadata {
|
||||
OutputPointer.Bool pNotNull = new OutputPointer.Bool();
|
||||
OutputPointer.Bool pPrimaryKey = new OutputPointer.Bool();
|
||||
OutputPointer.Bool pAutoinc = new OutputPointer.Bool();
|
||||
OutputPointer.String pzCollSeq = new OutputPointer.String();
|
||||
OutputPointer.String pzDataType = new OutputPointer.String();
|
||||
|
||||
public TableColumnMetadata(){
|
||||
}
|
||||
|
||||
public String getDataType(){ return pzDataType.value; }
|
||||
public String getCollation(){ return pzCollSeq.value; }
|
||||
public boolean isNotNull(){ return pNotNull.value; }
|
||||
public boolean isPrimaryKey(){ return pPrimaryKey.value; }
|
||||
public boolean isAutoincrement(){ return pAutoinc.value; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,832 +0,0 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file contains a set of tests for the sqlite3 JNI bindings.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
import static org.sqlite.jni.Tester1.*;
|
||||
import org.sqlite.jni.*;
|
||||
import org.sqlite.jni.fts5.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TesterFts5 {
|
||||
|
||||
private static void test1(){
|
||||
final Fts5ExtensionApi fea = Fts5ExtensionApi.getInstance();
|
||||
affirm( null != fea );
|
||||
affirm( fea.getNativePointer() != 0 );
|
||||
affirm( fea == Fts5ExtensionApi.getInstance() )/*singleton*/;
|
||||
|
||||
sqlite3 db = createNewDb();
|
||||
fts5_api fApi = fts5_api.getInstanceForDb(db);
|
||||
affirm( fApi != null );
|
||||
affirm( fApi == fts5_api.getInstanceForDb(db) /* singleton per db */ );
|
||||
|
||||
execSql(db, new String[] {
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(a, b);",
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(1, 'X Y', 'Y Z');",
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(2, 'A Z', 'Y Y');"
|
||||
});
|
||||
|
||||
final String pUserData = "This is pUserData";
|
||||
final int outputs[] = {0, 0};
|
||||
final fts5_extension_function func = new fts5_extension_function(){
|
||||
@Override public void call(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]){
|
||||
final int nCols = ext.xColumnCount(fCx);
|
||||
affirm( 2 == nCols );
|
||||
affirm( nCols == argv.length );
|
||||
affirm( ext.xUserData(fCx) == pUserData );
|
||||
final OutputPointer.String op = new OutputPointer.String();
|
||||
final OutputPointer.Int32 colsz = new OutputPointer.Int32();
|
||||
final OutputPointer.Int64 colTotalSz = new OutputPointer.Int64();
|
||||
for(int i = 0; i < nCols; ++i ){
|
||||
int rc = ext.xColumnText(fCx, i, op);
|
||||
affirm( 0 == rc );
|
||||
final String val = op.value;
|
||||
affirm( val.equals(sqlite3_value_text16(argv[i])) );
|
||||
rc = ext.xColumnSize(fCx, i, colsz);
|
||||
affirm( 0==rc );
|
||||
affirm( 3==sqlite3_value_bytes(argv[i]) );
|
||||
rc = ext.xColumnTotalSize(fCx, i, colTotalSz);
|
||||
affirm( 0==rc );
|
||||
}
|
||||
++outputs[0];
|
||||
}
|
||||
public void xDestroy(){
|
||||
outputs[1] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
int rc = fApi.xCreateFunction("myaux", pUserData, func);
|
||||
affirm( 0==rc );
|
||||
|
||||
affirm( 0==outputs[0] );
|
||||
execSql(db, "select myaux(ft,a,b) from ft;");
|
||||
affirm( 2==outputs[0] );
|
||||
affirm( 0==outputs[1] );
|
||||
sqlite3_close_v2(db);
|
||||
affirm( 1==outputs[1] );
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument sql is a string containing one or more SQL statements
|
||||
** separated by ";" characters. This function executes each of these
|
||||
** statements against the database passed as the first argument. If
|
||||
** no error occurs, the results of the SQL script are returned as
|
||||
** an array of strings. If an error does occur, a RuntimeException is
|
||||
** thrown.
|
||||
*/
|
||||
private static String[] sqlite3_exec(sqlite3 db, String sql) {
|
||||
List<String> aOut = new ArrayList<String>();
|
||||
|
||||
/* Iterate through the list of SQL statements. For each, step through
|
||||
** it and add any results to the aOut[] array. */
|
||||
int rc = sqlite3_prepare_multi(db, sql, new PrepareMultiCallback() {
|
||||
@Override public int call(sqlite3_stmt pStmt){
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
int ii;
|
||||
for(ii=0; ii<sqlite3_column_count(pStmt); ii++){
|
||||
aOut.add( sqlite3_column_text16(pStmt, ii) );
|
||||
}
|
||||
}
|
||||
return sqlite3_finalize(pStmt);
|
||||
}
|
||||
});
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException(sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
/* Convert to array and return */
|
||||
String[] arr = new String[aOut.size()];
|
||||
return aOut.toArray(arr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Execute the SQL script passed as the second parameter via
|
||||
** sqlite3_exec(). Then affirm() that the results, when converted to
|
||||
** a string, match the value of the 3rd parameter. Example:
|
||||
**
|
||||
** do_execsql_test(db, "SELECT 'abc'", "[abc]");
|
||||
**
|
||||
*/
|
||||
private static void do_execsql_test(sqlite3 db, String sql, String expect) {
|
||||
String res = Arrays.toString( sqlite3_exec(db, sql) );
|
||||
affirm( res.equals(expect),
|
||||
"got {" + res + "} expected {" + expect + "}"
|
||||
);
|
||||
}
|
||||
private static void do_execsql_test(sqlite3 db, String sql){
|
||||
do_execsql_test(db, sql, "[]");
|
||||
}
|
||||
|
||||
/*
|
||||
** Create the following custom SQL functions:
|
||||
**
|
||||
** fts5_rowid()
|
||||
** fts5_columncount()
|
||||
*/
|
||||
private static void create_test_functions(sqlite3 db){
|
||||
/*
|
||||
** A user-defined-function fts5_rowid() that uses xRowid()
|
||||
*/
|
||||
fts5_extension_function fts5_rowid = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
long rowid = ext.xRowid(fCx);
|
||||
sqlite3_result_int64(pCx, rowid);
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columncount() - xColumnCount()
|
||||
*/
|
||||
fts5_extension_function fts5_columncount = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
int nCol = ext.xColumnCount(fCx);
|
||||
sqlite3_result_int(pCx, nCol);
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columnsize() - xColumnSize()
|
||||
*/
|
||||
fts5_extension_function fts5_columnsize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
throw new RuntimeException("fts5_columncount: wrong number of args");
|
||||
}
|
||||
int iCol = sqlite3_value_int(argv[0]);
|
||||
|
||||
OutputPointer.Int32 piSz = new OutputPointer.Int32();
|
||||
int rc = ext.xColumnSize(fCx, iCol, piSz);
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException( sqlite3_errstr(rc) );
|
||||
}
|
||||
sqlite3_result_int(pCx, piSz.get());
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columntext() - xColumnText()
|
||||
*/
|
||||
fts5_extension_function fts5_columntext = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
throw new RuntimeException("fts5_columntext: wrong number of args");
|
||||
}
|
||||
int iCol = sqlite3_value_int(argv[0]);
|
||||
|
||||
OutputPointer.String pzText = new OutputPointer.String();
|
||||
int rc = ext.xColumnText(fCx, iCol, pzText);
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException( sqlite3_errstr(rc) );
|
||||
}
|
||||
sqlite3_result_text16(pCx, pzText.get());
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columntotalsize() - xColumnTotalSize()
|
||||
*/
|
||||
fts5_extension_function fts5_columntsize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
throw new RuntimeException(
|
||||
"fts5_columntotalsize: wrong number of args"
|
||||
);
|
||||
}
|
||||
int iCol = sqlite3_value_int(argv[0]);
|
||||
|
||||
OutputPointer.Int64 piSz = new OutputPointer.Int64();
|
||||
int rc = ext.xColumnTotalSize(fCx, iCol, piSz);
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException( sqlite3_errstr(rc) );
|
||||
}
|
||||
sqlite3_result_int64(pCx, piSz.get());
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_aux(<fts>, <value>);
|
||||
*/
|
||||
class fts5_aux implements fts5_extension_function {
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length>1 ){
|
||||
throw new RuntimeException("fts5_aux: wrong number of args");
|
||||
}
|
||||
|
||||
boolean bClear = (argv.length==1);
|
||||
Object obj = ext.xGetAuxdata(fCx, bClear);
|
||||
if( obj instanceof String ){
|
||||
sqlite3_result_text16(pCx, (String)obj);
|
||||
}
|
||||
|
||||
if( argv.length==1 ){
|
||||
String val = sqlite3_value_text16(argv[0]);
|
||||
if( !val.equals("") ){
|
||||
ext.xSetAuxdata(fCx, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_inst(<fts>);
|
||||
**
|
||||
** This is used to test the xInstCount() and xInst() APIs. It returns a
|
||||
** text value containing a Tcl list with xInstCount() elements. Each
|
||||
** element is itself a list of 3 integers - the phrase number, column
|
||||
** number and token offset returned by each call to xInst().
|
||||
*/
|
||||
fts5_extension_function fts5_inst = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
throw new RuntimeException("fts5_inst: wrong number of args");
|
||||
}
|
||||
|
||||
OutputPointer.Int32 pnInst = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piPhrase = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piCol = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piOff = new OutputPointer.Int32();
|
||||
String ret = new String();
|
||||
|
||||
int rc = ext.xInstCount(fCx, pnInst);
|
||||
int nInst = pnInst.get();
|
||||
int ii;
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
|
||||
ext.xInst(fCx, ii, piPhrase, piCol, piOff);
|
||||
if( ii>0 ) ret += " ";
|
||||
ret += "{"+piPhrase.get()+" "+piCol.get()+" "+piOff.get()+"}";
|
||||
}
|
||||
|
||||
sqlite3_result_text(pCx, ret);
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_pinst(<fts>);
|
||||
**
|
||||
** Like SQL function fts5_inst(), except using the following
|
||||
**
|
||||
** xPhraseCount
|
||||
** xPhraseFirst
|
||||
** xPhraseNext
|
||||
*/
|
||||
fts5_extension_function fts5_pinst = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
throw new RuntimeException("fts5_pinst: wrong number of args");
|
||||
}
|
||||
|
||||
OutputPointer.Int32 piCol = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piOff = new OutputPointer.Int32();
|
||||
String ret = new String();
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
int nPhrase = ext.xPhraseCount(fCx);
|
||||
int ii;
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<nPhrase; ii++){
|
||||
Fts5PhraseIter pIter = new Fts5PhraseIter();
|
||||
for(rc = ext.xPhraseFirst(fCx, ii, pIter, piCol, piOff);
|
||||
rc==SQLITE_OK && piCol.get()>=0;
|
||||
ext.xPhraseNext(fCx, pIter, piCol, piOff)
|
||||
){
|
||||
if( !ret.equals("") ) ret += " ";
|
||||
ret += "{"+ii+" "+piCol.get()+" "+piOff.get()+"}";
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException("fts5_pinst: rc=" + rc);
|
||||
}else{
|
||||
sqlite3_result_text(pCx, ret);
|
||||
}
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_pcolinst(<fts>);
|
||||
**
|
||||
** Like SQL function fts5_pinst(), except using the following
|
||||
**
|
||||
** xPhraseFirstColumn
|
||||
** xPhraseNextColumn
|
||||
*/
|
||||
fts5_extension_function fts5_pcolinst = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
throw new RuntimeException("fts5_pcolinst: wrong number of args");
|
||||
}
|
||||
|
||||
OutputPointer.Int32 piCol = new OutputPointer.Int32();
|
||||
String ret = new String();
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
int nPhrase = ext.xPhraseCount(fCx);
|
||||
int ii;
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<nPhrase; ii++){
|
||||
Fts5PhraseIter pIter = new Fts5PhraseIter();
|
||||
for(rc = ext.xPhraseFirstColumn(fCx, ii, pIter, piCol);
|
||||
rc==SQLITE_OK && piCol.get()>=0;
|
||||
ext.xPhraseNextColumn(fCx, pIter, piCol)
|
||||
){
|
||||
if( !ret.equals("") ) ret += " ";
|
||||
ret += "{"+ii+" "+piCol.get()+"}";
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException("fts5_pcolinst: rc=" + rc);
|
||||
}else{
|
||||
sqlite3_result_text(pCx, ret);
|
||||
}
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_rowcount(<fts>);
|
||||
*/
|
||||
fts5_extension_function fts5_rowcount = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
throw new RuntimeException("fts5_rowcount: wrong number of args");
|
||||
}
|
||||
OutputPointer.Int64 pnRow = new OutputPointer.Int64();
|
||||
|
||||
int rc = ext.xRowCount(fCx, pnRow);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_result_int64(pCx, pnRow.get());
|
||||
}else{
|
||||
throw new RuntimeException("fts5_rowcount: rc=" + rc);
|
||||
}
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_phrasesize(<fts>);
|
||||
*/
|
||||
fts5_extension_function fts5_phrasesize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
throw new RuntimeException("fts5_phrasesize: wrong number of args");
|
||||
}
|
||||
int iPhrase = sqlite3_value_int(argv[0]);
|
||||
|
||||
int sz = ext.xPhraseSize(fCx, iPhrase);
|
||||
sqlite3_result_int(pCx, sz);
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_phrasehits(<fts>, <phrase-number>);
|
||||
**
|
||||
** Use the xQueryPhrase() API to determine how many hits, in total,
|
||||
** there are for phrase <phrase-number> in the database.
|
||||
*/
|
||||
fts5_extension_function fts5_phrasehits = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
throw new RuntimeException("fts5_phrasesize: wrong number of args");
|
||||
}
|
||||
int iPhrase = sqlite3_value_int(argv[0]);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
class MyCallback implements Fts5ExtensionApi.XQueryPhraseCallback {
|
||||
public int nRet = 0;
|
||||
public int getRet() { return nRet; }
|
||||
|
||||
@Override
|
||||
public int call(Fts5ExtensionApi fapi, Fts5Context cx){
|
||||
OutputPointer.Int32 pnInst = new OutputPointer.Int32();
|
||||
int rc = fapi.xInstCount(cx, pnInst);
|
||||
nRet += pnInst.get();
|
||||
return rc;
|
||||
}
|
||||
};
|
||||
|
||||
MyCallback xCall = new MyCallback();
|
||||
rc = ext.xQueryPhrase(fCx, iPhrase, xCall);
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException("fts5_phrasehits: rc=" + rc);
|
||||
}
|
||||
sqlite3_result_int(pCx, xCall.getRet());
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_tokenize(<fts>, <text>)
|
||||
*/
|
||||
fts5_extension_function fts5_tokenize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
throw new RuntimeException("fts5_tokenize: wrong number of args");
|
||||
}
|
||||
byte[] utf8 = sqlite3_value_text(argv[0]);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
class MyCallback implements XTokenizeCallback {
|
||||
private List<String> myList = new ArrayList<String>();
|
||||
|
||||
public String getval() {
|
||||
return String.join("+", myList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int call(int tFlags, byte[] txt, int iStart, int iEnd){
|
||||
try {
|
||||
String str = new String(txt, "UTF-8");
|
||||
myList.add(str);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
};
|
||||
|
||||
MyCallback xCall = new MyCallback();
|
||||
ext.xTokenize(fCx, utf8, xCall);
|
||||
sqlite3_result_text16(pCx, xCall.getval());
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
throw new RuntimeException("fts5_tokenize: rc=" + rc);
|
||||
}
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
fts5_api api = fts5_api.getInstanceForDb(db);
|
||||
api.xCreateFunction("fts5_rowid", fts5_rowid);
|
||||
api.xCreateFunction("fts5_columncount", fts5_columncount);
|
||||
api.xCreateFunction("fts5_columnsize", fts5_columnsize);
|
||||
api.xCreateFunction("fts5_columntext", fts5_columntext);
|
||||
api.xCreateFunction("fts5_columntotalsize", fts5_columntsize);
|
||||
|
||||
api.xCreateFunction("fts5_aux1", new fts5_aux());
|
||||
api.xCreateFunction("fts5_aux2", new fts5_aux());
|
||||
|
||||
api.xCreateFunction("fts5_inst", fts5_inst);
|
||||
api.xCreateFunction("fts5_pinst", fts5_pinst);
|
||||
api.xCreateFunction("fts5_pcolinst", fts5_pcolinst);
|
||||
api.xCreateFunction("fts5_rowcount", fts5_rowcount);
|
||||
api.xCreateFunction("fts5_phrasesize", fts5_phrasesize);
|
||||
api.xCreateFunction("fts5_phrasehits", fts5_phrasehits);
|
||||
api.xCreateFunction("fts5_tokenize", fts5_tokenize);
|
||||
}
|
||||
/*
|
||||
** Test of various Fts5ExtensionApi methods
|
||||
*/
|
||||
private static void test2(){
|
||||
|
||||
/* Open db and populate an fts5 table */
|
||||
sqlite3 db = createNewDb();
|
||||
do_execsql_test(db,
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(a, b);" +
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(-9223372036854775808, 'x', 'x');" +
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(0, 'x', 'x');" +
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(1, 'x y z', 'x y z');" +
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(2, 'x y z', 'x z');" +
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(3, 'x y z', 'x y z');" +
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(9223372036854775807, 'x', 'x');"
|
||||
);
|
||||
|
||||
create_test_functions(db);
|
||||
|
||||
/* Test that fts5_rowid() seems to work */
|
||||
do_execsql_test(db,
|
||||
"SELECT rowid==fts5_rowid(ft) FROM ft('x')",
|
||||
"[1, 1, 1, 1, 1, 1]"
|
||||
);
|
||||
|
||||
/* Test fts5_columncount() */
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columncount(ft) FROM ft('x')",
|
||||
"[2, 2, 2, 2, 2, 2]"
|
||||
);
|
||||
|
||||
/* Test fts5_columnsize() */
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columnsize(ft, 0) FROM ft('x') ORDER BY rowid",
|
||||
"[1, 1, 3, 3, 3, 1]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columnsize(ft, 1) FROM ft('x') ORDER BY rowid",
|
||||
"[1, 1, 3, 2, 3, 1]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columnsize(ft, -1) FROM ft('x') ORDER BY rowid",
|
||||
"[2, 2, 6, 5, 6, 2]"
|
||||
);
|
||||
|
||||
/* Test that xColumnSize() returns SQLITE_RANGE if the column number
|
||||
** is out-of range */
|
||||
try {
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columnsize(ft, 2) FROM ft('x') ORDER BY rowid"
|
||||
);
|
||||
} catch( RuntimeException e ){
|
||||
affirm( e.getMessage().matches(".*column index out of range") );
|
||||
}
|
||||
|
||||
/* Test fts5_columntext() */
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntext(ft, 0) FROM ft('x') ORDER BY rowid",
|
||||
"[x, x, x y z, x y z, x y z, x]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntext(ft, 1) FROM ft('x') ORDER BY rowid",
|
||||
"[x, x, x y z, x z, x y z, x]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntext(ft, 2) FROM ft('x') ORDER BY rowid",
|
||||
"[null, null, null, null, null, null]"
|
||||
);
|
||||
|
||||
/* Test fts5_columntotalsize() */
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntotalsize(ft, 0) FROM ft('x') ORDER BY rowid",
|
||||
"[12, 12, 12, 12, 12, 12]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntotalsize(ft, 1) FROM ft('x') ORDER BY rowid",
|
||||
"[11, 11, 11, 11, 11, 11]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntotalsize(ft, -1) FROM ft('x') ORDER BY rowid",
|
||||
"[23, 23, 23, 23, 23, 23]"
|
||||
);
|
||||
|
||||
/* Test that xColumnTotalSize() returns SQLITE_RANGE if the column
|
||||
** number is out-of range */
|
||||
try {
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_columntotalsize(ft, 2) FROM ft('x') ORDER BY rowid"
|
||||
);
|
||||
} catch( RuntimeException e ){
|
||||
affirm( e.getMessage().matches(".*column index out of range") );
|
||||
}
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT rowid, fts5_rowcount(ft) FROM ft('z')",
|
||||
"[1, 6, 2, 6, 3, 6]"
|
||||
);
|
||||
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
/*
|
||||
** Test of various Fts5ExtensionApi methods
|
||||
*/
|
||||
private static void test3(){
|
||||
|
||||
/* Open db and populate an fts5 table */
|
||||
sqlite3 db = createNewDb();
|
||||
do_execsql_test(db,
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(a, b);" +
|
||||
"INSERT INTO ft(a, b) VALUES('the one', 1);" +
|
||||
"INSERT INTO ft(a, b) VALUES('the two', 2);" +
|
||||
"INSERT INTO ft(a, b) VALUES('the three', 3);" +
|
||||
"INSERT INTO ft(a, b) VALUES('the four', '');"
|
||||
);
|
||||
create_test_functions(db);
|
||||
|
||||
/* Test fts5_aux1() + fts5_aux2() - users of xGetAuxdata and xSetAuxdata */
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_aux1(ft, a) FROM ft('the')",
|
||||
"[null, the one, the two, the three]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_aux2(ft, b) FROM ft('the')",
|
||||
"[null, 1, 2, 3]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_aux1(ft, a), fts5_aux2(ft, b) FROM ft('the')",
|
||||
"[null, null, the one, 1, the two, 2, the three, 3]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_aux1(ft, b), fts5_aux1(ft) FROM ft('the')",
|
||||
"[null, 1, 1, 2, 2, 3, 3, null]"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
** Test of various Fts5ExtensionApi methods
|
||||
*/
|
||||
private static void test4(){
|
||||
|
||||
/* Open db and populate an fts5 table */
|
||||
sqlite3 db = createNewDb();
|
||||
create_test_functions(db);
|
||||
do_execsql_test(db,
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(a, b);" +
|
||||
"INSERT INTO ft(a, b) VALUES('one two three', 'two three four');" +
|
||||
"INSERT INTO ft(a, b) VALUES('two three four', 'three four five');" +
|
||||
"INSERT INTO ft(a, b) VALUES('three four five', 'four five six');"
|
||||
);
|
||||
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_inst(ft) FROM ft('two')",
|
||||
"[{0 0 1} {0 1 0}, {0 0 0}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_inst(ft) FROM ft('four')",
|
||||
"[{0 1 2}, {0 0 2} {0 1 1}, {0 0 1} {0 1 0}]"
|
||||
);
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_inst(ft) FROM ft('a OR b OR four')",
|
||||
"[{2 1 2}, {2 0 2} {2 1 1}, {2 0 1} {2 1 0}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_inst(ft) FROM ft('two four')",
|
||||
"[{0 0 1} {0 1 0} {1 1 2}, {0 0 0} {1 0 2} {1 1 1}]"
|
||||
);
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pinst(ft) FROM ft('two')",
|
||||
"[{0 0 1} {0 1 0}, {0 0 0}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pinst(ft) FROM ft('four')",
|
||||
"[{0 1 2}, {0 0 2} {0 1 1}, {0 0 1} {0 1 0}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pinst(ft) FROM ft('a OR b OR four')",
|
||||
"[{2 1 2}, {2 0 2} {2 1 1}, {2 0 1} {2 1 0}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pinst(ft) FROM ft('two four')",
|
||||
"[{0 0 1} {0 1 0} {1 1 2}, {0 0 0} {1 0 2} {1 1 1}]"
|
||||
);
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pcolinst(ft) FROM ft('two')",
|
||||
"[{0 0} {0 1}, {0 0}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pcolinst(ft) FROM ft('four')",
|
||||
"[{0 1}, {0 0} {0 1}, {0 0} {0 1}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pcolinst(ft) FROM ft('a OR b OR four')",
|
||||
"[{2 1}, {2 0} {2 1}, {2 0} {2 1}]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_pcolinst(ft) FROM ft('two four')",
|
||||
"[{0 0} {0 1} {1 1}, {0 0} {1 0} {1 1}]"
|
||||
);
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_phrasesize(ft, 0) FROM ft('four five six') LIMIT 1;",
|
||||
"[1]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_phrasesize(ft, 0) FROM ft('four + five + six') LIMIT 1;",
|
||||
"[3]"
|
||||
);
|
||||
|
||||
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
private static void test5(){
|
||||
/* Open db and populate an fts5 table */
|
||||
sqlite3 db = createNewDb();
|
||||
create_test_functions(db);
|
||||
do_execsql_test(db,
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(x, b);" +
|
||||
"INSERT INTO ft(x) VALUES('one two three four five six seven eight');" +
|
||||
"INSERT INTO ft(x) VALUES('one two one four one six one eight');" +
|
||||
"INSERT INTO ft(x) VALUES('one two three four five six seven eight');"
|
||||
);
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_phrasehits(ft, 0) FROM ft('one') LIMIT 1",
|
||||
"[6]"
|
||||
);
|
||||
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
private static void test6(){
|
||||
sqlite3 db = createNewDb();
|
||||
create_test_functions(db);
|
||||
do_execsql_test(db,
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(x, b);" +
|
||||
"INSERT INTO ft(x) VALUES('one two three four five six seven eight');"
|
||||
);
|
||||
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_tokenize(ft, 'abc def ghi') FROM ft('one')",
|
||||
"[abc+def+ghi]"
|
||||
);
|
||||
do_execsql_test(db,
|
||||
"SELECT fts5_tokenize(ft, 'it''s BEEN a...') FROM ft('one')",
|
||||
"[it+s+been+a]"
|
||||
);
|
||||
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
private static synchronized void runTests(){
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
test6();
|
||||
}
|
||||
|
||||
public TesterFts5(){
|
||||
runTests();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import org.sqlite.jni.annotation.Nullable;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_trace_v2}.
|
||||
*/
|
||||
public interface TraceV2Callback extends CallbackProxy {
|
||||
/**
|
||||
Called by sqlite3 for various tracing operations, as per
|
||||
sqlite3_trace_v2(). Note that this interface elides the 2nd
|
||||
argument to the native trace callback, as that role is better
|
||||
filled by instance-local state.
|
||||
|
||||
<p>These callbacks may throw, in which case their exceptions are
|
||||
converted to C-level error information.
|
||||
|
||||
<p>The 2nd argument to this function, if non-null, will be a an
|
||||
sqlite3 or sqlite3_stmt object, depending on the first argument
|
||||
(see below).
|
||||
|
||||
<p>The final argument to this function is the "X" argument
|
||||
documented for sqlite3_trace() and sqlite3_trace_v2(). Its type
|
||||
depends on value of the first argument:
|
||||
|
||||
<p>- SQLITE_TRACE_STMT: pNative is a sqlite3_stmt. pX is a String
|
||||
containing the prepared SQL.
|
||||
|
||||
<p>- SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long
|
||||
holding an approximate number of nanoseconds the statement took
|
||||
to run.
|
||||
|
||||
<p>- SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null.
|
||||
|
||||
<p>- SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null.
|
||||
*/
|
||||
int call(int traceFlag, Object pNative, @Nullable Object pX);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_update_hook}.
|
||||
*/
|
||||
public interface UpdateHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level sqlite3_update_hook()
|
||||
callback.
|
||||
*/
|
||||
void call(int opId, String dbName, String tableName, long rowId);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
|
||||
/**
|
||||
A SQLFunction implementation for window functions. Note that
|
||||
WindowFunction inherits from {@link AggregateFunction} and each
|
||||
instance is required to implement the inherited abstract methods
|
||||
from that class. See {@link AggregateFunction} for information on
|
||||
managing the UDF's invocation-specific state.
|
||||
*/
|
||||
public abstract class WindowFunction<T> extends AggregateFunction<T> {
|
||||
|
||||
/**
|
||||
As for the xInverse() argument of the C API's
|
||||
sqlite3_create_window_function(). If this function throws, the
|
||||
exception is not propagated and a warning might be emitted
|
||||
to a debugging channel.
|
||||
*/
|
||||
public abstract void xInverse(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's sqlite3_create_window_function().
|
||||
See xInverse() for the fate of any exceptions this throws.
|
||||
*/
|
||||
public abstract void xValue(sqlite3_context cx);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file declares JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
Callback for a hook called by SQLite when certain client-provided
|
||||
state are destroyed. It gets its name from the pervasive use of
|
||||
the symbol name xDestroy() for this purpose in the C API
|
||||
documentation.
|
||||
*/
|
||||
public interface XDestroyCallback {
|
||||
/**
|
||||
Must perform any cleanup required by this object. Must not
|
||||
throw. Must not call back into the sqlite3 API, else it might
|
||||
invoke a deadlock.
|
||||
|
||||
WARNING: as a rule, it is never safe to register individual
|
||||
instances with this interface multiple times in the
|
||||
library. e.g., do not register the same CollationCallback with
|
||||
multiple arities or names using sqlite3_create_collation(). If
|
||||
this rule is violated, the library will eventually try to free
|
||||
each individual reference, leading to memory corruption or a
|
||||
crash via duplicate free().
|
||||
*/
|
||||
public void xDestroy();
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
** 2023-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the Canonical annotaion for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
|
||||
/**
|
||||
This annotation is for marking functions as "canonical", meaning
|
||||
that they map directly to a function in the core sqlite3 C API. The
|
||||
intent is to distinguish them from functions added specifically to
|
||||
the Java API.
|
||||
|
||||
<p>Canonical functions, unless specifically documented, have the
|
||||
same semantics as their counterparts in <a
|
||||
href="https://sqlite.org/c3ref/intro.html">the C API
|
||||
documentation</a>, despite their signatures perhaps differing
|
||||
slightly. Canonical forms may be native or implemented in Java.
|
||||
Sometimes multiple overloads are labeled as Canonical because one
|
||||
or more of them are just type- or encoding-related conversion
|
||||
wrappers but provide identical semantics (e.g. from a String to a
|
||||
byte[]). The Java API adds a number of convenience overloads to
|
||||
simplify use, as well as a few Java-specific functions, and those
|
||||
are never flagged as @Canonical.
|
||||
|
||||
<p>In some cases, the canonical version of a function is private
|
||||
and exposed to Java via public overloads.
|
||||
|
||||
<p>The comment property can be used to add a comment.
|
||||
*/
|
||||
@java.lang.annotation.Documented
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD)
|
||||
public @interface Canonical{
|
||||
/**
|
||||
Brief comments about the binding, e.g. noting any major
|
||||
semantic differences.
|
||||
*/
|
||||
String comment() default "";
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
** 2023-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the NotNull annotaion for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
|
||||
/**
|
||||
This annotation is for flagging parameters which may not legally be
|
||||
null or point to closed/finalized C-side resources.
|
||||
|
||||
<p>In the case of Java types which map directly to C struct types
|
||||
(e.g. {@link org.sqlite.jni.sqlite3}, {@link
|
||||
org.sqlite.jni.sqlite3_stmt}, and {@link
|
||||
org.sqlite.jni.sqlite3_context}), a closed/finalized resource is
|
||||
also considered to be null for purposes this annotation because the
|
||||
C-side effect of passing such a handle is the same as if null is
|
||||
passed.</p>
|
||||
|
||||
<p>When used in the context of Java interfaces which are called
|
||||
from the C APIs, this annotation communicates that the C API will
|
||||
never pass a null value to the callback for that parameter.</p>
|
||||
|
||||
<p>Passing a null, for this annotation's definition of null, for
|
||||
any parameter marked with this annoation specifically invokes
|
||||
undefined behavior.</p>
|
||||
|
||||
<p>Passing 0 (i.e. C NULL) or a negative value for any long-type
|
||||
parameter marked with this annoation specifically invokes undefined
|
||||
behavior. Such values are treated as C pointers in the JNI
|
||||
layer.</p>
|
||||
|
||||
<p>Note that the C-style API does not throw any exceptions on its
|
||||
own because it has a no-throw policy in order to retain its C-style
|
||||
semantics, but it may trigger NullPointerExceptions (or similar) if
|
||||
passed a null for a parameter flagged with this annotation.</p>
|
||||
|
||||
<p>This annotation is informational only. No policy is in place to
|
||||
programmatically ensure that NotNull is conformed to in client
|
||||
code.</p>
|
||||
|
||||
<p>This annotation is solely for the use by the classes in the
|
||||
org.sqlite package and subpackages, but is made public so that
|
||||
javadoc will link to it from the annotated functions. It is not
|
||||
part of the public API and client-level code must not rely on
|
||||
it.</p>
|
||||
*/
|
||||
@java.lang.annotation.Documented
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
|
||||
@java.lang.annotation.Target(java.lang.annotation.ElementType.PARAMETER)
|
||||
public @interface NotNull{}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
** 2023-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the Nullable annotaion for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
|
||||
/**
|
||||
This annotation is for flagging parameters which may legally be
|
||||
null, noting that they may behave differently if passed null but
|
||||
are prepared to expect null as a value. When used in the context of
|
||||
callback methods which are called into from the C APIs, this
|
||||
annotation communicates that the C API may pass a null value to the
|
||||
callback.
|
||||
|
||||
<p>This annotation is solely for the use by the classes in this
|
||||
package but is made public so that javadoc will link to it from the
|
||||
annotated functions. It is not part of the public API and
|
||||
client-level code must not rely on it.
|
||||
*/
|
||||
@java.lang.annotation.Documented
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
|
||||
@java.lang.annotation.Target(java.lang.annotation.ElementType.PARAMETER)
|
||||
public @interface Nullable{}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
** 2023-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
/**
|
||||
This package houses annotations specific to the JNI bindings of the
|
||||
SQLite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A utility object for holding FTS5-specific types and constants
|
||||
which are used by multiple FTS5 classes.
|
||||
*/
|
||||
public final class Fts5 {
|
||||
/* Not used */
|
||||
private Fts5(){}
|
||||
|
||||
|
||||
public static final int FTS5_TOKENIZE_QUERY = 0x0001;
|
||||
public static final int FTS5_TOKENIZE_PREFIX = 0x0002;
|
||||
public static final int FTS5_TOKENIZE_DOCUMENT = 0x0004;
|
||||
public static final int FTS5_TOKENIZE_AUX = 0x0008;
|
||||
public static final int FTS5_TOKEN_COLOCATED = 0x0001;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.*;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (Fts5Context*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class Fts5Context extends NativePointerHolder<Fts5Context> {
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.sqlite.jni.*;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
|
||||
/**
|
||||
*/
|
||||
public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi> {
|
||||
//! Only called from JNI
|
||||
private Fts5ExtensionApi(){}
|
||||
private final int iVersion = 2;
|
||||
|
||||
/* Callback type for used by xQueryPhrase(). */
|
||||
public static interface XQueryPhraseCallback {
|
||||
int call(Fts5ExtensionApi fapi, Fts5Context cx);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the singleton instance of this class.
|
||||
*/
|
||||
public static native Fts5ExtensionApi getInstance();
|
||||
|
||||
@Canonical
|
||||
public native int xColumnCount(@NotNull Fts5Context fcx);
|
||||
|
||||
@Canonical
|
||||
public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.Int32 pnToken);
|
||||
|
||||
@Canonical
|
||||
public native int xColumnText(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.String txt);
|
||||
|
||||
@Canonical
|
||||
public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
|
||||
@NotNull OutputPointer.Int64 pnToken);
|
||||
|
||||
@Canonical
|
||||
public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
|
||||
|
||||
@Canonical
|
||||
public native int xInst(@NotNull Fts5Context cx, int iIdx,
|
||||
@NotNull OutputPointer.Int32 piPhrase,
|
||||
@NotNull OutputPointer.Int32 piCol,
|
||||
@NotNull OutputPointer.Int32 piOff);
|
||||
|
||||
@Canonical
|
||||
public native int xInstCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int32 pnInst);
|
||||
|
||||
@Canonical
|
||||
public native int xPhraseCount(@NotNull Fts5Context fcx);
|
||||
|
||||
@Canonical
|
||||
public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
|
||||
@Canonical
|
||||
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
@Canonical
|
||||
public native void xPhraseNext(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
@Canonical
|
||||
public native void xPhraseNextColumn(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
@Canonical
|
||||
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
|
||||
|
||||
@Canonical
|
||||
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull XQueryPhraseCallback callback);
|
||||
@Canonical
|
||||
public native int xRowCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int64 nRow);
|
||||
|
||||
@Canonical
|
||||
public native long xRowid(@NotNull Fts5Context cx);
|
||||
/* Note that the JNI binding lacks the C version's xDelete()
|
||||
callback argument. Instead, if pAux has an xDestroy() method, it
|
||||
is called if the FTS5 API finalizes the aux state (including if
|
||||
allocation of storage for the auxdata fails). Any reference to
|
||||
pAux held by the JNI layer will be relinquished regardless of
|
||||
whether pAux has an xDestroy() method. */
|
||||
|
||||
@Canonical
|
||||
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
|
||||
|
||||
@Canonical
|
||||
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
|
||||
@NotNull XTokenizeCallback callback);
|
||||
|
||||
@Canonical
|
||||
public native Object xUserData(Fts5Context cx);
|
||||
//^^^ returns the pointer passed as the 3rd arg to the C-level
|
||||
// fts5_api::xCreateFunction().
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.NativePointerHolder;
|
||||
|
||||
/**
|
||||
A wrapper for C-level Fts5PhraseIter. They are only modified and
|
||||
inspected by native-level code.
|
||||
*/
|
||||
public final class Fts5PhraseIter extends NativePointerHolder<Fts5PhraseIter> {
|
||||
//! Updated and used only by native code.
|
||||
private long a;
|
||||
private long b;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
** 2023-08-05x
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.NativePointerHolder;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (Fts5Tokenizer*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
|
||||
At the C level, the Fts5Tokenizer type is essentially a void
|
||||
pointer used specifically for tokenizers.
|
||||
*/
|
||||
public final class Fts5Tokenizer extends NativePointerHolder<Fts5Tokenizer> {
|
||||
//! Only called from JNI.
|
||||
private Fts5Tokenizer(){}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
|
||||
|
||||
/**
|
||||
Callback type for use with xTokenize() variants.
|
||||
*/
|
||||
public interface XTokenizeCallback {
|
||||
int call(int tFlags, byte[] txt, int iStart, int iEnd);
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
import org.sqlite.jni.*;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (fts5_api*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class fts5_api extends NativePointerHolder<fts5_api> {
|
||||
/* Only invoked from JNI */
|
||||
private fts5_api(){}
|
||||
|
||||
public static final int iVersion = 2;
|
||||
|
||||
/**
|
||||
Returns the fts5_api instance associated with the given db, or
|
||||
null if something goes horribly wrong.
|
||||
*/
|
||||
public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
|
||||
|
||||
@Canonical
|
||||
public synchronized native int xCreateFunction(@NotNull String name,
|
||||
@Nullable Object userData,
|
||||
@NotNull fts5_extension_function xFunction);
|
||||
|
||||
/**
|
||||
Convenience overload which passes null as the 2nd argument to the
|
||||
3-parameter form.
|
||||
*/
|
||||
public int xCreateFunction(@NotNull String name,
|
||||
@NotNull fts5_extension_function xFunction){
|
||||
return xCreateFunction(name, null, xFunction);
|
||||
}
|
||||
|
||||
// /* Create a new auxiliary function */
|
||||
// int (*xCreateFunction)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_extension_function xFunction,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
// Still potentially todo:
|
||||
|
||||
// int (*xCreateTokenizer)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_tokenizer *pTokenizer,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
// /* Find an existing tokenizer */
|
||||
// int (*xFindTokenizer)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void **ppContext,
|
||||
// fts5_tokenizer *pTokenizer
|
||||
// );
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.sqlite3_context;
|
||||
import org.sqlite.jni.sqlite3_value;
|
||||
|
||||
/**
|
||||
JNI-level wrapper for C's fts5_extension_function type.
|
||||
*/
|
||||
public interface fts5_extension_function {
|
||||
// typedef void (*fts5_extension_function)(
|
||||
// const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
|
||||
// Fts5Context *pFts, /* First arg to pass to pApi functions */
|
||||
// sqlite3_context *pCtx, /* Context for returning result/error */
|
||||
// int nVal, /* Number of values in apVal[] array */
|
||||
// sqlite3_value **apVal /* Array of trailing arguments */
|
||||
// );
|
||||
|
||||
/**
|
||||
The callback implementation, corresponding to the xFunction
|
||||
argument of C's fts5_api::xCreateFunction().
|
||||
*/
|
||||
void call(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]);
|
||||
/**
|
||||
Is called when this function is destroyed by sqlite3. Typically
|
||||
this function will be empty.
|
||||
*/
|
||||
void xDestroy();
|
||||
|
||||
public static abstract class Abstract implements fts5_extension_function {
|
||||
@Override public abstract void call(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]);
|
||||
@Override public void xDestroy(){}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.NativePointerHolder;
|
||||
import org.sqlite.jni.annotation.NotNull;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (fts5_tokenizer*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class fts5_tokenizer extends NativePointerHolder<fts5_tokenizer> {
|
||||
/* Only invoked by JNI */
|
||||
private fts5_tokenizer(){}
|
||||
|
||||
// int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
|
||||
// void (*xDelete)(Fts5Tokenizer*);
|
||||
|
||||
public native int xTokenize(@NotNull Fts5Tokenizer t, int tokFlags,
|
||||
@NotNull byte pText[],
|
||||
@NotNull XTokenizeCallback callback);
|
||||
|
||||
|
||||
// int (*xTokenize)(Fts5Tokenizer*,
|
||||
// void *pCtx,
|
||||
// int flags, /* Mask of FTS5_TOKENIZE_* flags */
|
||||
// const char *pText, int nText,
|
||||
// int (*xToken)(
|
||||
// void *pCtx, /* Copy of 2nd argument to xTokenize() */
|
||||
// int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
// const char *pToken, /* Pointer to buffer containing token */
|
||||
// int nToken, /* Size of token in bytes */
|
||||
// int iStart, /* Byte offset of token within input text */
|
||||
// int iEnd /* Byte offset of end of token within input text */
|
||||
// )
|
||||
// );
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/**
|
||||
This package houses a JNI binding to the SQLite3 C API.
|
||||
|
||||
<p>The primary interfaces are in {@link
|
||||
org.sqlite.jni.SQLite3Jni}.</p>
|
||||
|
||||
<h1>API Goals and Requirements</h1>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>A 1-to-1(-ish) mapping of the C API to Java via JNI, insofar
|
||||
as cross-language semantics allow for. A closely-related goal is
|
||||
that <a href='https://sqlite.org/c3ref/intro.html'>the C
|
||||
documentation</a> should be usable as-is, insofar as possible,
|
||||
for most of the JNI binding. As a rule, undocumented symbols in
|
||||
the Java interface behave as documented for their C API
|
||||
counterpart. Only semantic differences and Java-specific features
|
||||
are documented here.</li>
|
||||
|
||||
<li>Support Java as far back as version 8 (2014).</li>
|
||||
|
||||
<li>Environment-independent. Should work everywhere both Java and
|
||||
SQLite3 do.</li>
|
||||
|
||||
<li>No 3rd-party dependencies beyond the JDK. That includes no
|
||||
build-level dependencies for specific IDEs and toolchains. We
|
||||
welcome the addition of build files for arbitrary environments
|
||||
insofar as they neither interfere with each other nor become a
|
||||
maintenance burden for the sqlite developers.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>Non-Goals</h2>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>Creation of high-level OO wrapper APIs. Clients are free to
|
||||
create them off of the C-style API.</li>
|
||||
|
||||
<li>Support for mixed-mode operation, where client code accesses
|
||||
SQLite both via the Java-side API and the C API via their own
|
||||
native code. In such cases, proxy functionalities (primarily
|
||||
callback handler wrappers of all sorts) may fail because the
|
||||
C-side use of the SQLite APIs will bypass those proxies.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h1>State of this API</h1>
|
||||
|
||||
<p>As of version 3.43, this software is in "tech preview" form. We
|
||||
tentatively plan to stamp it as stable with the 3.44 release.</p>
|
||||
|
||||
<h1>Threading Considerations</h1>
|
||||
|
||||
<p>This API is, if built with SQLITE_THREADSAFE set to 1 or 2,
|
||||
thread-safe, insofar as the C API guarantees, with some addenda:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>It is not legal to use Java-facing SQLite3 resource handles
|
||||
(sqlite3, sqlite3_stmt, etc) from multiple threads concurrently,
|
||||
nor to use any database-specific resources concurrently in a
|
||||
thread separate from the one the database is currently in use
|
||||
in. i.e. do not use a sqlite3_stmt in thread #2 when thread #1 is
|
||||
using the database which prepared that handle.
|
||||
|
||||
<br>Violating this will eventually corrupt the JNI-level bindings
|
||||
between Java's and C's view of the database. This is a limitation
|
||||
of the JNI bindings, not the lower-level library.
|
||||
</li>
|
||||
|
||||
<li>It is legal to use a given handle, and database-specific
|
||||
resources, across threads, so long as no two threads pass
|
||||
resources owned by the same database into the library
|
||||
concurrently.
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<p>Any number of threads may, of course, create and use any number
|
||||
of database handles they wish. Care only needs to be taken when
|
||||
those handles or their associated resources cross threads, or...</p>
|
||||
|
||||
<p>When built with SQLITE_THREADSAFE=0 then no threading guarantees
|
||||
are provided and multi-threaded use of the library will provoke
|
||||
undefined behavior.</p>
|
||||
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (sqlite3*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java
|
||||
and C via JNI.
|
||||
*/
|
||||
public final class sqlite3 extends NativePointerHolder<sqlite3>
|
||||
implements AutoCloseable {
|
||||
|
||||
// Only invoked from JNI
|
||||
private sqlite3(){}
|
||||
|
||||
public String toString(){
|
||||
final long ptr = getNativePointer();
|
||||
if( 0==ptr ){
|
||||
return sqlite3.class.getSimpleName()+"@null";
|
||||
}
|
||||
final String fn = SQLite3Jni.sqlite3_db_filename(this, "main");
|
||||
return sqlite3.class.getSimpleName()
|
||||
+"@"+String.format("0x%08x",ptr)
|
||||
+"["+((null == fn) ? "<unnamed>" : fn)+"]"
|
||||
;
|
||||
}
|
||||
|
||||
@Override protected void finalize(){
|
||||
//System.out.println(this+".finalize()");
|
||||
SQLite3Jni.sqlite3_close_v2(this.clearNativePointer());
|
||||
}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_close_v2(this.clearNativePointer());
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
** 2023-09-03
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for passing C-level (sqlite3_backup*) instances around in
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class sqlite3_backup extends NativePointerHolder<sqlite3_backup>
|
||||
implements AutoCloseable {
|
||||
// Only invoked from JNI.
|
||||
private sqlite3_backup(){}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_backup_finish(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
** 2023-09-03
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for passing C-level (sqlite3_blob*) instances around in
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob>
|
||||
implements AutoCloseable {
|
||||
// Only invoked from JNI.
|
||||
private sqlite3_blob(){}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_blob_close(this.clearNativePointer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
sqlite3_context instances are used in conjunction with user-defined
|
||||
SQL functions (a.k.a. UDFs).
|
||||
*/
|
||||
public final class sqlite3_context extends NativePointerHolder<sqlite3_context> {
|
||||
private Long aggregateContext = null;
|
||||
|
||||
/**
|
||||
getAggregateContext() corresponds to C's
|
||||
sqlite3_aggregate_context(), with a slightly different interface
|
||||
to account for cross-language differences. It serves the same
|
||||
purposes in a slightly different way: it provides a key which is
|
||||
stable across invocations of a UDF's callbacks, such that all
|
||||
calls into those callbacks can determine which "set" of those
|
||||
calls they belong to.
|
||||
|
||||
<p>Note that use of this method is not a requirement for proper use
|
||||
of this class. sqlite3_aggregate_context() can also be used.
|
||||
|
||||
<p>If the argument is true and the aggregate context has not yet
|
||||
been set up, it will be initialized and fetched on demand, else it
|
||||
won't. The intent is that xStep(), xValue(), and xInverse()
|
||||
methods pass true and xFinal() methods pass false.
|
||||
|
||||
<p>This function treats numeric 0 as null, always returning null instead
|
||||
of 0.
|
||||
|
||||
<p>If this object is being used in the context of an aggregate or
|
||||
window UDF, this function returns a non-0 value which is distinct
|
||||
for each set of UDF callbacks from a single invocation of the
|
||||
UDF, otherwise it returns 0. The returned value is only only
|
||||
valid within the context of execution of a single SQL statement,
|
||||
and must not be re-used by future invocations of the UDF in
|
||||
different SQL statements.
|
||||
|
||||
<p>Consider this SQL, where MYFUNC is a user-defined aggregate function:
|
||||
|
||||
<pre>{@code
|
||||
SELECT MYFUNC(A), MYFUNC(B) FROM T;
|
||||
}</pre>
|
||||
|
||||
<p>The xStep() and xFinal() methods of the callback need to be able
|
||||
to differentiate between those two invocations in order to
|
||||
perform their work properly. The value returned by
|
||||
getAggregateContext() will be distinct for each of those
|
||||
invocations of MYFUNC() and is intended to be used as a lookup
|
||||
key for mapping callback invocations to whatever client-defined
|
||||
state is needed by the UDF.
|
||||
|
||||
<p>There is one case where this will return null in the context
|
||||
of an aggregate or window function: if the result set has no
|
||||
rows, the UDF's xFinal() will be called without any other x...()
|
||||
members having been called. In that one case, no aggregate
|
||||
context key will have been generated. xFinal() implementations
|
||||
need to be prepared to accept that condition as legal.
|
||||
*/
|
||||
public synchronized Long getAggregateContext(boolean initIfNeeded){
|
||||
if( aggregateContext==null ){
|
||||
aggregateContext = SQLite3Jni.sqlite3_aggregate_context(this, initIfNeeded);
|
||||
if( !initIfNeeded && null==aggregateContext ) aggregateContext = 0L;
|
||||
}
|
||||
return (null==aggregateContext || 0!=aggregateContext) ? aggregateContext : null;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (sqlite3_stmt*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt>
|
||||
implements AutoCloseable {
|
||||
// Only invoked from JNI.
|
||||
private sqlite3_stmt(){}
|
||||
|
||||
//For as-yet-unknown reasons, this triggers a JVM crash.
|
||||
//@Override protected void finalize(){
|
||||
// SQLite3Jni.sqlite3_finalize(this.clearNativePointer());
|
||||
//}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_finalize(this.clearNativePointer());
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
** 2023-07-21
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
public final class sqlite3_value extends NativePointerHolder<sqlite3_value> {
|
||||
//! Invoked only from JNI.
|
||||
private sqlite3_value(){}
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
# Specifications For A Rudimentary SQLite Test Script Interpreter
|
||||
|
||||
## Overview
|
||||
|
||||
The purpose of the Test Script Interpreter is to read and interpret
|
||||
script files that contain SQL commands and desired results. The
|
||||
interpreter will check results and report an discrepencies found.
|
||||
|
||||
The test script files are ASCII text files. The filename always ends with
|
||||
".test". Each script is evaluated independently; context does not carry
|
||||
forward from one script to the next. So, for example, the --null command
|
||||
run in one test script does not cause any changes in the behavior of
|
||||
subsequent test scripts. All open database connections are closed
|
||||
at the end of each test script. All database files created by a test
|
||||
script are deleted when the script finishes.
|
||||
|
||||
## Parsing Rules:
|
||||
|
||||
1. The test script is read line by line, where a line is a sequence of
|
||||
characters that runs up to the next '\\n' (0x0a) character or until
|
||||
the end of the file. There is never a need to read ahead past the
|
||||
end of the current line.
|
||||
|
||||
2. If any line contains the string " MODULE_NAME:" (with a space before
|
||||
the initial "M") or "MIXED_MODULE_NAME:" then that test script is
|
||||
incompatible with this spec. Processing of the test script should
|
||||
end immediately. There is no need to read any more of the file.
|
||||
In verbose mode, the interpreter might choose to emit an informational
|
||||
messages saying that the test script was abandoned due to an
|
||||
incompatible module type.
|
||||
|
||||
3. If any line contains the string "SCRIPT_MODULE_NAME:" then the input
|
||||
script is known to be of the correct type for this specification and
|
||||
processing may continue. The "MODULE_NAME" checking in steps 2 and 3
|
||||
may optionally be discontinued after sighting a "SCRIPT_MODULE_NAME".
|
||||
|
||||
4. If any line contains "REQUIRED_PROPERTIES:" and that substring is followed
|
||||
by any non-whitespace text, then the script is not compatible with this
|
||||
spec. Processing should stop immediately. In verbose mode, the
|
||||
interpreter might choose to emit an information message saying that the
|
||||
test script was abandoned due to unsupported requirement properties.
|
||||
|
||||
5. If any line begins with the "\|" (0x7c) character, that indicates that
|
||||
the input script is not compatible with this specification. Processing
|
||||
of the script should stop immediately. In verbose mode, the interpreter
|
||||
might choose to emit an informational message indicating that the
|
||||
test script was abandoned because it contained "a dbtotxt format database
|
||||
specification".
|
||||
|
||||
6. Any line that begins with "#" is a C-preprocessor line. The interpreter
|
||||
described by this spec does not know how to deal with C-preprocessor lines.
|
||||
Hence, processing should be abandoned. In verbose mode, the interpreter
|
||||
might emit an informational message similar to
|
||||
"script NAME abandoned due to C-preprocessor line: ..."
|
||||
|
||||
7. If a line begins with exactly two minus signs followed by a
|
||||
lowercase letter, that is a command. Process commands as described
|
||||
below.
|
||||
|
||||
8. All other lines should be accumulated into the "input buffer".
|
||||
The various commands will have access to this input buffer.
|
||||
Some commands will reset the buffer.
|
||||
|
||||
## Initialization
|
||||
|
||||
The initial state of the interpreter at the start of processing each script
|
||||
is as if the following command sequence had been run:
|
||||
|
||||
> ~~~
|
||||
--close all
|
||||
--db 0
|
||||
--new test.db
|
||||
--null nil
|
||||
~~~
|
||||
|
||||
In words, all database connections are closed except for connection 0 (the
|
||||
default) which is open on an empty database named "test.db". The string
|
||||
"nil" is displayed for NULL column values.
|
||||
|
||||
The only context carried forward after the evaluation of one test script
|
||||
into the evaluation of the next test script is the count of the number of
|
||||
tests run and the number of failures seen.
|
||||
|
||||
## Commands:
|
||||
|
||||
Each command looks like an SQL comment. The command begins at the left
|
||||
margin (no leading space) and starts with exactly 2 minus signs ("-").
|
||||
The command name consists of lowercase letters and maybe a "-" or two.
|
||||
Some commands have arguments.
|
||||
The arguments are separated from the command name by one or more spaces.
|
||||
|
||||
Commands have access to the input buffer and might reset the input buffer.
|
||||
The command can also optionally read (and consume) additional text from
|
||||
script that comes after the command.
|
||||
|
||||
Unknown or unrecognized commands indicate that the script contains features
|
||||
that are not (yet) supported by this specification. Processing of the
|
||||
script should terminate immediately. When this happens and when the
|
||||
interpreter is in a "verbose" mode, the interpreter might choose to emit
|
||||
an informational message along the lines of "test script NAME abandoned
|
||||
due to unsupported command: --whatever".
|
||||
|
||||
The initial implemention will only recognize a few commands. Other
|
||||
commands may be added later. The following is the initial set of
|
||||
commands:
|
||||
|
||||
### The --testcase command
|
||||
|
||||
Every test case starts with a --testcase command. The --testcase
|
||||
command resets both the "input buffer" and the "result buffer". The
|
||||
argument to the --testcase command is the name of the test case. That
|
||||
test case name is used for logging and debugging and when printing
|
||||
errors. The input buffer is set to the body of the test case.
|
||||
|
||||
### The --result command
|
||||
|
||||
The --result command tries to execute the text in the input buffer as SQL.
|
||||
For each row of result coming out of this SQL, the text of that result is
|
||||
appended to the "result buffer". If a result row contains multiple columns,
|
||||
the columns are processed from left to right. For each column, text is
|
||||
appended to the result buffer according to the following rules:
|
||||
|
||||
* If the result buffer already contains some text, append a space.
|
||||
(In this way, all column values and all row values are separated from
|
||||
each other by a single space.)
|
||||
|
||||
* If sqlite3_column_text() returns NULL, then append "nil" - or
|
||||
some other text that is specified by the --null command - and skip
|
||||
all subsequent rules.
|
||||
|
||||
* If sqlite3_column_text() is an empty string, append `{}` to the
|
||||
result buffer and skip all subsequent rules.
|
||||
|
||||
* If sqlite3_column_text() does not contain any special
|
||||
characters, append it to the result buffer without any
|
||||
formatting and skip all subsequent rules. Special characters are:
|
||||
0x00 to 0x20 (inclusive), double-quote (0x22), backslash (0x5c),
|
||||
curly braces (0x7b and 0x7d).
|
||||
|
||||
* If sqlite3_column_text() does not contains curly braces, then put
|
||||
the text inside of `{...}` and append it and skip all subsequent rules.
|
||||
|
||||
* Append the text within double-quotes (`"..."`) and within the text
|
||||
escape '"' and '\\' by prepending a single '\\' and escape any
|
||||
control characters (characters less than 0x20) using octal notation:
|
||||
'\\NNN'.
|
||||
|
||||
If an error is encountered while running the SQL, then append the
|
||||
symbolic C-preprocessor name for the error
|
||||
code (ex: "SQLITE_CONSTRAINT") as if it were a column value. Then append
|
||||
the error message text as if it where a column value. Then stop processing.
|
||||
|
||||
After the SQL text has been run, compare the content of the result buffer
|
||||
against the argument to the --result command and report a testing error if
|
||||
there are any differences.
|
||||
|
||||
The --result command resets the input buffer, but it does not reset
|
||||
the result buffer. This distinction does not matter for the --result
|
||||
command itself, but it is important for related commands like --glob
|
||||
and --notglob. Sometimes test cases will contains a bunch of SQL
|
||||
followed by multiple --glob and/or --notglob statements. All of the
|
||||
globs should be evaluted agains the result buffer correct, but the SQL
|
||||
should only be run once. This is accomplished by resetting the input
|
||||
buffer but not the result buffer.
|
||||
|
||||
### The --glob command
|
||||
|
||||
The --glob command works just like --result except that the argument to
|
||||
--glob is interpreted as a TEST-GLOB pattern and the results are compared
|
||||
using that glob pattern rather than using strcmp(). Other than that,
|
||||
the two operate the same.
|
||||
|
||||
The TEST-GLOB pattern is slightly different for a standard GLOB:
|
||||
|
||||
* The '*' character matches zero or more characters.
|
||||
|
||||
* The '?' character matches any single character
|
||||
|
||||
* The '[...]' character sequence machines a single character
|
||||
in between the brackets.
|
||||
|
||||
* The '#' character matches one or more digits (This is the main
|
||||
difference between standard unix-glob and TEST-GLOB. unix-glob
|
||||
does not have this feature. It was added to because it comes
|
||||
up a lot during SQLite testing.)
|
||||
|
||||
### The --notglob command
|
||||
|
||||
The --notglob command works just like --glob except that it reports an
|
||||
error if the GLOB does match, rather than if the GLOB does not matches.
|
||||
|
||||
### The --oom command
|
||||
|
||||
This command is to be used for out-of-memory testing. It means that
|
||||
OOM errors should be simulated to ensure that SQLite is able to deal with
|
||||
them. This command can be silently ignored for now. We might add support
|
||||
for this later.
|
||||
|
||||
### The --tableresult command
|
||||
|
||||
The --tableresult command works like --glob except that the GLOB pattern
|
||||
to be matched is taken from subsequent lines of the input script up to
|
||||
the next --end. Every span of one or more whitespace characters in this
|
||||
pattern text is collapsed into a single space (0x20).
|
||||
Leading and trailing whitespace are removed from the pattern.
|
||||
The --end that ends the GLOB pattern is not part of the GLOB pattern, but
|
||||
the --end is consumed from the script input.
|
||||
|
||||
### The --new and --open commands
|
||||
|
||||
The --new and --open commands cause a database file to be opened.
|
||||
The name of the file is the argument to the command. The --new command
|
||||
opens an initially empty database (it deletes the file before opening it)
|
||||
whereas the --open command opens an existing database if it already
|
||||
exists.
|
||||
|
||||
### The --db command
|
||||
|
||||
The script interpreter can have up to 7 different SQLite database
|
||||
connections open at a time. The --db command is used to switch between
|
||||
them. The argument to --db is an integer between 0 and 6 that selects
|
||||
which database connection to use moving forward.
|
||||
|
||||
### The --close command
|
||||
|
||||
The --close command causes an existing database connection to close.
|
||||
This command is a no-op if the database connection is not currently
|
||||
open. There can be up to 7 different database connections, numbered 0
|
||||
through 6. The number of the database connection to close is an
|
||||
argument to the --close command, which will fail if an out-of-range
|
||||
value is provided. Or if the argument to --close is "all" then all
|
||||
open database connections are closed. If passed no argument, the
|
||||
currently-active database is assumed.
|
||||
|
||||
### The --null command
|
||||
|
||||
The NULL command changes the text that is used to represent SQL NULL
|
||||
values in the result buffer.
|
||||
|
||||
### The --run command
|
||||
|
||||
The --run command executes text in the input buffer as if it where SQL.
|
||||
However, nothing is added to the result buffer. Any output from the SQL
|
||||
is silently ignored. Errors in the SQL are silently ignored.
|
||||
|
||||
The --run command normally executes the SQL in the current database
|
||||
connection. However, if --run has an argument that is an integer between
|
||||
0 and 6 then the SQL is run in the alternative database connection specified
|
||||
by that argument.
|
||||
|
||||
### The --json and --json-block commands
|
||||
|
||||
The --json and --json-block commands work like --result and --tableresult,
|
||||
respectively. The difference is that column values are appended to the
|
||||
result buffer literally, without ever enclosing the values in `{...}` or
|
||||
`"..."` and without escaping any characters in the column value and comparison
|
||||
is always an exact strcmp() not a GLOB.
|
||||
|
||||
### The --print command
|
||||
|
||||
The --print command emits both its arguments and its body (if any) to
|
||||
stdout, indenting each line of output.
|
||||
|
||||
### The --column-names command
|
||||
|
||||
The --column-names command requires 0 or 1 as an argument, to disable
|
||||
resp. enable it, and modifies SQL execution to include column names
|
||||
in output. When this option is on, each column value emitted gets
|
||||
prefixed by its column name, with a single space between them.
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
** This is a comment. There are many like it but this one is mine.
|
||||
**
|
||||
** SCRIPT_MODULE_NAME: sanity-check
|
||||
** xMIXED_MODULE_NAME: mixed-module
|
||||
** xMODULE_NAME: module-name
|
||||
** xREQUIRED_PROPERTIES: small fast reliable
|
||||
** xREQUIRED_PROPERTIES: RECURSIVE_TRIGGERS
|
||||
** xREQUIRED_PROPERTIES: TEMPSTORE_FILE TEMPSTORE_MEM
|
||||
** xREQUIRED_PROPERTIES: AUTOVACUUM INCRVACUUM
|
||||
**
|
||||
*/
|
||||
--print starting up 😃
|
||||
--close all
|
||||
--oom
|
||||
--db 0
|
||||
--new my.db
|
||||
--null zilch
|
||||
--testcase 1.0
|
||||
SELECT 1, null;
|
||||
--result 1 zilch
|
||||
--glob *zil*
|
||||
--notglob *ZIL*
|
||||
SELECT 1, 2;
|
||||
intentional error;
|
||||
--run
|
||||
--testcase json-1
|
||||
SELECT json_array(1,2,3)
|
||||
--json [1,2,3]
|
||||
--testcase tableresult-1
|
||||
select 1, 'a';
|
||||
select 2, 'b';
|
||||
--tableresult
|
||||
# [a-z]
|
||||
2 b
|
||||
--end
|
||||
--testcase json-block-1
|
||||
select json_array(1,2,3);
|
||||
select json_object('a',1,'b',2);
|
||||
--json-block
|
||||
[1,2,3]
|
||||
{"a":1,"b":2}
|
||||
--end
|
||||
--testcase col-names-on
|
||||
--column-names 1
|
||||
select 1 as 'a', 2 as 'b';
|
||||
--result a 1 b 2
|
||||
--testcase col-names-off
|
||||
--column-names 0
|
||||
select 1 as 'a', 2 as 'b';
|
||||
--result 1 2
|
||||
--close
|
||||
--print reached the end 😃
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user