Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 676f17a570 |
+2
-22
@@ -1291,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
|
||||
|
||||
@@ -1320,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)
|
||||
@@ -1525,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
|
||||
@@ -1589,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
|
||||
@@ -2518,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.43.0])
|
||||
AC_INIT([sqlite],[3.42.0])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+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;
|
||||
|
||||
+227
-1237
File diff suppressed because it is too large
Load Diff
+7
-22
@@ -1624,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 );
|
||||
@@ -1653,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]);
|
||||
}
|
||||
@@ -1677,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);
|
||||
}
|
||||
|
||||
@@ -1685,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 - DELETE is 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
|
||||
@@ -1777,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;
|
||||
}
|
||||
@@ -2544,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;
|
||||
}
|
||||
|
||||
+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 */
|
||||
|
||||
@@ -1,271 +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,208 +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,196 +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,248 +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,59 +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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -50,25 +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}
|
||||
|
||||
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,405 +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.jni.tester := $(dir.src.jni)/tester
|
||||
mkdir := mkdir -p
|
||||
$(dir.bld.c):
|
||||
$(mkdir) $@
|
||||
|
||||
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/tester/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.
|
||||
# It would be a real doozy to bind to JNI.
|
||||
enable.fts5 ?= 1
|
||||
# If enable.tester is 0, the org/sqlite/jni/tester/* bits are elided.
|
||||
enable.tester ?= 1
|
||||
|
||||
# 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)/%,\
|
||||
AggregateFunction.java \
|
||||
AuthorizerCallback.java \
|
||||
AutoExtensionCallback.java \
|
||||
BusyHandlerCallback.java \
|
||||
CollationCallback.java \
|
||||
CollationNeededCallback.java \
|
||||
CommitHookCallback.java \
|
||||
ConfigSqllogCallback.java \
|
||||
NativePointerHolder.java \
|
||||
NotNull.java \
|
||||
Nullable.java \
|
||||
OutputPointer.java \
|
||||
PreupdateHookCallback.java \
|
||||
ProgressHandlerCallback.java \
|
||||
ResultCode.java \
|
||||
RollbackHookCallback.java \
|
||||
ScalarFunction.java \
|
||||
SQLFunction.java \
|
||||
SQLite3Jni.java \
|
||||
Tester1.java \
|
||||
TraceV2Callback.java \
|
||||
UpdateHookCallback.java \
|
||||
ValueHolder.java \
|
||||
WindowFunction.java \
|
||||
XDestroyCallback.java \
|
||||
package-info.java \
|
||||
sqlite3.java \
|
||||
sqlite3_context.java \
|
||||
sqlite3_stmt.java \
|
||||
sqlite3_value.java \
|
||||
)
|
||||
ifeq (1,$(enable.fts5))
|
||||
JAVA_FILES.main += $(patsubst %,$(dir.src.jni)/%,\
|
||||
fts5_api.java \
|
||||
fts5_extension_function.java \
|
||||
fts5_tokenizer.java \
|
||||
Fts5.java \
|
||||
Fts5Context.java \
|
||||
Fts5ExtensionApi.java \
|
||||
Fts5Function.java \
|
||||
Fts5PhraseIter.java \
|
||||
Fts5Tokenizer.java \
|
||||
TesterFts5.java \
|
||||
)
|
||||
endif
|
||||
JAVA_FILES.tester := $(dir.src.jni.tester)/SQLTester.java
|
||||
|
||||
CLASS_FILES.main := $(JAVA_FILES.main:.java=.class)
|
||||
CLASS_FILES.tester := $(JAVA_FILES.tester:.java=.class)
|
||||
|
||||
JAVA_FILES += $(JAVA_FILES.main)
|
||||
ifeq (1,$(enable.tester))
|
||||
JAVA_FILES += $(JAVA_FILES.tester)
|
||||
endif
|
||||
|
||||
CLASS_FILES :=
|
||||
define DOTCLASS_DEPS
|
||||
$(1).class: $(1).java $(MAKEFILE)
|
||||
all: $(1).class
|
||||
CLASS_FILES += $(1).class
|
||||
endef
|
||||
$(foreach B,$(basename $(JAVA_FILES)),$(eval $(call DOTCLASS_DEPS,$(B))))
|
||||
$(CLASS_FILES.tester): $(CLASS_FILES.main)
|
||||
javac.flags ?= -Xlint:unchecked -Xlint:deprecation
|
||||
java.flags ?=
|
||||
jnicheck ?= 1
|
||||
ifeq (1,$(jnicheck))
|
||||
java.flags += -Xcheck:jni
|
||||
endif
|
||||
$(SQLite3Jni.class): $(JAVA_FILES)
|
||||
$(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES)
|
||||
all: $(SQLite3Jni.class)
|
||||
#.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
|
||||
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_SQLLOG \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_THREADSAFE=$(opt.threadsafe) \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_C=$(sqlite3.c) \
|
||||
-DSQLITE_JNI_FATAL_OOM=0 \
|
||||
-DSQLITE_DEBUG
|
||||
|
||||
SQLITE_OPT += -g -DDEBUG -UNDEBUG
|
||||
|
||||
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
|
||||
sqlite3-jni.dll := $(dir.bld.c)/libsqlite3-jni.so
|
||||
# All javac-generated .h files must be listed in $(sqlite3-jni.h.in):
|
||||
sqlite3-jni.h.in :=
|
||||
define ADD_JNI_H
|
||||
sqlite3-jni.h.in += $$(dir.bld.c)/org_sqlite_jni_$(1).h
|
||||
$$(dir.bld.c)/org_sqlite_jni_$(1).h: $$(dir.src.jni)/$(1).java
|
||||
endef
|
||||
$(eval $(call ADD_JNI_H,SQLite3Jni))
|
||||
ifeq (1,$(enable.fts5))
|
||||
$(eval $(call ADD_JNI_H,Fts5ExtensionApi))
|
||||
$(eval $(call ADD_JNI_H,fts5_api))
|
||||
$(eval $(call ADD_JNI_H,fts5_tokenizer))
|
||||
endif
|
||||
ifeq (1,$(enable.tester))
|
||||
sqlite3-jni.h.in += $(dir.bld.c)/org_sqlite_jni_tester_SQLTester.h
|
||||
$(dir.bld.c)/org_sqlite_jni_tester_SQLTester.h: $(dir.src.jni.tester)/SQLTester.java
|
||||
endif
|
||||
#sqlite3-jni.dll.cfiles := $(dir.src.c)
|
||||
sqlite3-jni.dll.cflags = \
|
||||
-fPIC \
|
||||
-I. \
|
||||
-I$(dir $(sqlite3.h)) \
|
||||
-I$(dir.src.c) \
|
||||
-I$(JDK_HOME)/include \
|
||||
$(patsubst %,-I%,$(patsubst %.h,,$(wildcard $(JDK_HOME)/include/*))) \
|
||||
-Wall
|
||||
# Using (-Wall -Wextra) triggers an untennable number of
|
||||
# gcc warnings from sqlite3.c for mundane things like
|
||||
# unused parameters.
|
||||
#
|
||||
# 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.
|
||||
########################################################################
|
||||
ifeq (1,$(enable.tester))
|
||||
sqlite3-jni.dll.cflags += -DSQLITE_JNI_ENABLE_SQLTester
|
||||
endif
|
||||
$(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
|
||||
cat $(sqlite3-jni.h.in) > $@
|
||||
$(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h)
|
||||
$(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE)
|
||||
$(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \
|
||||
$(sqlite3-jni.c) -shared -o $@
|
||||
all: $(sqlite3-jni.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 := $(SQLite3Jni.class) $(sqlite3-jni.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 5 -r 20 -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) $(sqlite3-jni.dll)
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.tester.SQLTester $(tester.flags) $(tester.scripts)
|
||||
tester: tester-local
|
||||
else
|
||||
tester:
|
||||
@echo "SQLTester support is disabled. Build with enable.tester=1 to enable it."
|
||||
endif
|
||||
|
||||
tester.extdir.default := src/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.tester.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
|
||||
$(MAKE) opt.threadsafe=0 tests clean
|
||||
$(MAKE) opt.threadsafe=1 tests clean
|
||||
$(MAKE) opt.threadsafe=2 tests clean
|
||||
|
||||
|
||||
########################################################################
|
||||
# jar bundle...
|
||||
package.jar.in := $(abspath $(dir.src)/jar.in)
|
||||
CLEAN_FILES += $(package.jar.in)
|
||||
$(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main)
|
||||
cd $(dir.src); ls -1 org/sqlite/jni/*.java org/sqlite/jni/*.class > $@
|
||||
@echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag."
|
||||
@echo "e.g. java -jar $@ -Djava.library.path=bld"
|
||||
|
||||
$(package.jar): $(CLASS_FILES) $(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)
|
||||
|
||||
jar: $(package.jar)
|
||||
|
||||
########################################################################
|
||||
# javadoc...
|
||||
dir.doc := $(dir.jni)/javadoc
|
||||
doc.index := $(dir.doc)/index.html
|
||||
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
|
||||
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
|
||||
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet org.sqlite.jni
|
||||
@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 \
|
||||
$(sqlite3-jni.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 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,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;
|
||||
|
||||
|
||||
/**
|
||||
SQLFunction subclass for creating 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> extends 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 Window<T>
|
||||
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,29 +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 sqlite3_set_authorizer().
|
||||
*/
|
||||
public interface AuthorizerCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level
|
||||
sqlite3_set_authorizer() callback.
|
||||
|
||||
Must not throw.
|
||||
*/
|
||||
int call(int opId, @Nullable String s1, @Nullable String s2,
|
||||
@Nullable String s3, @Nullable String s4);
|
||||
|
||||
}
|
||||
@@ -1,41 +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 callback for use with the sqlite3_auto_extension() family of
|
||||
APIs.
|
||||
*/
|
||||
public interface AutoExtensionCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
Must function as described for a C-level
|
||||
sqlite3_auto_extension() callback, with the caveat that the
|
||||
signature is shorter.
|
||||
|
||||
This callback may throw and the exception's error message will
|
||||
be set as the db's error string.
|
||||
|
||||
Tips for implementations:
|
||||
|
||||
- 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.
|
||||
|
||||
- If this routine is stateful, it may be useful to make the
|
||||
overridden method synchronized.
|
||||
|
||||
- Results are undefined if db is closed by an auto-extension.
|
||||
*/
|
||||
int call(sqlite3 db);
|
||||
}
|
||||
@@ -1,30 +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 sqlite3_busy_handler()
|
||||
*/
|
||||
public abstract class BusyHandlerCallback implements SQLite3CallbackProxy {
|
||||
/**
|
||||
Must function as documented for the C-level
|
||||
sqlite3_busy_handler() callback argument, minus the (void*)
|
||||
argument the C-level function requires.
|
||||
|
||||
Any exceptions thrown by this callback are suppressed in order to
|
||||
retain the C-style API semantics of the JNI bindings.
|
||||
*/
|
||||
public abstract int call(int n);
|
||||
}
|
||||
@@ -1,31 +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 sqlite3_create_collation()
|
||||
*/
|
||||
public abstract class CollationCallback
|
||||
implements SQLite3CallbackProxy, XDestroyCallback {
|
||||
/**
|
||||
Must compare the given byte arrays using memcmp() semantics.
|
||||
*/
|
||||
public abstract int call(byte[] lhs, byte[] rhs);
|
||||
|
||||
/**
|
||||
Called by SQLite when the collation is destroyed. If a collation
|
||||
requires custom cleanup, override this method.
|
||||
*/
|
||||
public 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 sqlite3_collation_needed().
|
||||
*/
|
||||
public interface CollationNeededCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
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 sqlite3_commit_hook()
|
||||
*/
|
||||
public interface CommitHookCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
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 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,40 +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;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
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 {
|
||||
/* Not used */
|
||||
private Fts5(){}
|
||||
|
||||
/**
|
||||
Callback type for use with xTokenize() variants
|
||||
*/
|
||||
public static interface xTokenize_callback {
|
||||
int call(int tFlags, byte[] txt, int iStart, int iEnd);
|
||||
}
|
||||
|
||||
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,23 +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;
|
||||
|
||||
/**
|
||||
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,86 +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;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
ALMOST COMPLETELY UNTESTED.
|
||||
|
||||
FAR FROM COMPLETE and the feasibility of binding this to Java
|
||||
is still undetermined. This might be removed.
|
||||
*/
|
||||
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 xCallback(Fts5ExtensionApi fapi, Fts5Context cx);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the singleton instance of this class.
|
||||
*/
|
||||
public static native Fts5ExtensionApi getInstance();
|
||||
|
||||
public native int xColumnCount(@NotNull Fts5Context fcx);
|
||||
public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.Int32 pnToken);
|
||||
public native int xColumnText(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.String txt);
|
||||
public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
|
||||
@NotNull OutputPointer.Int64 pnToken);
|
||||
public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
|
||||
public native int xInst(@NotNull Fts5Context cx, int iIdx,
|
||||
@NotNull OutputPointer.Int32 piPhrase,
|
||||
@NotNull OutputPointer.Int32 piCol,
|
||||
@NotNull OutputPointer.Int32 piOff);
|
||||
public native int xInstCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int32 pnInst);
|
||||
public native int xPhraseCount(@NotNull Fts5Context fcx);
|
||||
public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
public native void xPhraseNext(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
public native void xPhraseNextColumn(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
|
||||
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull xQueryPhraseCallback callback);
|
||||
public native int xRowCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int64 nRow);
|
||||
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. */
|
||||
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
|
||||
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
|
||||
@NotNull Fts5.xTokenize_callback callback);
|
||||
|
||||
public native Object xUserData(Fts5Context cx);
|
||||
//^^^ returns the pointer passed as the 3rd arg to the C-level
|
||||
// fts5_api::xCreateFunction().
|
||||
}
|
||||
@@ -1,27 +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;
|
||||
|
||||
/**
|
||||
Fts5Function is used in conjunction with the
|
||||
sqlite3_create_fts_function() JNI-bound API to give that native code
|
||||
access to the callback functions needed in order to implement
|
||||
FTS5 auxiliary functions in Java.
|
||||
*/
|
||||
public abstract class Fts5Function {
|
||||
|
||||
public abstract void xFunction(Fts5ExtensionApi pApi, Fts5Context pFts,
|
||||
sqlite3_context pCtx, sqlite3_value argv[]);
|
||||
public void xDestroy() {}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
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,30 +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;
|
||||
|
||||
/**
|
||||
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,33 +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;
|
||||
public final long getNativePointer(){ return nativePointer; }
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
This annotation is for flagging parameters which may not legally be
|
||||
null. 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>This annotation is informational only. No policy is in place to
|
||||
programmatically ensure that NotNull is conformed to in client
|
||||
code.
|
||||
|
||||
<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 NotNull{}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
<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,190 +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 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 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,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 sqlite3_preupdate_hook().
|
||||
*/
|
||||
public interface PreupdateHookCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level sqlite3_preupdate_hook()
|
||||
callback. Must not throw.
|
||||
*/
|
||||
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 sqlite3_progress_handler()
|
||||
*/
|
||||
public interface ProgressHandlerCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_progress_handler() callback.
|
||||
|
||||
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 sqlite3_rollback_hook()
|
||||
*/
|
||||
public interface RollbackHookCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_rollback_hook()
|
||||
callback. Must not throw.
|
||||
*/
|
||||
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 abstract class 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 Aggregate<T> and Window<T> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
/**
|
||||
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>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 sqlite3_ prefix, plus a Callback
|
||||
suffix. e.g. sqlite3_busy_handler()'s callback is named
|
||||
BusyHandlerCallback. Exceptions are made where that would
|
||||
potentially be ambiguous, e.g. ConfigSqllogCallback instead of
|
||||
config_callback because the sqlite3_config() interface may need to
|
||||
support more callback types in the future.
|
||||
|
||||
<p>2) They all have a call() method but its signature is
|
||||
callback-specific.
|
||||
*/
|
||||
public interface SQLite3CallbackProxy {}
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
SQLFunction subclass for creating scalar functions.
|
||||
*/
|
||||
public abstract class ScalarFunction extends 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.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,89 +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.*;
|
||||
|
||||
public class TesterFts5 {
|
||||
|
||||
private static void test1(){
|
||||
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";
|
||||
ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
|
||||
ValueHolder<Integer> xFuncCount = new ValueHolder<>(0);
|
||||
final fts5_extension_function func = new fts5_extension_function(){
|
||||
public void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]){
|
||||
int nCols = ext.xColumnCount(fCx);
|
||||
affirm( 2 == nCols );
|
||||
affirm( nCols == argv.length );
|
||||
affirm( ext.xUserData(fCx) == pUserData );
|
||||
if(true){
|
||||
OutputPointer.String op = new OutputPointer.String();
|
||||
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_text(argv[i])) );
|
||||
//outln("xFunction col "+i+": "+val);
|
||||
}
|
||||
}
|
||||
++xFuncCount.value;
|
||||
}
|
||||
public void xDestroy(){
|
||||
xDestroyCalled.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
int rc = fApi.xCreateFunction("myaux", pUserData, func);
|
||||
affirm( 0==rc );
|
||||
|
||||
affirm( 0==xFuncCount.value );
|
||||
execSql(db, "select myaux(ft,a,b) from ft;");
|
||||
affirm( 2==xFuncCount.value );
|
||||
affirm( !xDestroyCalled.value );
|
||||
sqlite3_close_v2(db);
|
||||
affirm( xDestroyCalled.value );
|
||||
}
|
||||
|
||||
public TesterFts5(boolean verbose){
|
||||
if(verbose){
|
||||
final long timeStart = System.currentTimeMillis();
|
||||
final int oldAffirmCount = Tester1.affirmCount;
|
||||
test1();
|
||||
final int affirmCount = Tester1.affirmCount - oldAffirmCount;
|
||||
final long timeEnd = System.currentTimeMillis();
|
||||
outln("FTS5 Tests done. Assertions checked = ",affirmCount,
|
||||
", Total time = ",(timeEnd - timeStart),"ms");
|
||||
}else{
|
||||
test1();
|
||||
}
|
||||
}
|
||||
public TesterFts5(){ this(false); }
|
||||
}
|
||||
@@ -1,49 +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 proxy for use with sqlite3_trace_v2().
|
||||
*/
|
||||
public interface TraceV2Callback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
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.
|
||||
|
||||
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, 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 sqlite3_update_hook().
|
||||
*/
|
||||
public interface UpdateHookCallback extends SQLite3CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level sqlite3_update_hook()
|
||||
callback. Must not throw.
|
||||
*/
|
||||
void call(int opId, String dbName, String tableName, long rowId);
|
||||
}
|
||||
@@ -1,25 +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 class which simply holds a single value. Its current use
|
||||
is for communicating values out of anonymous classes, as doing so
|
||||
requires a "final" reference.
|
||||
*/
|
||||
public class ValueHolder<T> {
|
||||
public T value;
|
||||
public ValueHolder(){}
|
||||
public ValueHolder(T v){value = v;}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
/**
|
||||
An SQLFunction subclass for creating window functions. Note that
|
||||
Window<T> inherits from Aggregate<T> and each instance is
|
||||
required to implement the inherited abstract methods from that
|
||||
class. See Aggregate<T> 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,29 +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.
|
||||
*/
|
||||
public void xDestroy();
|
||||
}
|
||||
@@ -1,69 +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;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
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 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);
|
||||
|
||||
// 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
|
||||
// );
|
||||
|
||||
// /* Create a new auxiliary function */
|
||||
// int (*xCreateFunction)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_extension_function xFunction,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
public synchronized native int xCreateFunction(@NotNull String name,
|
||||
@Nullable Object userData,
|
||||
@NotNull fts5_extension_function xFunction);
|
||||
|
||||
public int xCreateFunction(@NotNull String name,
|
||||
@NotNull fts5_extension_function xFunction){
|
||||
return xCreateFunction(name, null, xFunction);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +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;
|
||||
|
||||
/**
|
||||
JNI-level wrapper for C's fts5_extension_function type.
|
||||
|
||||
*/
|
||||
public abstract class 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().
|
||||
*/
|
||||
public abstract void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]);
|
||||
//! Optionally 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;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
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 Fts5.xTokenize_callback 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,43 +0,0 @@
|
||||
/**
|
||||
This package houses a JNI binding to the SQLite3 C API.
|
||||
|
||||
<p>The docs are in progress.
|
||||
|
||||
<p>The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}.
|
||||
|
||||
<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:
|
||||
|
||||
<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>When built with SQLITE_THREADSAFE=0 then no threading guarantees
|
||||
are provided and multi-threaded use of the library will provoke
|
||||
undefined behavior.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
@@ -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 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> {
|
||||
// Only invoked from JNI
|
||||
private sqlite3(){}
|
||||
|
||||
public String toString(){
|
||||
long ptr = getNativePointer();
|
||||
if( 0==ptr ){
|
||||
return sqlite3.class.getSimpleName()+"@null";
|
||||
}
|
||||
String fn = SQLite3Jni.sqlite3_db_filename(this, "main");
|
||||
return sqlite3.class.getSimpleName()
|
||||
+"@"+String.format("0x%08x",ptr)
|
||||
+"["+((null == fn) ? "<unnamed>" : fn)+"]"
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -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,25 +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> {
|
||||
// Only invoked from JNI.
|
||||
private sqlite3_stmt(){}
|
||||
}
|
||||
@@ -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(){}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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,52 +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_MEM TEMPSTORE_FILE
|
||||
**
|
||||
*/
|
||||
--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 😃
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
** This script must be marked as ignored because it contains
|
||||
** content which triggers that condition.
|
||||
**
|
||||
** SCRIPT_MODULE_NAME: ignored
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
||||
+3
-3
@@ -642,7 +642,7 @@ static Decimal *decimalFromDouble(double r){
|
||||
|
||||
/*
|
||||
** SQL Function: decimal(X)
|
||||
** OR: decimal_exp(X)
|
||||
** OR: decimal_sci(X)
|
||||
**
|
||||
** Convert input X into decimal and then back into text.
|
||||
**
|
||||
@@ -650,7 +650,7 @@ static Decimal *decimalFromDouble(double r){
|
||||
** point value is done. Or if X is an 8-byte blob, it is interpreted
|
||||
** as a float and similarly expanded.
|
||||
**
|
||||
** The decimal_exp(X) function returns the result in exponential notation.
|
||||
** The decimal_sci(X) function returns the result in scientific notation.
|
||||
** decimal(X) returns a complete decimal, without the e+NNN at the end.
|
||||
*/
|
||||
static void decimalFunc(
|
||||
@@ -853,7 +853,7 @@ int sqlite3_decimal_init(
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} aFunc[] = {
|
||||
{ "decimal", 1, 0, decimalFunc },
|
||||
{ "decimal_exp", 1, 1, decimalFunc },
|
||||
{ "decimal_sci", 1, 1, decimalFunc },
|
||||
{ "decimal_cmp", 2, 0, decimalCmpFunc },
|
||||
{ "decimal_add", 2, 0, decimalAddFunc },
|
||||
{ "decimal_sub", 2, 0, decimalSubFunc },
|
||||
|
||||
+1
-1
@@ -1919,7 +1919,7 @@ static int rtreeFilter(
|
||||
#else
|
||||
p->u.rValue = (double)iVal;
|
||||
if( iVal>=((sqlite3_int64)1)<<48
|
||||
|| iVal<=-(((sqlite3_int64)1)<<48)
|
||||
|| -iVal>=((sqlite3_int64)1)<<48
|
||||
){
|
||||
if( p->op==RTREE_LT ) p->op = RTREE_LE;
|
||||
if( p->op==RTREE_GT ) p->op = RTREE_GE;
|
||||
|
||||
@@ -891,7 +891,6 @@ static int sessionPreupdateEqual(
|
||||
rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
|
||||
}
|
||||
assert( rc==SQLITE_OK );
|
||||
(void)rc; /* Suppress warning about unused variable */
|
||||
if( sqlite3_value_type(pVal)!=eType ) return 0;
|
||||
|
||||
/* A SessionChange object never has a NULL value in a PK column */
|
||||
|
||||
+111
-138
@@ -18,6 +18,8 @@
|
||||
# quick, q = do just a minimal build (sqlite3.js/wasm, tester1) for
|
||||
# faster development-mode turnaround.
|
||||
#
|
||||
# qo2, qoz = a combination of quick+o2/oz.
|
||||
#
|
||||
# dist = create end user deliverables. Add dist.build=oX to build
|
||||
# with a specific optimization level, where oX is one of the
|
||||
# above-listed o? or qo? target names.
|
||||
@@ -44,12 +46,11 @@
|
||||
# $(eval), or at least centralize the setup of the numerous vars
|
||||
# related to each build variant $(JS_BUILD_MODES).
|
||||
#
|
||||
default: all
|
||||
#default: quick
|
||||
SHELL := $(shell which bash 2>/dev/null)
|
||||
MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
CLEAN_FILES :=
|
||||
DISTCLEAN_FILES := ./--dummy--
|
||||
default: all
|
||||
release: oz
|
||||
# JS_BUILD_MODES exists solely to reduce repetition in documentation
|
||||
# below.
|
||||
@@ -67,6 +68,13 @@ ifeq (,$(emcc.version))
|
||||
else
|
||||
$(info using emcc version [$(emcc.version)])
|
||||
endif
|
||||
emcc.version := $(shell "$(emcc.bin)" --version | sed -n 1p \
|
||||
| sed -e 's/^.* \([3-9][^ ]*\) .*$$/\1/;')
|
||||
ifeq (,$(emcc.version))
|
||||
$(warning Cannot determine emcc version. This might unduly impact build flags.)
|
||||
else
|
||||
$(info using emcc version [$(emcc.version)])
|
||||
endif
|
||||
|
||||
wasm-strip ?= $(shell which wasm-strip 2>/dev/null)
|
||||
ifeq (,$(filter clean,$(MAKECMDGOALS)))
|
||||
@@ -151,9 +159,6 @@ endif
|
||||
# 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 := $(dir.top)/sqlite3.c
|
||||
sqlite3.c ?= $(firstword $(wildcard $(dir.top)/sqlite3-see.c) $(sqlite3.canonical.c))
|
||||
sqlite3.h := $(dir.top)/sqlite3.h
|
||||
@@ -179,19 +184,14 @@ SQLITE_OPT = \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_UTF16 \
|
||||
-DSQLITE_OMIT_SHARED_CACHE \
|
||||
-DSQLITE_OMIT_WAL \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_TEMP_STORE=2 \
|
||||
-DSQLITE_TEMP_STORE=3 \
|
||||
-DSQLITE_OS_KV_OPTIONAL=1 \
|
||||
'-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \
|
||||
-DSQLITE_USE_URI=1 \
|
||||
-DSQLITE_WASM_ENABLE_C_TESTS \
|
||||
-DSQLITE_C=$(sqlite3.c)
|
||||
#SQLITE_OPT += -DSQLITE_DEBUG
|
||||
# Enabling SQLITE_DEBUG will break sqlite3_wasm_vfs_create_file()
|
||||
# (and thus sqlite3_js_vfs_create_file()). Those functions are
|
||||
# deprecated and alternatives are in place, but this crash behavior
|
||||
# can be used to find errant uses of sqlite3_js_vfs_create_file()
|
||||
# in client code.
|
||||
|
||||
.NOTPARALLEL: $(sqlite3.h)
|
||||
$(sqlite3.h):
|
||||
@@ -246,10 +246,10 @@ endif
|
||||
# embedding in the JS files and in building the distribution zip file.
|
||||
# It must NOT be in $(dir.tmp) because we need it to survive the
|
||||
# cleanup process for the dist build to work properly.
|
||||
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
|
||||
bin.version-info := $(dir.wasm)/version-info
|
||||
$(bin.version-info): $(dir.wasm)/version-info.c $(sqlite3.h) $(MAKEFILE)
|
||||
$(CC) -O0 -I$(dir $(sqlite3.c)) -o $@ $<
|
||||
DISTCLEAN_FILES += $(bin.version-info)
|
||||
|
||||
# bin.stripcomments is used for stripping C/C++-style comments from JS
|
||||
# files. The JS files contain large chunks of documentation which we
|
||||
@@ -379,17 +379,20 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||
|
||||
# SOAP.js is an external API file which is part of our distribution
|
||||
# "External" API files which are part of our distribution
|
||||
# but not part of the sqlite3-api.js amalgamation.
|
||||
SOAP.js := $(dir.api)/sqlite3-opfs-async-proxy.js
|
||||
SOAP.js.bld := $(dir.dout)/$(notdir $(SOAP.js))
|
||||
sqlite3-api.ext.jses += $(SOAP.js.bld)
|
||||
$(SOAP.js.bld): $(SOAP.js)
|
||||
cp $< $@
|
||||
|
||||
# COPY_XAPI = a $(call)able function to copy $1 to $(dir.dout), where
|
||||
# $1 must be one of the "external" JS API files.
|
||||
define COPY_XAPI
|
||||
sqlite3-api.ext.jses += $$(dir.dout)/$$(notdir $(1))
|
||||
$$(dir.dout)/$$(notdir $(1)): $(1) $$(MAKEFILE)
|
||||
cp $$< $$@
|
||||
endef
|
||||
$(foreach X,$(SOAP.js),\
|
||||
$(eval $(call COPY_XAPI,$(X))))
|
||||
all quick: $(sqlite3-api.ext.jses)
|
||||
q: quick
|
||||
|
||||
@@ -612,40 +615,26 @@ $(post-js.js.in): $(post-jses.js) $(MAKEFILE)
|
||||
|
||||
########################################################################
|
||||
# call-make-pre-post is a $(call)able which creates rules for
|
||||
# pre-js-$(1)-$(2).js. $1 = the base name of the JS file on whose
|
||||
# behalf this pre-js is for (one of: sqlite3, sqlite3-wasmfs). $2 is
|
||||
# the build mode: one of $(JS_BUILD_MODES). This sets up
|
||||
# --[extern-][pre/post]-js flags in $(pre-post-$(1)-$(2).flags) and
|
||||
# dependencies in $(pre-post-$(1)-$(2).deps). The resulting files get
|
||||
# filtered using $(C-PP.FILTER). Any flags necessary for such
|
||||
# filtering need to be set in $(c-pp.D.$(1)-$(2)) before $(call)ing
|
||||
# this.
|
||||
# pre-js-$(1).js. $1 = the base name of the JS file on whose behalf
|
||||
# this pre-js is for (one of: sqlite3, sqlite3-wasmfs). $2 is the
|
||||
# build mode: one of $(JS_BUILD_MODES). This
|
||||
# sets up --[extern-][pre/post]-js flags in
|
||||
# $(pre-post-$(1).flags.$(2)) and dependencies in
|
||||
# $(pre-post-$(1).deps.$(2)).
|
||||
define call-make-pre-post
|
||||
pre-post-$(1)-$(2).flags ?=
|
||||
pre-js.js.$(1)-$(2) := $$(dir.tmp)/pre-js.$(1)-$(2).intermediary.js
|
||||
$$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
post-js.js.$(1)-$(2) := $$(dir.tmp)/post-js.$(1)-$(2).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
extern-post-js.js.$(1)-$(2) := $$(dir.tmp)/extern-post-js.$(1)-$(2).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)-$(2)),$$(c-pp.D.$(1)-$(2))))
|
||||
pre-post-common.flags.$(1)-$(2) := \
|
||||
$$(pre-post-common.flags) \
|
||||
--post-js=$$(post-js.js.$(1)-$(2)) \
|
||||
--extern-post-js=$$(extern-post-js.js.$(1)-$(2))
|
||||
pre-post-jses.$(1)-$(2).deps := $$(pre-post-jses.deps.common) \
|
||||
$$(post-js.js.$(1)-$(2)) $$(extern-post-js.js.$(1)-$(2))
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(1)-$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(1)-$(2)) $$@
|
||||
pre-post-$(1).flags.$(2) ?=
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js: $$(pre-js.js.$(2)) $$(MAKEFILE)
|
||||
cp $$(pre-js.js.$(2)) $$@
|
||||
@if [ sqlite3-wasmfs = $(1) ]; then \
|
||||
echo "delete Module[xNameOfInstantiateWasm] /*for WASMFS build*/;"; \
|
||||
elif [ sqlite3 != $(1) ]; then \
|
||||
echo "Module[xNameOfInstantiateWasm].uri = '$(1).wasm';"; \
|
||||
fi >> $$@
|
||||
pre-post-$(1)-$(2).deps := \
|
||||
$$(pre-post-jses.$(1)-$(2).deps) \
|
||||
pre-post-$(1).deps.$(2) := \
|
||||
$$(pre-post-jses.deps.$(2)) \
|
||||
$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
pre-post-$(1)-$(2).flags += \
|
||||
$$(pre-post-common.flags.$(1)-$(2)) \
|
||||
pre-post-$(1).flags.$(2) += \
|
||||
$$(pre-post-common.flags.$(2)) \
|
||||
--pre-js=$$(dir.tmp)/pre-js-$(1)-$(2).js
|
||||
endef
|
||||
# /post-js and pre-js
|
||||
@@ -656,8 +645,7 @@ endef
|
||||
# https://github.com/emscripten-core/emscripten/issues/14383
|
||||
sqlite3.wasm := $(dir.dout)/sqlite3.wasm
|
||||
sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
|
||||
sqlite3-wasm.cfiles := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
|
||||
sqlite3-wasm.cses := $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c)
|
||||
# sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter
|
||||
# (predictably) results in a slightly faster binary. We're close
|
||||
# enough to the target speed requirements that the 500ms makes a
|
||||
@@ -667,8 +655,7 @@ sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
|
||||
# SQLITE3.xJS.EXPORT-DEFAULT is part of SQLITE3-WASMFS.xJS.RECIPE and
|
||||
# SETUP_LIB_BUILD_MODE, factored into a separate piece to avoid code
|
||||
# duplication. $1 is 1 if the build mode needs this workaround (esm,
|
||||
# bundler-friendly, node) and 0 if not (vanilla). $2 must be empty for
|
||||
# all builds except sqlite3-wasmfs.mjs, in which case it must be 1.
|
||||
# bundler-friendly) and 0 if not (vanilla).
|
||||
#
|
||||
# Reminder for ESM builds: even if we use -sEXPORT_ES6=0, emcc _still_
|
||||
# adds:
|
||||
@@ -682,20 +669,13 @@ sqlite3-wasmfs.cfiles := $(sqlite3-wasm.cfiles)
|
||||
#
|
||||
# Upstream RFE:
|
||||
# https://github.com/emscripten-core/emscripten/issues/18237
|
||||
#
|
||||
# Maintenance reminder: Mac sed works differently than GNU sed, so
|
||||
# don't use sed for this.
|
||||
define SQLITE3.xJS.ESM-EXPORT-DEFAULT
|
||||
if [ x1 = x$(1) ]; then \
|
||||
echo "Fragile workaround for emscripten/issues/18237. See SQLITE3.xJS.RECIPE."; \
|
||||
{\
|
||||
awk '/^export default/ && !f{f=1; next} 1' $@ > $@.tmp && mv $@.tmp $@; \
|
||||
} || exit $$?; \
|
||||
if [ x != x$(2) ]; then \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
echo "Fragile workaround for an Emscripten annoyance. See SQLITE3.xJS.RECIPE."; \
|
||||
sed -i -e '0,/^export default/{/^export default/d;}' $@ || exit $$?; \
|
||||
if ! grep -q '^export default' $@; then \
|
||||
echo "Cannot find export default." 1>&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
fi
|
||||
endef
|
||||
@@ -715,57 +695,53 @@ pre-post-jses.deps.common := $(extern-pre-js.js) $(sqlite3-license-version.js)
|
||||
# SETUP_LIB_BUILD_MODE is a $(call)'able which sets up numerous pieces
|
||||
# for one of the build modes.
|
||||
#
|
||||
# $1 = one of: sqlite3, sqlite3-wasmfs
|
||||
# $2 = build mode name: one of $(JS_BUILD_MODES)
|
||||
# $3 = 1 for ESM build mode, else 0
|
||||
# $4 = resulting sqlite-api JS/MJS file
|
||||
# $5 = resulting JS/MJS file
|
||||
# $6 = -D... flags for $(bin.c-pp)
|
||||
# $7 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
# $1 = build mode name: one of $(JS_BUILD_MODES)
|
||||
# $2 = 1 for ESM build mode, else 0
|
||||
# $3 = resulting sqlite-api JS/MJS file
|
||||
# $4 = resulting JS/MJS file
|
||||
# $5 = -D... flags for $(bin.c-pp)
|
||||
# $6 = emcc -sXYZ flags (CURRENTLY UNUSED - was factored out)
|
||||
#
|
||||
# Maintenance reminder: be careful not to introduce spaces around args
|
||||
# ($1, $2), otherwise string concatenation will malfunction.
|
||||
#
|
||||
# emcc.environment.$(2) must be set to a value for the -sENVIRONMENT flag.
|
||||
#
|
||||
# $(cflags.$(1)) and $(cflags.$(1).$(2)) may be defined to append
|
||||
# CFLAGS to a given build mode.
|
||||
#
|
||||
# $(emcc.flags.$(1)) and $(emcc.flags.$(1).$(2)) may be defined to
|
||||
# append emcc-specific flags to a given build mode.
|
||||
# emcc.environment.$(1) must be set to a value for the -sENVIRONMENT flag.
|
||||
define SETUP_LIB_BUILD_MODE
|
||||
$(info Setting up build [$(1)-$(2)]: $(5))
|
||||
c-pp.D.$(1)-$(2) := $(6)
|
||||
$$(eval $$(call call-make-pre-post,$(1),$(2)))
|
||||
emcc.flags.$(1).$(2) ?=
|
||||
emcc.flags.$(1).$(2) += $(7)
|
||||
$$(eval $$(call C-PP.FILTER, $$(sqlite3-api.js.in), $(4), $(6)))
|
||||
$(5): $(4) $$(MAKEFILE) $$(sqlite3-wasm.cfiles) $$(EXPORTED_FUNCTIONS.api) $$(pre-post-$(1)-$(2).deps)
|
||||
$(info Setting up build [$(1)]: $(4))
|
||||
c-pp.D.$(1) := $(5)
|
||||
pre-js.js.$(1) := $$(dir.tmp)/pre-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(pre-js.js.in),$$(pre-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
post-js.js.$(1) := $$(dir.tmp)/post-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(post-js.js.in),$$(post-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
extern-post-js.js.$(1) := $$(dir.tmp)/extern-post-js.$(1).js
|
||||
$$(eval $$(call C-PP.FILTER,$$(extern-post-js.js.in),$$(extern-post-js.js.$(1)),$$(c-pp.D.$(1))))
|
||||
pre-post-common.flags.$(1) := \
|
||||
$$(pre-post-common.flags) \
|
||||
--post-js=$$(post-js.js.$(1)) \
|
||||
--extern-post-js=$$(extern-post-js.js.$(1))
|
||||
pre-post-jses.deps.$(1) := $$(pre-post-jses.deps.common) \
|
||||
$$(post-js.js.$(1)) $$(extern-post-js.js.$(1))
|
||||
$$(eval $$(call call-make-pre-post,sqlite3,$(1)))
|
||||
emcc.flags.sqlite3.$(1) := $(6)
|
||||
$$(eval $$(call C-PP.FILTER, $$(sqlite3-api.js.in), $(3), $(5)))
|
||||
$(4): $(3) $$(MAKEFILE) $$(sqlite3-wasm.cses) $$(EXPORTED_FUNCTIONS.api) $$(pre-post-sqlite3.deps.$(1))
|
||||
@echo "Building $$@ ..."
|
||||
$$(emcc.bin) -o $$@ $$(emcc_opt_full) $$(emcc.flags) \
|
||||
$$(emcc.jsflags) \
|
||||
-sENVIRONMENT=$$(emcc.environment.$(2)) \
|
||||
$$(pre-post-$(1)-$(2).flags) \
|
||||
$$(emcc.flags.$(1)) $$(emcc.flags.$(1).$(2)) \
|
||||
$$(cflags.common) $$(SQLITE_OPT) \
|
||||
$$(cflags.$(1)) $$(cflags.$(1).$(2)) \
|
||||
$$(cflags.wasm_extra_init) $$(sqlite3-wasm.cfiles)
|
||||
@$$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(3))
|
||||
@dotwasm=$$(basename $$@).wasm; \
|
||||
chmod -x $$$$dotwasm; \
|
||||
$(maybe-wasm-strip) $$$$dotwasm; \
|
||||
case $(2) in \
|
||||
-sENVIRONMENT=$$(emcc.environment.$(1)) \
|
||||
$$(pre-post-sqlite3.flags.$(1)) $$(emcc.flags.sqlite3.$(1)) \
|
||||
$$(cflags.common) $$(SQLITE_OPT) $$(cflags.wasm_extra_init) $$(sqlite3-wasm.cses)
|
||||
@$$(call SQLITE3.xJS.ESM-EXPORT-DEFAULT,$(2))
|
||||
@case $(1) in \
|
||||
bundler-friendly|node) \
|
||||
echo "Patching $$@ for $(1).wasm..."; \
|
||||
rm -f $$$$dotwasm; \
|
||||
dotwasm=; \
|
||||
sed -i -e 's/$(1)-$(2).wasm/$(1).wasm/g' $$@ || exit $$$$?; \
|
||||
echo "Patching $(3) for sqlite3.wasm..."; \
|
||||
rm -f $$(dir.dout)/sqlite3-$(1).wasm; \
|
||||
sed -i -e 's/sqlite3-$(1).wasm/sqlite3.wasm/g' $$@ || exit $$$$?; \
|
||||
;; \
|
||||
esac; \
|
||||
ls -la $$$$dotwasm $$@
|
||||
all: $(5)
|
||||
#quick: $(5)
|
||||
CLEAN_FILES += $(4) $(5)
|
||||
esac
|
||||
chmod -x $$(sqlite3.wasm)
|
||||
$$(maybe-wasm-strip) $$(sqlite3.wasm)
|
||||
@ls -la $@ $$(sqlite3.wasm)
|
||||
all: $(4)
|
||||
quick: $(4)
|
||||
CLEAN_FILES += $(3) $(4)
|
||||
endef
|
||||
# ^^^ /SETUP_LIB_BUILD_MODE
|
||||
########################################################################
|
||||
@@ -777,24 +753,21 @@ sqlite3-api-bundler-friendly.mjs := $(dir.dout)/sqlite3-api-bundler-friendly.mjs
|
||||
sqlite3-bundler-friendly.mjs := $(dir.dout)/sqlite3-bundler-friendly.mjs
|
||||
sqlite3-api-node.mjs := $(dir.dout)/sqlite3-api-node.mjs
|
||||
sqlite3-node.mjs := $(dir.dout)/sqlite3-node.mjs
|
||||
#$(info $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,vanilla,0,\
|
||||
$(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,esm,1,\
|
||||
$(sqlite3-api.mjs), $(sqlite3.mjs), \
|
||||
# Maintenance reminder: careful not to introduce spaces around args $1, $2
|
||||
#$(info $(call SETUP_LIB_BUILD_MODE,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,vanilla,0, $(sqlite3-api.js), $(sqlite3.js)))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,esm,1, $(sqlite3-api.mjs), $(sqlite3.mjs), \
|
||||
-Dtarget=es6-module, -sEXPORT_ES6 -sUSE_ES6_IMPORT_META))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,bundler-friendly,1,\
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,bundler-friendly,1,\
|
||||
$(sqlite3-api-bundler-friendly.mjs),$(sqlite3-bundler-friendly.mjs),\
|
||||
$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,sqlite3,node,1,\
|
||||
$(c-pp.D.esm) -Dtarget=es6-bundler-friendly))
|
||||
$(eval $(call SETUP_LIB_BUILD_MODE,node,1,\
|
||||
$(sqlite3-api-node.mjs),$(sqlite3-node.mjs),\
|
||||
$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node))
|
||||
$(c-pp.D.bundler-friendly) -Dtarget=node))
|
||||
# The various -D... values used by *.c-pp.js include:
|
||||
#
|
||||
# -Dtarget=es6-module: for all ESM module builds
|
||||
#
|
||||
# -Dtarget=node: for node.js builds
|
||||
#
|
||||
# -Dtarget=es6-module -Dtarget=es6-bundler-friendly: intended for
|
||||
# "bundler-friendly" ESM module build. These have some restrictions
|
||||
# on how URL() objects are constructed in some contexts: URLs which
|
||||
@@ -868,7 +841,7 @@ clean-batch:
|
||||
# a regular basis with different -Ox flags and rebuilding the batch
|
||||
# pieces each time is an unnecessary time sink.
|
||||
batch: batch-runner.list
|
||||
#all: batch
|
||||
all: batch
|
||||
# end batch-runner.js
|
||||
########################################################################
|
||||
# Wasmified speedtest1 is our primary benchmarking tool.
|
||||
@@ -876,7 +849,7 @@ batch: batch-runner.list
|
||||
# emcc.speedtest1.common = emcc flags used by multiple builds of speedtest1
|
||||
# emcc.speedtest1 = emcc flags used by main build of speedtest1
|
||||
emcc.speedtest1.common := $(emcc_opt_full)
|
||||
emcc.speedtest1 := -I. -I$(dir $(sqlite3.canonical.c))
|
||||
emcc.speedtest1 :=
|
||||
emcc.speedtest1 += -sENVIRONMENT=web
|
||||
emcc.speedtest1 += -sALLOW_MEMORY_GROWTH
|
||||
emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))
|
||||
@@ -919,24 +892,22 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api.main)
|
||||
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.main); } > $@
|
||||
speedtest1.js := $(dir.dout)/speedtest1.js
|
||||
speedtest1.wasm := $(dir.dout)/speedtest1.wasm
|
||||
emcc.flags.speedtest1-vanilla := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
|
||||
speedtest1.cfiles := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
cflags.speedtest1 := $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
|
||||
speedtest1.cses := $(speedtest1.c) $(sqlite3-wasm.c)
|
||||
$(eval $(call call-make-pre-post,speedtest1,vanilla))
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
|
||||
$(pre-post-speedtest1-vanilla.deps) \
|
||||
$(speedtest1.js): $(MAKEFILE) $(speedtest1.cses) \
|
||||
$(pre-post-speedtest1.deps.vanilla) \
|
||||
$(EXPORTED_FUNCTIONS.speedtest1)
|
||||
@echo "Building $@ ..."
|
||||
$(emcc.bin) \
|
||||
$(emcc.speedtest1) \
|
||||
$(emcc.speedtest1) -I$(dir $(sqlite3.canonical.c)) \
|
||||
$(emcc.speedtest1.common) \
|
||||
$(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \
|
||||
$(cflags.speedtest1) $(pre-post-speedtest1.flags.vanilla) \
|
||||
$(SQLITE_OPT) \
|
||||
-USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \
|
||||
$(speedtest1.exit-runtime0) \
|
||||
-o $@ $(speedtest1.cfiles) -lm
|
||||
-o $@ $(speedtest1.cses) -lm
|
||||
$(maybe-wasm-strip) $(speedtest1.wasm)
|
||||
chmod -x $(speedtest1.wasm)
|
||||
ls -la $@ $(speedtest1.wasm)
|
||||
|
||||
speedtest1: $(speedtest1.js)
|
||||
@@ -962,14 +933,13 @@ CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
|
||||
#
|
||||
# To create those, we filter tester1.c-pp.js with $(bin.c-pp)...
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.js))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1.html))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm)))
|
||||
$(eval $(call C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.esm)))
|
||||
tester1: tester1.js tester1.mjs tester1.html tester1-esm.html
|
||||
# Note that we do not include $(sqlite3-bundler-friendly.mjs) in this
|
||||
# because bundlers are client-specific.
|
||||
all quick: tester1
|
||||
quick: $(sqlite3.js)
|
||||
|
||||
########################################################################
|
||||
# Convenience rules to rebuild with various -Ox levels. Much
|
||||
@@ -989,6 +959,8 @@ o1: clean
|
||||
$(MAKE) -e "emcc_opt=-O1 $(o-xtra)"
|
||||
o2: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-O2 $(o-xtra)"
|
||||
qo2: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-O2 $(o-xtra)" quick
|
||||
o3: clean
|
||||
$(MAKE) -e "emcc_opt=-O3 $(o-xtra)"
|
||||
os: clean
|
||||
@@ -996,6 +968,8 @@ os: clean
|
||||
$(MAKE) -e "emcc_opt=-Os $(o-xtra)"
|
||||
oz: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-Oz $(o-xtra)"
|
||||
qoz: clean
|
||||
$(MAKE) -j2 -e "emcc_opt=-Oz $(o-xtra)" quick
|
||||
|
||||
########################################################################
|
||||
# Sub-makes...
|
||||
@@ -1007,8 +981,6 @@ include fiddle.make
|
||||
ifneq (,$(filter wasmfs,$(MAKECMDGOALS)))
|
||||
wasmfs.enable ?= 1
|
||||
else
|
||||
# Unconditionally enable wasmfs for [dist]clean so that the wasmfs
|
||||
# sub-make can clean up.
|
||||
wasmfs.enable ?= $(if $(filter %clean,$(MAKECMDGOALS)),1,0)
|
||||
endif
|
||||
ifeq (1,$(wasmfs.enable))
|
||||
@@ -1039,7 +1011,8 @@ endif
|
||||
# Push files to public wasm-testing.sqlite.org server
|
||||
wasm-testing.include = *.js *.mjs *.html \
|
||||
./tests \
|
||||
$(dir.dout) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
batch-runner.list \
|
||||
$(dir.dout) $(dir.sql) $(dir.common) $(dir.fiddle) $(dir.jacc)
|
||||
wasm-testing.exclude = sql/speedtest1.sql
|
||||
wasm-testing.dir = /jail/sites/wasm-testing
|
||||
wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir)
|
||||
|
||||
+7
-8
@@ -82,7 +82,7 @@ features in the apps which use them.
|
||||
|
||||
# Testing on a remote machine that is accessed via SSH
|
||||
|
||||
*NB: The following are developer notes, last validated on 2023-07-19*
|
||||
*NB: The following are developer notes, last validated on 2022-08-18*
|
||||
|
||||
* Remote: Install git, emsdk, and althttpd
|
||||
* Use a [version of althttpd][althttpd] from
|
||||
@@ -93,14 +93,13 @@ features in the apps which use them.
|
||||
* Local: `ssh -L 8180:localhost:8080 remote`
|
||||
* Local: Point your web-browser at http://localhost:8180/index.html
|
||||
|
||||
In order to enable [SharedArrayBuffer][], the web-browser requires
|
||||
that the two extra Cross-Origin lines be present in HTTP reply headers
|
||||
and that the request must come from "localhost" (_or_ over an SSL
|
||||
connection). Since the web-server is on a different machine from the
|
||||
web-broser, the localhost requirement means that the connection must
|
||||
be tunneled using SSH.
|
||||
In order to enable [SharedArrayBuffers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer),
|
||||
the web-browser requires that the two extra Cross-Origin lines be present
|
||||
in HTTP reply headers and that the request must come from "localhost".
|
||||
Since the web-server is on a different machine from
|
||||
the web-broser, the localhost requirement means that the connection must be tunneled
|
||||
using SSH.
|
||||
|
||||
|
||||
[emscripten]: https://emscripten.org
|
||||
[althttpd]: https://sqlite.org/althttpd
|
||||
[SharedArrayBuffer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
|
||||
|
||||
+11
-16
@@ -83,18 +83,15 @@ browser client:
|
||||
helpers for use by downstream code which creates `sqlite3_vfs`
|
||||
and `sqlite3_module` implementations.
|
||||
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
||||
is an sqlite3 VFS implementation which supports the Origin-Private
|
||||
FileSystem (OPFS) as a storage layer to provide persistent storage
|
||||
for database files in a browser. It requires...
|
||||
is an sqlite3 VFS implementation which supports Google Chrome's
|
||||
Origin-Private FileSystem (OPFS) as a storage layer to provide
|
||||
persistent storage for database files in a browser. It requires...
|
||||
- **`sqlite3-opfs-async-proxy.js`**\
|
||||
is the asynchronous backend part of the OPFS proxy. It speaks
|
||||
directly to the (async) OPFS API and channels those results back
|
||||
to its synchronous counterpart. This file, because it must be
|
||||
started in its own Worker, is not part of the amalgamation.
|
||||
- **`sqlite3-vfs-opfs-sahpool.c-pp.js`**\
|
||||
is another sqlite3 VFS supporting the OPFS, but uses a completely
|
||||
different approach that the above-listed one.
|
||||
- **`sqlite3-api-cleanup.js`**\
|
||||
- **`api/sqlite3-api-cleanup.js`**\
|
||||
The previous files do not immediately extend the library. Instead
|
||||
they add callback functions to be called during its
|
||||
bootstrapping. Some also temporarily create global objects in order
|
||||
@@ -111,15 +108,13 @@ browser client:
|
||||
with `c-pp`](#c-pp), noting that such preprocessing may be applied
|
||||
after all of the relevant files are concatenated. That extension is
|
||||
used primarily to keep the code maintainers cognisant of the fact that
|
||||
those files contain constructs which may not run as-is in any given
|
||||
JavaScript environment.
|
||||
those files contain constructs which will not run as-is in JavaScript.
|
||||
|
||||
The build process glues those files together, resulting in
|
||||
`sqlite3-api.js`, which is everything except for the
|
||||
`pre/post-js-*.js` files, and `sqlite3.js`, which is the
|
||||
Emscripten-generated amalgamated output and includes the
|
||||
`pre/post-js-*.js` parts, as well as the Emscripten-provided module
|
||||
loading pieces.
|
||||
`sqlite3-api.js`, which is everything except for the `post-js-*.js`
|
||||
files, and `sqlite3.js`, which is the Emscripten-generated amalgamated
|
||||
output and includes the `post-js-*.js` parts, as well as the
|
||||
Emscripten-provided module loading pieces.
|
||||
|
||||
The non-JS outlier file is `sqlite3-wasm.c`: it is a proxy for
|
||||
`sqlite3.c` which `#include`'s that file and adds a couple more
|
||||
@@ -157,8 +152,8 @@ Preprocessing of Source Files
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Certain files in the build require preprocessing to filter in/out
|
||||
parts which differ between vanilla JS, ES6 Modules, and node.js
|
||||
builds. The preprocessor application itself is in
|
||||
parts which differ between vanilla JS builds and ES6 Module
|
||||
(a.k.a. esm) builds. The preprocessor application itself is in
|
||||
[`c-pp.c`](/file/ext/wasm/c-pp.c) and the complete technical details
|
||||
of such preprocessing are maintained in
|
||||
[`GNUMakefile`](/file/ext/wasm/GNUmakefile).
|
||||
|
||||
@@ -23,7 +23,10 @@ const toExportForESM =
|
||||
impls which Emscripten installs at some point in the file above
|
||||
this.
|
||||
*/
|
||||
const originalInit = sqlite3InitModule;
|
||||
const originalInit =
|
||||
/* Maintenance reminder: DO NOT use `self.` here. It's correct
|
||||
for non-ES6 Module cases but wrong for ES6 modules because those
|
||||
resolve this symbol differently. */ sqlite3InitModule;
|
||||
if(!originalInit){
|
||||
throw new Error("Expecting globalThis.sqlite3InitModule to be defined by the Emscripten build.");
|
||||
}
|
||||
@@ -62,19 +65,19 @@ const toExportForESM =
|
||||
globalThis.sqlite3InitModule = function ff(...args){
|
||||
//console.warn("Using replaced sqlite3InitModule()",globalThis.location);
|
||||
return originalInit(...args).then((EmscriptenModule)=>{
|
||||
//#if wasmfs
|
||||
if('undefined'!==typeof WorkerGlobalScope &&
|
||||
EmscriptenModule['ENVIRONMENT_IS_PTHREAD']){
|
||||
(EmscriptenModule['ENVIRONMENT_IS_PTHREAD']
|
||||
|| EmscriptenModule['_pthread_self']
|
||||
|| 'function'===typeof threadAlert
|
||||
|| globalThis?.location?.pathname?.endsWith?.('.worker.js')
|
||||
)){
|
||||
/** Workaround for wasmfs-generated worker, which calls this
|
||||
routine from each individual thread and requires that its
|
||||
argument be returned. The conditional criteria above are
|
||||
fragile, based solely on inspection of the offending code,
|
||||
not public Emscripten details. */
|
||||
//console.warn("sqlite3InitModule() returning E-module.",EmscriptenModule);
|
||||
argument be returned. All of the criteria above are fragile,
|
||||
based solely on inspection of the offending code, not public
|
||||
Emscripten details. */
|
||||
return EmscriptenModule;
|
||||
}
|
||||
//#endif
|
||||
//console.warn("sqlite3InitModule() returning sqlite3 object.");
|
||||
const s = EmscriptenModule.sqlite3;
|
||||
s.scriptInfo = initModuleState;
|
||||
//console.warn("sqlite3.scriptInfo =",s.scriptInfo);
|
||||
@@ -121,6 +124,5 @@ const toExportForESM =
|
||||
return globalThis.sqlite3InitModule /* required for ESM */;
|
||||
})();
|
||||
//#if target=es6-module
|
||||
sqlite3InitModule = toExportForESM;
|
||||
export default sqlite3InitModule;
|
||||
export default toExportForESM;
|
||||
//#endif
|
||||
|
||||
@@ -22,10 +22,8 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
|
||||
*/
|
||||
const SABC = Object.assign(
|
||||
Object.create(null), {
|
||||
exports: ('undefined'===typeof wasmExports)
|
||||
? Module['asm']/* emscripten <=3.1.43 */
|
||||
: wasmExports /* emscripten >=3.1.44 */,
|
||||
memory: Module.wasmMemory /* gets set if built with -sIMPORTED_MEMORY */
|
||||
exports: Module['asm'],
|
||||
memory: Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */
|
||||
},
|
||||
globalThis.sqlite3ApiConfig || {}
|
||||
);
|
||||
|
||||
@@ -608,7 +608,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"],
|
||||
["sqlite3_wasm_vfs_create_file", "int",
|
||||
"sqlite3_vfs*","string","*", "int"],
|
||||
["sqlite3_wasm_posix_create_file", "int", "string","*", "int"],
|
||||
["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
|
||||
];
|
||||
|
||||
@@ -729,15 +728,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
Populate api object with sqlite3_...() by binding the "raw" wasm
|
||||
exports into type-converting proxies using wasm.xWrap().
|
||||
*/
|
||||
if(0 === wasm.exports.sqlite3_step.length){
|
||||
/* This environment wraps exports in nullary functions, which means
|
||||
we must disable the arg-count validation we otherwise perform
|
||||
on the wrappers. */
|
||||
wasm.xWrap.doArgcCheck = false;
|
||||
sqlite3.config.warn(
|
||||
"Disabling sqlite3.wasm.xWrap.doArgcCheck due to environmental quirks."
|
||||
);
|
||||
}
|
||||
for(const e of wasm.bindingSignatures){
|
||||
capi[e[0]] = wasm.xWrap.apply(null, e);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
|
||||
`exports.memory`. In Emscripten environments this should be set
|
||||
to `Module.wasmMemory` if the build uses `-sIMPORTED_MEMORY`, or be
|
||||
to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be
|
||||
left undefined/falsy to default to `exports.memory` when using
|
||||
WASM-exported memory.
|
||||
|
||||
@@ -88,12 +88,12 @@
|
||||
can be replaced with (e.g.) empty functions to squelch all such
|
||||
output.
|
||||
|
||||
- `wasmfsOpfsDir`[^1]: Specifies the "mount point" of the OPFS-backed
|
||||
filesystem in WASMFS-capable builds.
|
||||
- `wasmfsOpfsDir`[^1]: As of 2022-12-17, this feature does not
|
||||
currently work due to incompatible Emscripten-side changes made
|
||||
in the WASMFS+OPFS combination. This option is currently ignored.
|
||||
|
||||
|
||||
[^1] = This property may optionally be a function, in which case
|
||||
this function calls that function to fetch the value,
|
||||
[^1] = This property may optionally be a function, in which case this
|
||||
function re-assigns calls that function to fetch the value,
|
||||
enabling delayed evaluation.
|
||||
|
||||
The returned object is the top-level sqlite3 namespace object.
|
||||
@@ -125,11 +125,11 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
log: console.log.bind(console),
|
||||
wasmfsOpfsDir: '/opfs',
|
||||
/**
|
||||
useStdAlloc is just for testing allocator discrepancies. The
|
||||
useStdAlloc is just for testing an allocator discrepancy. The
|
||||
docs guarantee that this is false in the canonical builds. For
|
||||
99% of purposes it doesn't matter which allocators we use, but
|
||||
it becomes significant with, e.g., sqlite3_deserialize() and
|
||||
certain wasm.xWrap.resultAdapter()s.
|
||||
it becomes significant with, e.g., sqlite3_deserialize()
|
||||
and certain wasm.xWrap.resultAdapter()s.
|
||||
*/
|
||||
useStdAlloc: false
|
||||
}, apiConfig || {});
|
||||
@@ -149,6 +149,11 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
config[k] = config[k]();
|
||||
}
|
||||
});
|
||||
config.wasmOpfsDir =
|
||||
/* 2022-12-17: WASMFS+OPFS can no longer be activated from the
|
||||
main thread (aborts via a failed assert() if it's attempted),
|
||||
which eliminates any(?) benefit to supporting it. */ false;
|
||||
|
||||
/**
|
||||
The main sqlite3 binding API gets installed into this object,
|
||||
mimicking the C API as closely as we can. The numerous members
|
||||
@@ -772,43 +777,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
isSharedTypedArray,
|
||||
toss: function(...args){throw new Error(args.join(' '))},
|
||||
toss3,
|
||||
typedArrayPart,
|
||||
/**
|
||||
Given a byte array or ArrayBuffer, this function throws if the
|
||||
lead bytes of that buffer do not hold a SQLite3 database header,
|
||||
else it returns without side effects.
|
||||
|
||||
Added in 3.44.
|
||||
*/
|
||||
affirmDbHeader: function(bytes){
|
||||
if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
||||
const header = "SQLite format 3";
|
||||
if( header.length > bytes.byteLength ){
|
||||
toss3("Input does not contain an SQLite3 database header.");
|
||||
}
|
||||
for(let i = 0; i < header.length; ++i){
|
||||
if( header.charCodeAt(i) !== bytes[i] ){
|
||||
toss3("Input does not contain an SQLite3 database header.");
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
Given a byte array or ArrayBuffer, this function throws if the
|
||||
database does not, at a cursory glance, appear to be an SQLite3
|
||||
database. It only examines the size and header, but further
|
||||
checks may be added in the future.
|
||||
|
||||
Added in 3.44.
|
||||
*/
|
||||
affirmIsDb: function(bytes){
|
||||
if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
||||
const n = bytes.byteLength;
|
||||
if(n<512 || n%512!==0) {
|
||||
toss3("Byte array size",n,"is invalid for an SQLite3 db.");
|
||||
}
|
||||
util.affirmDbHeader(bytes);
|
||||
}
|
||||
}/*util*/;
|
||||
typedArrayPart
|
||||
};
|
||||
|
||||
Object.assign(wasm, {
|
||||
/**
|
||||
@@ -839,7 +809,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
|| toss3("Missing API config.exports (WASM module exports)."),
|
||||
|
||||
/**
|
||||
When Emscripten compiles with `-sIMPORTED_MEMORY`, it
|
||||
When Emscripten compiles with `-sIMPORT_MEMORY`, it
|
||||
initalizes the heap and imports it into wasm, as opposed to
|
||||
the other way around. In this case, the memory is not
|
||||
available via this.exports.memory.
|
||||
@@ -1207,31 +1177,31 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/** State for sqlite3_wasmfs_opfs_dir(). */
|
||||
let __wasmfsOpfsDir = undefined;
|
||||
/**
|
||||
2022-12-17: incompatible WASMFS changes have made WASMFS+OPFS
|
||||
unavailable from the main thread, which eliminates the most
|
||||
significant benefit of supporting WASMFS. This function is now a
|
||||
no-op which always returns a falsy value. Before that change,
|
||||
this function behaved as documented below (and how it will again
|
||||
if we can find a compelling reason to support it).
|
||||
|
||||
If the wasm environment has a WASMFS/OPFS-backed persistent
|
||||
storage directory, its path is returned by this function. If it
|
||||
does not then it returns "" (noting that "" is a falsy value).
|
||||
|
||||
The first time this is called, this function inspects the current
|
||||
environment to determine whether persistence support is available
|
||||
and, if it is, enables it (if needed). After the first call it
|
||||
always returns the cached result.
|
||||
and, if it is, enables it (if needed).
|
||||
|
||||
If the returned string is not empty, any files stored under the
|
||||
given path (recursively) are housed in OPFS storage. If the
|
||||
returned string is empty, this particular persistent storage
|
||||
option is not available on the client.
|
||||
|
||||
Though the mount point name returned by this function is intended
|
||||
to remain stable, clients should not hard-coded it anywhere. Always call this function to get the path.
|
||||
|
||||
Note that this function is a no-op in must builds of this
|
||||
library, as the WASMFS capability requires a custom
|
||||
build.
|
||||
This function currently only recognizes the WASMFS/OPFS storage
|
||||
combination and its path refers to storage rooted in the
|
||||
Emscripten-managed virtual filesystem.
|
||||
*/
|
||||
capi.sqlite3_wasmfs_opfs_dir = function(){
|
||||
if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
|
||||
// If we have no OPFS, there is no persistent dir
|
||||
const pdir = config.wasmfsOpfsDir;
|
||||
console.error("sqlite3_wasmfs_opfs_dir() can no longer work due "+
|
||||
"to incompatible WASMFS changes. It will be removed.");
|
||||
if(!pdir
|
||||
|| !globalThis.FileSystemHandle
|
||||
|| !globalThis.FileSystemDirectoryHandle
|
||||
@@ -1253,6 +1223,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
Experimental and subject to change or removal.
|
||||
|
||||
Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a
|
||||
non-empty string and the given name starts with (that string +
|
||||
'/'), else returns false.
|
||||
@@ -1262,6 +1234,13 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
return (p && name) ? name.startsWith(p+'/') : false;
|
||||
};
|
||||
|
||||
// This bit is highly arguable and is incompatible with the fiddle shell.
|
||||
if(false && 0===wasm.exports.sqlite3_vfs_find(0)){
|
||||
/* Assume that sqlite3_initialize() has not yet been called.
|
||||
This will be the case in an SQLITE_OS_KV build. */
|
||||
wasm.exports.sqlite3_initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name
|
||||
(defaulting to "main"), returns a truthy value (see below) if
|
||||
@@ -1392,74 +1371,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
};
|
||||
|
||||
/**
|
||||
If the current environment supports the POSIX file APIs, this routine
|
||||
creates (or overwrites) the given file using those APIs. This is
|
||||
primarily intended for use in Emscripten-based builds where the POSIX
|
||||
APIs are transparently proxied by an in-memory virtual filesystem.
|
||||
It may behave diffrently in other environments.
|
||||
|
||||
The first argument must be either a JS string or WASM C-string
|
||||
holding the filename. Note that this routine does _not_ create
|
||||
intermediary directories if the filename has a directory part.
|
||||
|
||||
The 2nd argument may either a valid WASM memory pointer, an
|
||||
ArrayBuffer, or a Uint8Array. The 3rd must be the length, in
|
||||
bytes, of the data array to copy. If the 2nd argument is an
|
||||
ArrayBuffer or Uint8Array and the 3rd is not a positive integer
|
||||
then the 3rd defaults to the array's byteLength value.
|
||||
|
||||
Results are undefined if data is a WASM pointer and dataLen is
|
||||
exceeds data's bounds.
|
||||
|
||||
Throws if any arguments are invalid or if creating or writing to
|
||||
the file fails.
|
||||
|
||||
Added in 3.43 as an alternative for the deprecated
|
||||
sqlite3_js_vfs_create_file().
|
||||
*/
|
||||
capi.sqlite3_js_posix_create_file = function(filename, data, dataLen){
|
||||
let pData;
|
||||
if(data && wasm.isPtr(data)){
|
||||
pData = data;
|
||||
}else if(data instanceof ArrayBuffer || data instanceof Uint8Array){
|
||||
pData = wasm.allocFromTypedArray(data);
|
||||
if(arguments.length<3 || !util.isInt32(dataLen) || dataLen<0){
|
||||
dataLen = data.byteLength;
|
||||
}
|
||||
}else{
|
||||
SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
try{
|
||||
if(!util.isInt32(dataLen) || dataLen<0){
|
||||
SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file().");
|
||||
}
|
||||
const rc = wasm.sqlite3_wasm_posix_create_file(filename, pData, dataLen);
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
wasm.dealloc(pData);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Deprecation warning: this function does not work properly in
|
||||
debug builds of sqlite3 because its out-of-scope use of the
|
||||
sqlite3_vfs API triggers assertions in the core library. That
|
||||
was unfortunately not discovered until 2023-08-11. This function
|
||||
is now deprecated and should not be used in new code.
|
||||
|
||||
Alternative options:
|
||||
|
||||
- "unix" VFS and its variants can get equivalent functionality
|
||||
with sqlite3_js_posix_create_file().
|
||||
|
||||
- OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs"
|
||||
VFS, or the importDb() method of the PoolUtil object provided
|
||||
by the "opfs-sahpool" OPFS (noting that its VFS name may differ
|
||||
depending on client-side configuration). We cannot proxy those
|
||||
from here because the former is necessarily asynchronous and
|
||||
the latter requires information not available to this function.
|
||||
|
||||
Creates a file using the storage appropriate for the given
|
||||
sqlite3_vfs. The first argument may be a VFS name (JS string
|
||||
only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or
|
||||
@@ -1505,13 +1416,9 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
VFS nor the WASM environment imposes requirements which break it.
|
||||
|
||||
- "opfs": uses OPFS storage and creates directory parts of the
|
||||
filename. It can only be used to import an SQLite3 database
|
||||
file and will fail if given anything else.
|
||||
filename.
|
||||
*/
|
||||
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
|
||||
config.warn("sqlite3_js_vfs_create_file() is deprecated and",
|
||||
"should be avoided because it can lead to C-level crashes.",
|
||||
"See its documentation for alternative options.");
|
||||
let pData;
|
||||
if(data){
|
||||
if(wasm.isPtr(data)){
|
||||
@@ -1539,7 +1446,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
|
||||
capi.sqlite3_js_rc_str(rc));
|
||||
}finally{
|
||||
wasm.dealloc(pData);
|
||||
wasm.dealloc(pData);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1752,8 +1659,7 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
do not.
|
||||
*/
|
||||
tgt.push(capi.sqlite3_value_to_js(
|
||||
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)),
|
||||
throwIfCannotConvert
|
||||
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
|
||||
));
|
||||
}
|
||||
return tgt;
|
||||
@@ -1969,9 +1875,6 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
client: undefined,
|
||||
|
||||
/**
|
||||
This function is not part of the public interface, but a
|
||||
piece of internal bootstrapping infrastructure.
|
||||
|
||||
Performs any optional asynchronous library-level initialization
|
||||
which might be required. This function returns a Promise which
|
||||
resolves to the sqlite3 namespace object. Any error in the
|
||||
@@ -1987,19 +1890,27 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
then it must be called by client-level code, which must not use
|
||||
the library until the returned promise resolves.
|
||||
|
||||
If called multiple times it will return the same promise on
|
||||
subsequent calls. The current build setup precludes that
|
||||
possibility, so it's only a hypothetical problem if/when this
|
||||
function ever needs to be invoked by clients.
|
||||
Bug: if called while a prior call is still resolving, the 2nd
|
||||
call will resolve prematurely, before the 1st call has finished
|
||||
resolving. The current build setup precludes that possibility,
|
||||
so it's only a hypothetical problem if/when this function
|
||||
ever needs to be invoked by clients.
|
||||
|
||||
In Emscripten-based builds, this function is called
|
||||
automatically and deleted from this object.
|
||||
*/
|
||||
asyncPostInit: async function ff(){
|
||||
if(ff.isReady instanceof Promise) return ff.isReady;
|
||||
let lia = sqlite3ApiBootstrap.initializersAsync;
|
||||
asyncPostInit: async function(){
|
||||
let lip = sqlite3ApiBootstrap.initializersAsync;
|
||||
delete sqlite3ApiBootstrap.initializersAsync;
|
||||
const postInit = async ()=>{
|
||||
if(!lip || !lip.length) return Promise.resolve(sqlite3);
|
||||
lip = lip.map((f)=>{
|
||||
const p = (f instanceof Promise) ? f : f(sqlite3);
|
||||
return p.catch((e)=>{
|
||||
console.error("an async sqlite3 initializer failed:",e);
|
||||
throw e;
|
||||
});
|
||||
});
|
||||
const postInit = ()=>{
|
||||
if(!sqlite3.__isUnderTest){
|
||||
/* Delete references to internal-only APIs which are used by
|
||||
some initializers. Retain them when running in test mode
|
||||
@@ -2008,25 +1919,23 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
/* It's conceivable that we might want to expose
|
||||
StructBinder to client-side code, but it's only useful if
|
||||
clients build their own sqlite3.wasm which contains their
|
||||
own C struct types. */
|
||||
one C struct types. */
|
||||
delete sqlite3.StructBinder;
|
||||
}
|
||||
return sqlite3;
|
||||
};
|
||||
const catcher = (e)=>{
|
||||
config.error("an async sqlite3 initializer failed:",e);
|
||||
throw e;
|
||||
};
|
||||
if(!lia || !lia.length){
|
||||
return ff.isReady = postInit().catch(catcher);
|
||||
if(1){
|
||||
/* Run all initializers in sequence. The advantage is that it
|
||||
allows us to have post-init cleanup defined outside of this
|
||||
routine at the end of the list and have it run at a
|
||||
well-defined time. */
|
||||
let p = lip.shift();
|
||||
while(lip.length) p = p.then(lip.shift());
|
||||
return p.then(postInit);
|
||||
}else{
|
||||
/* Run them in an arbitrary order. */
|
||||
return Promise.all(lip).then(postInit);
|
||||
}
|
||||
lia = lia.map((f)=>{
|
||||
return (f instanceof Function) ? async x=>f(sqlite3) : f;
|
||||
});
|
||||
lia.push(postInit);
|
||||
let p = Promise.resolve(sqlite3);
|
||||
while(lia.length) p = p.then(lia.shift());
|
||||
return ff.isReady = p.catch(catcher);
|
||||
},
|
||||
/**
|
||||
scriptInfo ideally gets injected into this object by the
|
||||
@@ -2086,7 +1995,7 @@ globalThis.sqlite3ApiBootstrap.initializers = [];
|
||||
specifically for initializers which are asynchronous. All entries in
|
||||
this list must be either async functions, non-async functions which
|
||||
return a Promise, or a Promise. Each function in the list is called
|
||||
with the sqlite3 object as its only argument.
|
||||
with the sqlite3 ojbect as its only argument.
|
||||
|
||||
The resolved value of any Promise is ignored and rejection will kill
|
||||
the asyncPostInit() process (at an indeterminate point because all
|
||||
|
||||
@@ -35,9 +35,6 @@
|
||||
|
||||
https://developer.chrome.com/blog/sync-methods-for-accesshandles/
|
||||
|
||||
Firefox v111 and Safari 16.4, both released in March 2023, also
|
||||
include this.
|
||||
|
||||
We cannot change to the sync forms at this point without breaking
|
||||
clients who use Chrome v104-ish or higher. truncate(), getSize(),
|
||||
flush(), and close() are now (as of v108) synchronous. Calling them
|
||||
|
||||
@@ -100,7 +100,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
ACHTUNG: because we cannot generically know how to transform JS
|
||||
exceptions into result codes, the installed functions do no
|
||||
automatic catching of exceptions. It is critical, to avoid
|
||||
automatic catching of exceptions. It is critical, to avoid
|
||||
undefined behavior in the C layer, that methods mapped via
|
||||
this function do not throw. The exception, as it were, to that
|
||||
rule is...
|
||||
@@ -295,8 +295,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
- If `struct.$zName` is falsy and the entry has a string-type
|
||||
`name` property, `struct.$zName` is set to the C-string form of
|
||||
that `name` value before registerVfs() is called. That string
|
||||
gets added to the on-dispose state of the struct.
|
||||
that `name` value before registerVfs() is called.
|
||||
|
||||
On success returns this object. Throws on error.
|
||||
*/
|
||||
@@ -609,7 +608,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
This is to facilitate creation of those methods inline in the
|
||||
passed-in object without requiring the client to explicitly get a
|
||||
reference to one of them in order to assign it to the other
|
||||
one.
|
||||
one.
|
||||
|
||||
The `catchExceptions`-installed handlers will account for
|
||||
identical references to the above functions and will install the
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
//#ifnot target=node
|
||||
/*
|
||||
2022-09-18
|
||||
|
||||
@@ -24,7 +23,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
installOpfsVfs() returns a Promise which, on success, installs an
|
||||
sqlite3_vfs named "opfs", suitable for use with all sqlite3 APIs
|
||||
which accept a VFS. It is intended to be called via
|
||||
sqlite3ApiBootstrap.initializers or an equivalent mechanism.
|
||||
sqlite3ApiBootstrap.initializersAsync or an equivalent mechanism.
|
||||
|
||||
The installed VFS uses the Origin-Private FileSystem API for
|
||||
all file storage. On error it is rejected with an exception
|
||||
@@ -102,10 +101,6 @@ const installOpfsVfs = function callee(options){
|
||||
options = Object.create(null);
|
||||
}
|
||||
const urlParams = new URL(globalThis.location.href).searchParams;
|
||||
if(urlParams.has('opfs-disable')){
|
||||
//sqlite3.config.warn('Explicitly not installing "opfs" VFS due to opfs-disable flag.');
|
||||
return Promise.resolve(sqlite3);
|
||||
}
|
||||
if(undefined===options.verbose){
|
||||
options.verbose = urlParams.has('opfs-verbose')
|
||||
? (+urlParams.get('opfs-verbose') || 2) : 1;
|
||||
@@ -123,11 +118,11 @@ const installOpfsVfs = function callee(options){
|
||||
options.proxyUri = options.proxyUri();
|
||||
}
|
||||
const thePromise = new Promise(function(promiseResolve_, promiseReject_){
|
||||
const loggers = [
|
||||
sqlite3.config.error,
|
||||
sqlite3.config.warn,
|
||||
sqlite3.config.log
|
||||
];
|
||||
const loggers = {
|
||||
0:sqlite3.config.error,
|
||||
1:sqlite3.config.warn,
|
||||
2:sqlite3.config.log
|
||||
};
|
||||
const logImpl = (level,...args)=>{
|
||||
if(options.verbose>level) loggers[level]("OPFS syncer:",...args);
|
||||
};
|
||||
@@ -136,7 +131,6 @@ const installOpfsVfs = function callee(options){
|
||||
const error = (...args)=>logImpl(0, ...args);
|
||||
const toss = sqlite3.util.toss;
|
||||
const capi = sqlite3.capi;
|
||||
const util = sqlite3.util;
|
||||
const wasm = sqlite3.wasm;
|
||||
const sqlite3_vfs = capi.sqlite3_vfs;
|
||||
const sqlite3_file = capi.sqlite3_file;
|
||||
@@ -197,18 +191,17 @@ const installOpfsVfs = function callee(options){
|
||||
s.count = s.time = 0;
|
||||
}
|
||||
}/*metrics*/;
|
||||
const opfsVfs = new sqlite3_vfs();
|
||||
const opfsIoMethods = new sqlite3_io_methods();
|
||||
const opfsVfs = new sqlite3_vfs()
|
||||
.addOnDispose( ()=>opfsIoMethods.dispose());
|
||||
let promiseWasRejected = undefined;
|
||||
const promiseReject = (err)=>{
|
||||
promiseWasRejected = true;
|
||||
opfsVfs.dispose();
|
||||
return promiseReject_(err);
|
||||
};
|
||||
const promiseResolve = ()=>{
|
||||
const promiseResolve = (value)=>{
|
||||
promiseWasRejected = false;
|
||||
return promiseResolve_(sqlite3);
|
||||
return promiseResolve_(value);
|
||||
};
|
||||
const W =
|
||||
//#if target=es6-bundler-friendly
|
||||
@@ -242,17 +235,17 @@ const installOpfsVfs = function callee(options){
|
||||
? new sqlite3_vfs(pDVfs)
|
||||
: null /* dVfs will be null when sqlite3 is built with
|
||||
SQLITE_OS_OTHER. */;
|
||||
opfsIoMethods.$iVersion = 1;
|
||||
opfsVfs.$iVersion = 2/*yes, two*/;
|
||||
opfsVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
|
||||
opfsVfs.$mxPathname = 1024/*sure, why not?*/;
|
||||
opfsVfs.$zName = wasm.allocCString("opfs");
|
||||
// All C-side memory of opfsVfs is zeroed out, but just to be explicit:
|
||||
opfsVfs.$xDlOpen = opfsVfs.$xDlError = opfsVfs.$xDlSym = opfsVfs.$xDlClose = null;
|
||||
opfsVfs.addOnDispose(
|
||||
opfsVfs.ondispose = [
|
||||
'$zName', opfsVfs.$zName,
|
||||
'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null)
|
||||
);
|
||||
'cleanup default VFS wrapper', ()=>(dVfs ? dVfs.dispose() : null),
|
||||
'cleanup opfsIoMethods', ()=>opfsIoMethods.dispose()
|
||||
];
|
||||
/**
|
||||
Pedantic sidebar about opfsVfs.ondispose: the entries in that array
|
||||
are items to clean up when opfsVfs.dispose() is called, but in this
|
||||
@@ -305,7 +298,6 @@ const installOpfsVfs = function callee(options){
|
||||
lock contention to free up.
|
||||
*/
|
||||
state.asyncIdleWaitTime = 150;
|
||||
|
||||
/**
|
||||
Whether the async counterpart should log exceptions to
|
||||
the serialization channel. That produces a great deal of
|
||||
@@ -840,19 +832,22 @@ const installOpfsVfs = function callee(options){
|
||||
/* If it turns out that we need to adjust for timezone, see:
|
||||
https://stackoverflow.com/a/11760121/1458521 */
|
||||
wasm.poke(pOut, 2440587.5 + (new Date().getTime()/86400000),
|
||||
'double');
|
||||
'double');
|
||||
return 0;
|
||||
},
|
||||
xCurrentTimeInt64: function(pVfs,pOut){
|
||||
// TODO: confirm that this calculation is correct
|
||||
wasm.poke(pOut, (2440587.5 * 86400000) + new Date().getTime(),
|
||||
'i64');
|
||||
'i64');
|
||||
return 0;
|
||||
},
|
||||
xDelete: function(pVfs, zName, doSyncDir){
|
||||
mTimeStart('xDelete');
|
||||
const rc = opRun('xDelete', wasm.cstrToJs(zName), doSyncDir, false);
|
||||
opRun('xDelete', wasm.cstrToJs(zName), doSyncDir, false);
|
||||
/* We're ignoring errors because we cannot yet differentiate
|
||||
between harmless and non-harmless failures. */
|
||||
mTimeEnd();
|
||||
return rc;
|
||||
return 0;
|
||||
},
|
||||
xFullPathname: function(pVfs,zName,nOut,pOut){
|
||||
/* Until/unless we have some notion of "current dir"
|
||||
@@ -1094,7 +1089,7 @@ const installOpfsVfs = function callee(options){
|
||||
propagate any exception on error, rather than returning false.
|
||||
*/
|
||||
opfsUtil.unlink = async function(fsEntryName, recursive = false,
|
||||
throwOnError = false){
|
||||
throwOnError = false){
|
||||
try {
|
||||
const [hDir, filenamePart] =
|
||||
await opfsUtil.getDirForFilename(fsEntryName, false);
|
||||
@@ -1169,102 +1164,11 @@ const installOpfsVfs = function callee(options){
|
||||
doDir(opt.directory, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
impl of importDb() when it's given a function as its second
|
||||
argument.
|
||||
*/
|
||||
const importDbChunked = async function(filename, callback){
|
||||
const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true);
|
||||
const hFile = await hDir.getFileHandle(fnamePart, {create:true});
|
||||
let sah = await hFile.createSyncAccessHandle();
|
||||
let nWrote = 0, chunk, checkedHeader = false, err = false;
|
||||
try{
|
||||
sah.truncate(0);
|
||||
while( undefined !== (chunk = await callback()) ){
|
||||
if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk);
|
||||
if( 0===nWrote && chunk.byteLength>=15 ){
|
||||
util.affirmDbHeader(chunk);
|
||||
checkedHeader = true;
|
||||
}
|
||||
sah.write(chunk, {at: nWrote});
|
||||
nWrote += chunk.byteLength;
|
||||
}
|
||||
if( nWrote < 512 || 0!==nWrote % 512 ){
|
||||
toss("Input size",nWrote,"is not correct for an SQLite database.");
|
||||
}
|
||||
if( !checkedHeader ){
|
||||
const header = new Uint8Array(20);
|
||||
sah.read( header, {at: 0} );
|
||||
util.affirmDbHeader( header );
|
||||
}
|
||||
sah.write(new Uint8Array(2), {at: 18}/*force db out of WAL mode*/);
|
||||
return nWrote;
|
||||
}catch(e){
|
||||
await sah.close();
|
||||
sah = undefined;
|
||||
await hDir.removeEntry( fnamePart ).catch(()=>{});
|
||||
throw e;
|
||||
}finally {
|
||||
if( sah ) await sah.close();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Asynchronously imports the given bytes (a byte array or
|
||||
ArrayBuffer) into the given database file.
|
||||
|
||||
If passed a function for its second argument, its behaviour
|
||||
changes to async and it imports its data in chunks fed to it by
|
||||
the given callback function. It calls the callback (which may
|
||||
be async) repeatedly, expecting either a Uint8Array or
|
||||
ArrayBuffer (to denote new input) or undefined (to denote
|
||||
EOF). For so long as the callback continues to return
|
||||
non-undefined, it will append incoming data to the given
|
||||
VFS-hosted database file. When called this way, the resolved
|
||||
value of the returned Promise is the number of bytes written to
|
||||
the target file.
|
||||
|
||||
It very specifically requires the input to be an SQLite3
|
||||
database and throws if that's not the case. It does so in
|
||||
order to prevent this function from taking on a larger scope
|
||||
than it is specifically intended to. i.e. we do not want it to
|
||||
become a convenience for importing arbitrary files into OPFS.
|
||||
|
||||
This routine rewrites the database header bytes in the output
|
||||
file (not the input array) to force disabling of WAL mode.
|
||||
|
||||
On error this throws and the state of the input file is
|
||||
undefined (it depends on where the exception was triggered).
|
||||
|
||||
On success, resolves to the number of bytes written.
|
||||
*/
|
||||
opfsUtil.importDb = async function(filename, bytes){
|
||||
if( bytes instanceof Function ){
|
||||
return importDbChunked(filename, bytes);
|
||||
}
|
||||
if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
|
||||
util.affirmIsDb(bytes);
|
||||
const n = bytes.byteLength;
|
||||
const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true);
|
||||
let sah, err, nWrote = 0;
|
||||
try {
|
||||
const hFile = await hDir.getFileHandle(fnamePart, {create:true});
|
||||
sah = await hFile.createSyncAccessHandle();
|
||||
sah.truncate(0);
|
||||
nWrote = sah.write(bytes, {at: 0});
|
||||
if(nWrote != n){
|
||||
toss("Expected to write "+n+" bytes but wrote "+nWrote+".");
|
||||
}
|
||||
sah.write(new Uint8Array(2), {at: 18}) /* force db out of WAL mode */;
|
||||
return nWrote;
|
||||
}catch(e){
|
||||
if( sah ){ await sah.close(); sah = undefined; }
|
||||
await hDir.removeEntry( fnamePart ).catch(()=>{});
|
||||
throw e;
|
||||
}finally{
|
||||
if( sah ) await sah.close();
|
||||
}
|
||||
};
|
||||
//TODO to support fiddle and worker1 db upload:
|
||||
//opfsUtil.createFile = function(absName, content=undefined){...}
|
||||
//We have sqlite3.wasm.sqlite3_wasm_vfs_create_file() for this
|
||||
//purpose but its interface and name are still under
|
||||
//consideration.
|
||||
|
||||
if(sqlite3.oo1){
|
||||
const OpfsDb = function(...args){
|
||||
@@ -1274,7 +1178,6 @@ const installOpfsVfs = function callee(options){
|
||||
};
|
||||
OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
|
||||
sqlite3.oo1.OpfsDb = OpfsDb;
|
||||
OpfsDb.importDb = opfsUtil.importDb;
|
||||
sqlite3.oo1.DB.dbCtorHelper.setVfsPostOpenSql(
|
||||
opfsVfs.pointer,
|
||||
function(oo1Db, sqlite3){
|
||||
@@ -1283,23 +1186,19 @@ const installOpfsVfs = function callee(options){
|
||||
contention. */
|
||||
sqlite3.capi.sqlite3_busy_timeout(oo1Db, 10000);
|
||||
sqlite3.capi.sqlite3_exec(oo1Db, [
|
||||
/* As of July 2023, the PERSIST journal mode on OPFS is
|
||||
somewhat slower than DELETE or TRUNCATE (it was faster
|
||||
before Chrome version 108 or 109). TRUNCATE and DELETE
|
||||
have very similar performance on OPFS.
|
||||
/* Truncate journal mode is faster than delete for
|
||||
this vfs, per speedtest1. That gap seems to have closed with
|
||||
Chrome version 108 or 109, but "persist" is very roughly 5-6%
|
||||
faster than truncate in initial tests.
|
||||
|
||||
Roy Hashimoto notes that TRUNCATE and PERSIST modes may
|
||||
decrease OPFS concurrency because multiple connections
|
||||
can open the journal file in those modes:
|
||||
For later analysis: Roy Hashimoto notes that TRUNCATE
|
||||
and PERSIST modes may decrease OPFS concurrency because
|
||||
multiple connections can open the journal file in those
|
||||
modes:
|
||||
|
||||
https://github.com/rhashimoto/wa-sqlite/issues/68
|
||||
|
||||
Given that, and the fact that testing has not revealed
|
||||
any appreciable difference between performance of
|
||||
TRUNCATE and DELETE modes on OPFS, we currently (as of
|
||||
2023-07-13) default to DELETE mode.
|
||||
*/
|
||||
"pragma journal_mode=DELETE;",
|
||||
"pragma journal_mode=persist;",
|
||||
/*
|
||||
This vfs benefits hugely from cache on moderate/large
|
||||
speedtest1 --size 50 and --size 100 workloads. We
|
||||
@@ -1420,10 +1319,10 @@ const installOpfsVfs = function callee(options){
|
||||
sqlite3.opfs = opfsUtil;
|
||||
opfsUtil.rootDirectory = d;
|
||||
log("End of OPFS sqlite3_vfs setup.", opfsVfs);
|
||||
promiseResolve();
|
||||
promiseResolve(sqlite3);
|
||||
}).catch(promiseReject);
|
||||
}else{
|
||||
promiseResolve();
|
||||
promiseResolve(sqlite3);
|
||||
}
|
||||
}catch(e){
|
||||
error(e);
|
||||
@@ -1460,10 +1359,7 @@ globalThis.sqlite3ApiBootstrap.initializersAsync.push(async (sqlite3)=>{
|
||||
});
|
||||
}catch(e){
|
||||
sqlite3.config.error("installOpfsVfs() exception:",e);
|
||||
return Promise.reject(e);
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
||||
//#else
|
||||
/* The OPFS VFS parts are elided from builds targeting node.js. */
|
||||
//#endif target=node
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
Dummy function stubs to get sqlite3.c compiling with
|
||||
wasi-sdk. This requires, in addition:
|
||||
|
||||
-D_WASI_EMULATED_MMAN -D_WASI_EMULATED_GETPID
|
||||
|
||||
-lwasi-emulated-getpid
|
||||
*/
|
||||
typedef unsigned mode_t;
|
||||
int fchmod(int fd, mode_t mode);
|
||||
int fchmod(int fd, mode_t mode){
|
||||
return (fd && mode) ? 0 : 0;
|
||||
}
|
||||
typedef unsigned uid_t;
|
||||
typedef uid_t gid_t;
|
||||
int fchown(int fd, uid_t owner, gid_t group);
|
||||
int fchown(int fd, uid_t owner, gid_t group){
|
||||
return (fd && owner && group) ? 0 : 0;
|
||||
}
|
||||
uid_t geteuid(void);
|
||||
uid_t geteuid(void){return 0;}
|
||||
#if !defined(F_WRLCK)
|
||||
enum {
|
||||
F_WRLCK,
|
||||
F_RDLCK,
|
||||
F_GETLK,
|
||||
F_SETLK,
|
||||
F_UNLCK
|
||||
};
|
||||
#endif
|
||||
|
||||
#undef HAVE_PREAD
|
||||
|
||||
#include <wasi/api.h>
|
||||
#define WASM__KEEP __attribute__((used))
|
||||
|
||||
#if 0
|
||||
/**
|
||||
wasi-sdk cannot build sqlite3's default VFS without at least the following
|
||||
functions. They are apparently syscalls which clients have to implement or
|
||||
otherwise obtain.
|
||||
|
||||
https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md
|
||||
*/
|
||||
environ_get
|
||||
environ_sizes_get
|
||||
clock_time_get
|
||||
fd_close
|
||||
fd_fdstat_get
|
||||
fd_fdstat_set_flags
|
||||
fd_filestat_get
|
||||
fd_filestat_set_size
|
||||
fd_pread
|
||||
fd_prestat_get
|
||||
fd_prestat_dir_name
|
||||
fd_read
|
||||
fd_seek
|
||||
fd_sync
|
||||
fd_write
|
||||
path_create_directory
|
||||
path_filestat_get
|
||||
path_filestat_set_times
|
||||
path_open
|
||||
path_readlink
|
||||
path_remove_directory
|
||||
path_unlink_file
|
||||
poll_oneoff
|
||||
proc_exit
|
||||
#endif
|
||||
@@ -141,6 +141,9 @@
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
# define SQLITE_OMIT_UTF16 1
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
# define SQLITE_OMIT_WAL 1
|
||||
#endif
|
||||
#ifndef SQLITE_OS_KV_OPTIONAL
|
||||
# define SQLITE_OS_KV_OPTIONAL 1
|
||||
#endif
|
||||
@@ -148,7 +151,7 @@
|
||||
/**********************************************************************/
|
||||
/* SQLITE_T... */
|
||||
#ifndef SQLITE_TEMP_STORE
|
||||
# define SQLITE_TEMP_STORE 2
|
||||
# define SQLITE_TEMP_STORE 3
|
||||
#endif
|
||||
#ifndef SQLITE_THREADSAFE
|
||||
# define SQLITE_THREADSAFE 0
|
||||
@@ -292,7 +295,7 @@ SQLITE_WASM_EXPORT void * sqlite3_wasm_pstack_ptr(void){
|
||||
*/
|
||||
SQLITE_WASM_EXPORT void sqlite3_wasm_pstack_restore(unsigned char * p){
|
||||
assert(p>=PStack.pBegin && p<=PStack.pEnd && p>=PStack.pPos);
|
||||
assert(0==((unsigned long long)p & 0x7));
|
||||
assert(0==(p & 0x7));
|
||||
if(p>=PStack.pBegin && p<=PStack.pEnd /*&& p>=PStack.pPos*/){
|
||||
PStack.pPos = p;
|
||||
}
|
||||
@@ -352,9 +355,7 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
|
||||
if( db!=0 ){
|
||||
if( 0!=zMsg ){
|
||||
const int nMsg = sqlite3Strlen30(zMsg);
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg);
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
}else{
|
||||
sqlite3ErrorWithMsg(db, err_code, NULL);
|
||||
}
|
||||
@@ -1352,13 +1353,6 @@ int sqlite3_wasm_db_serialize( sqlite3 *pDb, const char *zSchema,
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** ACHTUNG: it was discovered on 2023-08-11 that, with SQLITE_DEBUG,
|
||||
** this function's out-of-scope use of the sqlite3_vfs/file/io_methods
|
||||
** APIs leads to triggering of assertions in the core library. Its use
|
||||
** is now deprecated and VFS-specific APIs for importing files need to
|
||||
** be found to replace it. sqlite3_wasm_posix_create_file() is
|
||||
** suitable for the "unix" family of VFSes.
|
||||
**
|
||||
** Creates a new file using the I/O API of the given VFS, containing
|
||||
** the given number of bytes of the given data. If the file exists, it
|
||||
** is truncated to the given length and populated with the given
|
||||
@@ -1404,14 +1398,7 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
int rc;
|
||||
sqlite3_file *pFile = 0;
|
||||
sqlite3_io_methods const *pIo;
|
||||
const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
|
||||
#if 0 && defined(SQLITE_DEBUG)
|
||||
| SQLITE_OPEN_MAIN_JOURNAL
|
||||
/* ^^^^ This is for testing a horrible workaround to avoid
|
||||
triggering a specific assert() in os_unix.c:unixOpen(). Please
|
||||
do not enable this in real builds. */
|
||||
#endif
|
||||
;
|
||||
const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
int flagsOut = 0;
|
||||
int fileExisted = 0;
|
||||
int doUnlock = 0;
|
||||
@@ -1477,34 +1464,6 @@ int sqlite3_wasm_vfs_create_file( sqlite3_vfs *pVfs,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
**
|
||||
** Creates or overwrites a file using the POSIX file API,
|
||||
** i.e. Emscripten's virtual filesystem. Creates or truncates
|
||||
** zFilename, appends pData bytes to it, and returns 0 on success or
|
||||
** SQLITE_IOERR on error.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
int sqlite3_wasm_posix_create_file( const char *zFilename,
|
||||
const unsigned char * pData,
|
||||
int nData ){
|
||||
int rc;
|
||||
FILE * pFile = 0;
|
||||
int fileExisted = 0;
|
||||
size_t nWrote = 1;
|
||||
|
||||
if( !zFilename || nData<0 || (pData==0 && nData>0) ) return SQLITE_MISUSE;
|
||||
pFile = fopen(zFilename, "w");
|
||||
if( 0==pFile ) return SQLITE_IOERR;
|
||||
if( nData>0 ){
|
||||
nWrote = fwrite(pData, (size_t)nData, 1, pFile);
|
||||
}
|
||||
fclose(pFile);
|
||||
return 1==nWrote ? 0 : SQLITE_IOERR;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is NOT part of the sqlite3 public API. It is strictly
|
||||
** for use by the sqlite project's own JS/WASM bindings.
|
||||
|
||||
@@ -40,41 +40,8 @@ span.labeled-input {
|
||||
.tests-pass { background-color: green; color: white }
|
||||
.tests-fail { background-color: red; color: yellow }
|
||||
.faded { opacity: 0.5; }
|
||||
.group-start {
|
||||
color: blue;
|
||||
background-color: skyblue;
|
||||
font-weight: bold;
|
||||
border-top: 1px dotted blue;
|
||||
padding: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.group-end {
|
||||
padding: 0.5em;
|
||||
margin-bottom: 0.25em;
|
||||
/*border-bottom: 1px dotted blue;*/
|
||||
}
|
||||
.group-end.green {
|
||||
background: lightgreen;
|
||||
border-bottom: 1px dotted green;
|
||||
}
|
||||
.one-test-line, .skipping-group {
|
||||
margin-left: 3em;
|
||||
}
|
||||
.skipping-test, .skipping-group {
|
||||
padding: 0.25em 0.5em;
|
||||
background-color: #ffff73;
|
||||
}
|
||||
.skipping-test {
|
||||
margin-left: 6em;
|
||||
}
|
||||
.one-test-summary {
|
||||
margin-left: 6em;
|
||||
}
|
||||
.full-test-summary {
|
||||
padding-bottom: 0.5em;
|
||||
padding-top: 0.5em;
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
.group-start { color: blue; }
|
||||
.group-end { color: blue; }
|
||||
.input-wrapper {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
|
||||
@@ -111,8 +111,7 @@
|
||||
of `target.instance` (a WebAssembly.Module instance) and it must
|
||||
contain the symbols exported by the WASM module associated with
|
||||
this code. In an Enscripten environment it must be set to
|
||||
`Module['asm']` (versions <=3.1.43) or `wasmExports` (versions
|
||||
>=3.1.44). The exports object must contain a minimum of the
|
||||
`Module['asm']`. The exports object must contain a minimum of the
|
||||
following symbols:
|
||||
|
||||
- `memory`: a WebAssembly.Memory object representing the WASM
|
||||
@@ -613,6 +612,8 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
target.installFunction = (func, sig)=>__installFunction(func, sig, false);
|
||||
|
||||
/**
|
||||
EXPERIMENTAL! DO NOT USE IN CLIENT CODE!
|
||||
|
||||
Works exactly like installFunction() but requires that a
|
||||
scopedAllocPush() is active and uninstalls the given function
|
||||
when that alloc scope is popped via scopedAllocPop().
|
||||
@@ -1178,7 +1179,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
cache.scopedAlloc.splice(n,1);
|
||||
for(let p; (p = state.pop()); ){
|
||||
if(target.functionEntry(p)){
|
||||
//console.warn("scopedAllocPop() uninstalling function",p);
|
||||
//console.warn("scopedAllocPop() uninstalling transient function",p);
|
||||
target.uninstallFunction(p);
|
||||
}
|
||||
else target.dealloc(p);
|
||||
@@ -1635,7 +1636,6 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
'and is not intended to be invoked from',
|
||||
'client-level code. Invoked with:',opt);
|
||||
}
|
||||
this.name = opt.name || "unnamed";
|
||||
this.signature = opt.signature;
|
||||
if(opt.contextKey instanceof Function){
|
||||
this.contextKey = opt.contextKey;
|
||||
@@ -1656,11 +1656,25 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
? opt.callProxy : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
Note that static class members are defined outside of the class
|
||||
to work around an emcc toolchain build problem: one of the
|
||||
tools in emsdk v3.1.42 does not support the static keyword.
|
||||
*/
|
||||
/** If true, the constructor emits a warning. The intent is that
|
||||
this be set to true after bootstrapping of the higher-level
|
||||
client library is complete, to warn downstream clients that
|
||||
they shouldn't be relying on this implemenation detail which
|
||||
does not have a stable interface. */
|
||||
static warnOnUse = false;
|
||||
|
||||
/** If true, convertArg() will FuncPtrAdapter.debugOut() when it
|
||||
(un)installs a function binding to/from WASM. Note that
|
||||
deinstallation of bindScope=transient bindings happens
|
||||
via scopedAllocPop() so will not be output. */
|
||||
static debugFuncInstall = false;
|
||||
|
||||
/** Function used for debug output. */
|
||||
static debugOut = console.debug.bind(console);
|
||||
|
||||
static bindScopes = [
|
||||
'transient', 'context', 'singleton', 'permanent'
|
||||
];
|
||||
|
||||
/* Dummy impl. Overwritten per-instance as needed. */
|
||||
contextKey(argv,argIndex){
|
||||
@@ -1697,16 +1711,14 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
exactly the 2nd and 3rd arguments are.
|
||||
*/
|
||||
convertArg(v,argv,argIndex){
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v);
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
|
||||
let pair = this.singleton;
|
||||
if(!pair && this.isContext){
|
||||
pair = this.contextMap(this.contextKey(argv,argIndex));
|
||||
//FuncPtrAdapter.debugOut(this.name, this.signature, "contextKey() =",this.contextKey(argv,argIndex), pair);
|
||||
}
|
||||
if(pair && pair[0]===v) return pair[1];
|
||||
if(v instanceof Function){
|
||||
/* Install a WASM binding and return its pointer. */
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
|
||||
if(this.callProxy) v = this.callProxy(v);
|
||||
const fp = __installFunction(v, this.signature, this.isTransient);
|
||||
if(FuncPtrAdapter.debugFuncInstall){
|
||||
@@ -1720,18 +1732,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
|
||||
this.contextKey(argv,argIndex), '@'+pair[1], v);
|
||||
}
|
||||
try{
|
||||
/* Because the pending native call might rely on the
|
||||
pointer we're replacing, e.g. as is normally the case
|
||||
with sqlite3's xDestroy() methods, we don't
|
||||
immediately uninstall but instead add its pointer to
|
||||
the scopedAlloc stack, which will be cleared when the
|
||||
xWrap() mechanism is done calling the native
|
||||
function. We're relying very much here on xWrap()
|
||||
having pushed an alloc scope.
|
||||
*/
|
||||
cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]);
|
||||
}
|
||||
try{target.uninstallFunction(pair[1])}
|
||||
catch(e){/*ignored*/}
|
||||
}
|
||||
pair[0] = v;
|
||||
@@ -1739,14 +1740,13 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
}
|
||||
return fp;
|
||||
}else if(target.isPtr(v) || null===v || undefined===v){
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
|
||||
if(pair && pair[1] && pair[1]!==v){
|
||||
/* uninstall stashed mapping and replace stashed mapping with v. */
|
||||
if(FuncPtrAdapter.debugFuncInstall){
|
||||
FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
|
||||
this.contextKey(argv,argIndex), '@'+pair[1], v);
|
||||
}
|
||||
try{ cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]) }
|
||||
try{target.uninstallFunction(pair[1])}
|
||||
catch(e){/*ignored*/}
|
||||
pair[0] = pair[1] = (v | 0);
|
||||
}
|
||||
@@ -1761,26 +1761,6 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
}/*convertArg()*/
|
||||
}/*FuncPtrAdapter*/;
|
||||
|
||||
/** If true, the constructor emits a warning. The intent is that
|
||||
this be set to true after bootstrapping of the higher-level
|
||||
client library is complete, to warn downstream clients that
|
||||
they shouldn't be relying on this implemenation detail which
|
||||
does not have a stable interface. */
|
||||
xArg.FuncPtrAdapter.warnOnUse = false;
|
||||
|
||||
/** If true, convertArg() will FuncPtrAdapter.debugOut() when it
|
||||
(un)installs a function binding to/from WASM. Note that
|
||||
deinstallation of bindScope=transient bindings happens
|
||||
via scopedAllocPop() so will not be output. */
|
||||
xArg.FuncPtrAdapter.debugFuncInstall = false;
|
||||
|
||||
/** Function used for debug output. */
|
||||
xArg.FuncPtrAdapter.debugOut = console.debug.bind(console);
|
||||
|
||||
xArg.FuncPtrAdapter.bindScopes = [
|
||||
'transient', 'context', 'singleton', 'permanent'
|
||||
];
|
||||
|
||||
const __xArgAdapterCheck =
|
||||
(t)=>xArg.get(t) || toss("Argument adapter not found:",t);
|
||||
|
||||
|
||||
@@ -235,10 +235,10 @@
|
||||
|
||||
- get change count (total or statement-local, 32- or 64-bit)
|
||||
- get a DB's file name
|
||||
|
||||
|
||||
Misc. Stmt features:
|
||||
|
||||
- Various forms of bind()
|
||||
- Various forms of bind()
|
||||
- clearBindings()
|
||||
- reset()
|
||||
- Various forms of step()
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ dist-name := $(dist-name-prefix)-TEMP
|
||||
# date. Our general policy is that we want the smallest binaries for
|
||||
# dist zip files, so use the oz build unless there is a compelling
|
||||
# reason not to.
|
||||
dist.build ?= oz
|
||||
dist.build ?= qoz
|
||||
|
||||
dist-dir.top := $(dist-name)
|
||||
dist-dir.jswasm := $(dist-dir.top)/$(notdir $(dir.dout))
|
||||
|
||||
@@ -18,7 +18,7 @@ endif
|
||||
ifeq (,$(SHELL_SRC))
|
||||
$(error Could not parse SHELL_SRC from $(dir.top)/Makefile.)
|
||||
endif
|
||||
$(dir.top)/shell.c: $(SHELL_SRC) $(dir.top)/tool/mkshellc.tcl $(sqlite3.c)
|
||||
$(dir.top)/shell.c: $(SHELL_SRC) $(dir.top)/tool/mkshellc.tcl
|
||||
$(MAKE) -C $(dir.top) shell.c
|
||||
# /shell.c
|
||||
########################################################################
|
||||
@@ -61,9 +61,9 @@ $(fiddle.SOAP.js): $(SOAP.js)
|
||||
$(eval $(call call-make-pre-post,fiddle-module,vanilla))
|
||||
$(fiddle-module.js): $(MAKEFILE) $(MAKEFILE.fiddle) \
|
||||
$(EXPORTED_FUNCTIONS.fiddle) \
|
||||
$(fiddle.cses) $(pre-post-fiddle-module-vanilla.deps) $(fiddle.SOAP.js)
|
||||
$(fiddle.cses) $(pre-post-fiddle-module.deps.vanilla) $(fiddle.SOAP.js)
|
||||
$(emcc.bin) -o $@ $(fiddle.emcc-flags) \
|
||||
$(pre-post-fiddle-module-vanilla.flags) \
|
||||
$(pre-post-fiddle-module.flags.vanilla) \
|
||||
$(fiddle.cses)
|
||||
$(maybe-wasm-strip) $(fiddle-module.wasm)
|
||||
gzip < $@ > $@.gz
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user