Compare commits
315 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 735e7ee62a | |||
| f2bb6ab3fb | |||
| c15133e82a | |||
| 91c8e65dd4 | |||
| 9eb7694d58 | |||
| 5cc4ab93f2 | |||
| 1b963102b7 | |||
| bd06d8672d | |||
| 4d6b10cdbe | |||
| 6b36312e8a | |||
| 973011432b | |||
| d7ef0e8f87 | |||
| 253727b33d | |||
| 7df044633d | |||
| 5d59669711 | |||
| d928e78088 | |||
| cb3a0b1b67 | |||
| 34ccd18d65 | |||
| 4ce5bc2836 | |||
| bdfc51dfef | |||
| adcd13d243 | |||
| b481413d95 | |||
| ce2edab088 | |||
| 87c407da18 | |||
| 7f7d7bea01 | |||
| 56e1610f7a | |||
| 307f95c1e4 | |||
| fb28a93f75 | |||
| 88dcfe56da | |||
| 10c815a55d | |||
| 5a81e6e5ce | |||
| 53381132be | |||
| 0eed27d38b | |||
| 9d60c3c256 | |||
| 828a00c287 | |||
| af65e7d21f | |||
| 6db0b11e07 | |||
| 3839dcf973 | |||
| 2009a5acba | |||
| beb06e6b0a | |||
| cb1f190ed2 | |||
| b10c3d32e0 | |||
| 752722e8d1 | |||
| 6eb381ff4a | |||
| b9050dcec7 | |||
| b18bb822dc | |||
| 194b8d514b | |||
| ec427813ac | |||
| c060b5f3a8 | |||
| 243f2ec6a1 | |||
| 2cbe14098b | |||
| b494366370 | |||
| 6234b33478 | |||
| e445586630 | |||
| 0ad5301378 | |||
| 8657eddcdb | |||
| 86b1898cb4 | |||
| c78d3b5963 | |||
| ec8f893e06 | |||
| d7b0b5ec6f | |||
| 275234e320 | |||
| 344255e5aa | |||
| 17a3295387 | |||
| 637922acac | |||
| 488125d4ce | |||
| 6dc368e632 | |||
| 6ca0cfd87a | |||
| 23056b608e | |||
| a3e8822c4b | |||
| ed99e7493a | |||
| 546db3f14a | |||
| 88c90a793e | |||
| 9a26589940 | |||
| 0ee7ae72a1 | |||
| 2c930e3b86 | |||
| b02ca781ad | |||
| 15d38c0dde | |||
| dc8a684c11 | |||
| ffdb479e7c | |||
| d4677f192f | |||
| 1b199243cb | |||
| 348e192ea8 | |||
| 5f3b13d136 | |||
| aad6808efc | |||
| 5189ef98d3 | |||
| ab8a4b2bb0 | |||
| b945183195 | |||
| 073f8f5458 | |||
| 1d5f2af063 | |||
| d706adba48 | |||
| dcd6d21fe9 | |||
| 792103a0f2 | |||
| 5e4233a9e4 | |||
| 78fee3f738 | |||
| 7b0fd0c564 | |||
| 06af40b2e1 | |||
| e186fe20f5 | |||
| 845a2e0cc2 | |||
| 8a93ce7458 | |||
| 57a7fdd814 | |||
| 7d52262889 | |||
| 0577cdec5f | |||
| 54e7a0f360 | |||
| 66476771b7 | |||
| 5ba99da869 | |||
| ddd1c97500 | |||
| 739edb99ee | |||
| 7545b0eab7 | |||
| 23f5f62d41 | |||
| cc2af7e68a | |||
| f3f0bd2155 | |||
| 383295eb17 | |||
| 2d0dd57dd5 | |||
| aa36dcf074 | |||
| a238c57eb8 | |||
| 30c9aca83e | |||
| 23e8f976dd | |||
| 88d3c148c8 | |||
| 8a95e92af9 | |||
| d2147bd32b | |||
| 7c2d3e8a06 | |||
| 84eab13df9 | |||
| 060c097e0d | |||
| 3017a6851f | |||
| 5269e846dc | |||
| 50862112b9 | |||
| 1b772cae2d | |||
| 793e872803 | |||
| a9fea28761 | |||
| 95c11b1158 | |||
| 4b8010a8ee | |||
| 26422f823f | |||
| 6572f7799c | |||
| 2e0d7b28c1 | |||
| a03932a790 | |||
| 1b2a93ea36 | |||
| c42276f63e | |||
| 1ecf531fb0 | |||
| d7969e5274 | |||
| 2473180bb1 | |||
| 113e15fbbe | |||
| 94331d406a | |||
| 69d40f8e1f | |||
| 3c75a03615 | |||
| 634b037fa3 | |||
| 63aff62e69 | |||
| 799e743333 | |||
| 208f5c65d4 | |||
| d91884586a | |||
| 1d91f8619b | |||
| b7086c48b6 | |||
| 47a484d8ed | |||
| 8f99cb9ad6 | |||
| a9a7d118f6 | |||
| 9f20bde65a | |||
| f4154879eb | |||
| 85b1f5c2f6 | |||
| 9d486435f1 | |||
| 3da982635c | |||
| d548f74024 | |||
| bdcae89358 | |||
| da67619d97 | |||
| c0ba6a97ec | |||
| eb9882d7d1 | |||
| 13e69440fb | |||
| 043768ce5a | |||
| 811bece3f7 | |||
| 4fecc81e20 | |||
| 2db02ec245 | |||
| 8c86f56e90 | |||
| fd05978a1e | |||
| 45b2e946cc | |||
| bd8941a4f8 | |||
| f49d52d642 | |||
| 3f81c3264a | |||
| 06700d77c2 | |||
| 47b496868c | |||
| 334b6fe418 | |||
| aa040d891f | |||
| 81be0d26bb | |||
| 90a5617b55 | |||
| 5e6dde3b35 | |||
| 96aa4d344d | |||
| a63539143d | |||
| 166c8d0067 | |||
| 32a0d6129f | |||
| 4598b6e5ae | |||
| 4bcde614d8 | |||
| 28f45b1ab1 | |||
| 6e4ef06ca1 | |||
| 06f4dc7edb | |||
| d2c737f2e4 | |||
| 76bd238a66 | |||
| ef6bf1bbe5 | |||
| da4cfde031 | |||
| 5c8cfe9617 | |||
| fcef73a787 | |||
| ca9dc173a2 | |||
| 51b358e5e5 | |||
| a35a54b824 | |||
| 813159e2ea | |||
| 531ca6058e | |||
| 266e5d034b | |||
| 51bb5dec6c | |||
| d5e040b6f0 | |||
| 71bae9f19f | |||
| 81656b15c9 | |||
| e98f62fb3f | |||
| ab3eb5b770 | |||
| 17c3408e0e | |||
| 90e4a3b7fc | |||
| 0a261eb4bb | |||
| 11223e0cff | |||
| d707e012f0 | |||
| dcf76a8bbd | |||
| 5f53f85e22 | |||
| 5080397e67 | |||
| d8b840a4f4 | |||
| ee3c55471c | |||
| 4e254641ff | |||
| 0588385799 | |||
| 11877c6ba4 | |||
| 266178f369 | |||
| 519017f25a | |||
| d9451051f1 | |||
| 0b633640fa | |||
| 69ff726d4d | |||
| d05e54efff | |||
| 42f5ea354e | |||
| ce51b31367 | |||
| 20b95f8d3f | |||
| 49dc1be96f | |||
| 0021079ceb | |||
| 59a0d0bbf9 | |||
| db19f48b69 | |||
| f8202f1ff3 | |||
| 0b4de1acac | |||
| 43dc31cf1f | |||
| 6db4e5ed4c | |||
| 42b49a3138 | |||
| 4c827feba1 | |||
| 52d934e316 | |||
| 5e0b11bda7 | |||
| 11095bcb89 | |||
| b866f98346 | |||
| 08747d44a2 | |||
| 626d0a9fda | |||
| f2d7e961d9 | |||
| 7e540e5a2c | |||
| 22fd153e43 | |||
| 19179722d6 | |||
| f83a4d850c | |||
| 24f6baca16 | |||
| 5698e62f9e | |||
| 29aeb620e7 | |||
| 6089e5bfcb | |||
| 8a273bb6e8 | |||
| 4b1e42ef94 | |||
| d718bde6da | |||
| eb62ccdab5 | |||
| 153790d9ae | |||
| 4213cc78cd | |||
| a9000d424e | |||
| c76e450908 | |||
| 7a54b12695 | |||
| a17f63290c | |||
| 067a13c395 | |||
| 6d035d481f | |||
| eaebd7fb6a | |||
| 2ba1acce81 | |||
| c84a0b2195 | |||
| 9132b8816a | |||
| cc67a62fa0 | |||
| 6f12e51313 | |||
| d7cdfe74f6 | |||
| af527231c1 | |||
| 9817065340 | |||
| 7dc0cc45f3 | |||
| 99d43979d3 | |||
| 5dc4a405c5 | |||
| 3046039309 | |||
| 4b136ed70a | |||
| a11499a156 | |||
| 5d9a6c6734 | |||
| 35d6c24fd5 | |||
| 6a0f297da9 | |||
| ac4aea5102 | |||
| 582d65cce3 | |||
| 58c7b770de | |||
| 853fb5e723 | |||
| 0b29d37ef9 | |||
| aaf1341dcd | |||
| 6c96bf2c3f | |||
| 01a5a19919 | |||
| 7e60f6dbf1 | |||
| 50da20d5b1 | |||
| faef28e6bd | |||
| 66dacae4c3 | |||
| 4a11142072 | |||
| c8f829cdac | |||
| fd013682d6 | |||
| 68585ea342 | |||
| 3b381b4afc | |||
| 4bdcfc6666 | |||
| 8f0634549c | |||
| 43e232dc47 | |||
| 1da3215d65 | |||
| 16096e6bd1 | |||
| e5adfb1a5f | |||
| 5918adb374 | |||
| 7ecef617ee | |||
| a25d478ea8 | |||
| 163af02aca | |||
| 4ffaa7c5de | |||
| 5289c51050 |
+14
-5
@@ -707,6 +707,9 @@ fuzzcheck$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
|
||||
fuzzcheck-asan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
|
||||
$(LTLINK) -o $@ -fsanitize=address $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
|
||||
|
||||
fuzzcheck-ubsan$(TEXE): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
|
||||
$(LTLINK) -o $@ -fsanitize=undefined $(FUZZCHECK_OPT) $(FUZZCHECK_SRC) sqlite3.c $(TLIBS)
|
||||
|
||||
ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
|
||||
$(LTLINK) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \
|
||||
$(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS)
|
||||
@@ -1328,17 +1331,23 @@ testrunner: testfixture$(TEXE)
|
||||
|
||||
# Runs both fuzztest and testrunner, consecutively.
|
||||
#
|
||||
devtest: testfixture$(TEXE) fuzztest testrunner
|
||||
devtest: srctree-check testfixture$(TEXE) fuzztest testrunner
|
||||
|
||||
mdevtest: has_tclsh85
|
||||
mdevtest: srctree-check has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl mdevtest
|
||||
|
||||
sdevtest: has_tclsh85
|
||||
$(TCLSH_CMD) $(TOP)/test/testrunner.tcl sdevtest
|
||||
|
||||
# Validate that various generated files in the source tree
|
||||
# are up-to-date.
|
||||
#
|
||||
srctree-check: $(TOP)/tool/srctree-check.tcl
|
||||
$(TCLSH_CMD) $(TOP)/tool/srctree-check.tcl
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest: testfixture$(TEXE)
|
||||
releasetest: srctree-check testfixture$(TEXE)
|
||||
./testfixture$(TEXE) $(TOP)/test/testrunner.tcl release
|
||||
|
||||
# Minimal testing that runs in less than 3 minutes
|
||||
@@ -1349,7 +1358,7 @@ quicktest: ./testfixture$(TEXE)
|
||||
# This is the common case. Run many tests that do not take too long,
|
||||
# including fuzzcheck, sqlite3_analyzer, and sqldiff tests.
|
||||
#
|
||||
test: fuzztest sourcetest $(TESTPROGS) tcltest
|
||||
test: srctree-check fuzztest sourcetest $(TESTPROGS) tcltest
|
||||
|
||||
# Run a test using valgrind. This can take a really long time
|
||||
# because valgrind is so much slower than a native machine.
|
||||
@@ -1480,7 +1489,7 @@ snapshot-tarball: sqlite3.c sqlite3rc.h
|
||||
|
||||
# Build a ZIP archive containing various command-line tools.
|
||||
#
|
||||
tool-zip: testfixture sqlite3 sqldiff sqlite3_analyzer tool/mktoolzip.tcl
|
||||
tool-zip: testfixture sqlite3 sqldiff sqlite3_analyzer $(TOP)/tool/mktoolzip.tcl
|
||||
./testfixture $(TOP)/tool/mktoolzip.tcl
|
||||
|
||||
# The next two rules are used to support the "threadtest" target. Building
|
||||
|
||||
+15
-11
@@ -52,8 +52,8 @@ MINIMAL_AMALGAMATION = 0
|
||||
USE_STDCALL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use structured exception handling (SEH) for WAL mode
|
||||
# in the core library.
|
||||
# Use the USE_SEH=0 option on the nmake command line to omit structured
|
||||
# exception handling (SEH) support. SEH is on by default.
|
||||
#
|
||||
!IFNDEF USE_SEH
|
||||
USE_SEH = 1
|
||||
@@ -374,6 +374,7 @@ SQLITE_TCL_DEP =
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
!IF $(MINIMAL_AMALGAMATION)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
|
||||
@@ -403,10 +404,11 @@ 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?
|
||||
# the core library? It is on by default. Only omit it if the
|
||||
# USE_SEH=0 option is provided on the nmake command-line.
|
||||
#
|
||||
!IF $(USE_SEH)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
|
||||
!IF $(USE_SEH)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
@@ -1592,8 +1594,7 @@ TESTEXT = \
|
||||
$(TOP)\ext\rtree\test_rtreedoc.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\test_recover.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
fts5.c
|
||||
$(TOP)\ext\recover\dbdata.c
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
#
|
||||
@@ -1609,7 +1610,8 @@ TESTSRC2 = \
|
||||
$(SRC01) \
|
||||
$(SRC07) \
|
||||
$(SRC10) \
|
||||
$(TOP)\ext\async\sqlite3async.c
|
||||
$(TOP)\ext\async\sqlite3async.c \
|
||||
fts5.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
@@ -1688,6 +1690,8 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
@@ -1819,8 +1823,8 @@ $(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLIT
|
||||
/link $(SQLITE3EXEPDB) $(LDFLAGS) $(LTLINKOPTS) $(SHELL_LINK_OPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS)
|
||||
|
||||
# <<mark>>
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
sqldiff.exe: $(TOP)\tool\sqldiff.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\sqldiff.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
|
||||
|
||||
dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
@@ -2466,7 +2470,7 @@ extensiontest: testfixture.exe testloadext.dll
|
||||
@set PATH=$(LIBTCLPATH);$(PATH)
|
||||
.\testfixture.exe $(TOP)\test\loadext.test $(TESTOPTS)
|
||||
|
||||
tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe tool\mktoolzip.tcl
|
||||
tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe $(TOP)\tool\mktoolzip.tcl
|
||||
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl
|
||||
|
||||
coretestprogs: $(TESTPROGS)
|
||||
|
||||
@@ -52,8 +52,8 @@ MINIMAL_AMALGAMATION = 0
|
||||
USE_STDCALL = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to use structured exception handling (SEH) for WAL mode
|
||||
# in the core library.
|
||||
# Use the USE_SEH=0 option on the nmake command line to omit structured
|
||||
# exception handling (SEH) support. SEH is on by default.
|
||||
#
|
||||
!IFNDEF USE_SEH
|
||||
USE_SEH = 1
|
||||
@@ -296,6 +296,7 @@ SQLITE3EXEPDB = /pdb:sqlite3sh.pdb
|
||||
!IFNDEF OPT_FEATURE_FLAGS
|
||||
!IF $(MINIMAL_AMALGAMATION)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_GEOPOLY=1
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB=1
|
||||
@@ -325,10 +326,11 @@ 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?
|
||||
# the core library? It is on by default. Only omit it if the
|
||||
# USE_SEH=0 option is provided on the nmake command-line.
|
||||
#
|
||||
!IF $(USE_SEH)!=0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_USE_SEH=1
|
||||
!IF $(USE_SEH)==0
|
||||
OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_OMIT_SEH=1
|
||||
!ENDIF
|
||||
|
||||
# These are the "extended" SQLite compilation options used when compiling for
|
||||
@@ -986,6 +988,8 @@ SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_DQS=0
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_FTS4=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_OFFSET_SQL_FUNC=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION=1
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_ENABLE_STMT_SCANSTATUS=1
|
||||
!ENDIF
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ dnl to configure the system for the local environment.
|
||||
# so that we create the export library with the dll.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
AC_INIT([sqlite],[3.44.0])
|
||||
AC_INIT([sqlite],[3.45.0])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.44.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.45.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.44.0'
|
||||
PACKAGE_STRING='sqlite 3.44.0'
|
||||
PACKAGE_VERSION='3.45.0'
|
||||
PACKAGE_STRING='sqlite 3.45.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1472,7 +1472,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.44.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.45.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1537,7 +1537,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.44.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.45.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1668,7 +1668,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.44.0
|
||||
sqlite configure 3.45.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2087,7 +2087,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.44.0, which was
|
||||
It was created by sqlite $as_me 3.45.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -12481,7 +12481,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.44.0, which was
|
||||
This file was extended by sqlite $as_me 3.45.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12547,7 +12547,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.44.0
|
||||
sqlite config.status 3.45.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@
|
||||
# you don't need (for example BLT) by erasing or commenting out
|
||||
# the corresponding code.
|
||||
#
|
||||
AC_INIT([sqlite],[m4_esyscmd(cat VERSION | tr -d '\n')])
|
||||
AC_INIT([sqlite],m4_esyscmd(cat VERSION | tr -d '\n'))
|
||||
|
||||
dnl Make sure the local VERSION file matches this configure script
|
||||
sqlite_version_sanity_check=`cat $srcdir/VERSION | tr -d '\n'`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 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:
|
||||
canonical source on a new Windows 11 PC, as of 2023-11-01:
|
||||
|
||||
1. Install Microsoft Visual Studio. The free "community edition"
|
||||
will work fine. Do a standard install for C++ development.
|
||||
@@ -16,7 +16,7 @@ canonical source on a new Windows 11 PC, as of 2023-08-16:
|
||||
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
|
||||
3. Install TCL development libraries. This note assumes that you will
|
||||
install the TCL development libraries in the "`c:\Tcl`" directory.
|
||||
Make adjustments
|
||||
if you want TCL installed somewhere else. SQLite needs both the
|
||||
@@ -83,3 +83,66 @@ following minor changes:
|
||||
<ul>
|
||||
<li> `set PATH=c:\tcl32\bin;%PATH%`
|
||||
</ul>
|
||||
|
||||
## Building a DLL
|
||||
|
||||
The command the developers use for building the deliverable DLL on the
|
||||
[download page](https://sqlite.org/download.html) is as follows:
|
||||
|
||||
> ~~~~
|
||||
nmake /f Makefile.msc sqlite3.dll USE_NATIVE_LIBPATHS=1 "OPTS=-DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_JSON1=1 -DSQLITE_ENABLE_GEOPOLY=1 -DSQLITE_ENABLE_SESSION=1 -DSQLITE_ENABLE_PREUPDATE_HOOK=1 -DSQLITE_ENABLE_SERIALIZE=1 -DSQLITE_ENABLE_MATH_FUNCTIONS=1"
|
||||
~~~~
|
||||
|
||||
That command generates both the sqlite3.dll and sqlite3.def files. The same
|
||||
command works for both 32-bit and 64-bit builds.
|
||||
|
||||
## Statically Linking The TCL Library
|
||||
|
||||
Some utility programs associated with SQLite need to be linked
|
||||
with TCL in order to function. The [sqlite3_analyzer.exe program](https://sqlite.org/sqlanalyze.html)
|
||||
is an example. You can build as described above, and then
|
||||
enter:
|
||||
|
||||
> ~~~~
|
||||
nmake /f Makefile.msc sqlite3_analyzer.exe
|
||||
~~~~
|
||||
|
||||
And you will end up with a working executable. However, that executable
|
||||
will depend on having the "tcl86.dll" library somewhere on your %PATH%.
|
||||
Use the following steps to build an executable that has the TCL library
|
||||
statically linked so that it does not depend on separate DLL:
|
||||
|
||||
1. Use the appropriate "Command Prompt" window - either x86 or
|
||||
x64, depending on whether you want a 32-bit or 64-bit executable.
|
||||
|
||||
2. Untar the TCL source tarball into a fresh directory. CD into
|
||||
the "win/" subfolder.
|
||||
|
||||
3. Run: `nmake /f makefile.vc OPTS=nothreads,static shell`
|
||||
|
||||
|
||||
4. CD into the "Release*" subfolder that is created (note the
|
||||
wildcard - the full name of the directory might vary). There
|
||||
you will find the "tcl86s.lib" file. Copy this file into the
|
||||
same directory that you put the "tcl86.lib" on your initial
|
||||
installation. (In this document, that directory is
|
||||
"`C:\Tcl32\lib`" for 32-bit builds and
|
||||
"`C:\Tcl\lib`" for 64-bit builds.)
|
||||
|
||||
5. CD into your SQLite source code directory and build the desired
|
||||
utility program, but add the following extra arguments to the
|
||||
nmake command line:
|
||||
<blockquote><pre>
|
||||
CCOPTS="-DSTATIC_BUILD" LIBTCL="tcl86s.lib netapi32.lib user32.lib"
|
||||
</pre></blockquote>
|
||||
<p>So, for example, to build a statically linked version of
|
||||
sqlite3_analyzer.exe, you might type:
|
||||
<blockquote><pre>
|
||||
nmake /f Makefile.msc CCOPTS="-DSTATIC_BUILD" LIBTCL="tcl86s.lib netapi32.lib user32.lib" sqlite3_analyzer.exe
|
||||
</pre></blockquote>
|
||||
|
||||
6. After your executable is built, you can verify that it does not
|
||||
depend on the TCL DLL by running:
|
||||
<blockquote><pre>
|
||||
dumpbin /dependents sqlite3_analyzer.exe
|
||||
</pre></blockquote>
|
||||
|
||||
@@ -464,4 +464,23 @@ do_execsql_test 5.3 {
|
||||
t2 t2_idx_0001295b {100 20 5}
|
||||
}
|
||||
|
||||
if 0 {
|
||||
do_test expert1-6.0 {
|
||||
catchcmd :memory: {
|
||||
.expert
|
||||
select base64('');
|
||||
.expert
|
||||
select name from pragma_collation_list order by name collate uint;
|
||||
}
|
||||
} {0 {(no new indexes)
|
||||
|
||||
SCAN CONSTANT ROW
|
||||
|
||||
(no new indexes)
|
||||
|
||||
SCAN pragma_collation_list VIRTUAL TABLE INDEX 0:
|
||||
USE TEMP B-TREE FOR ORDER BY
|
||||
}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+102
-2
@@ -32,7 +32,7 @@
|
||||
#endif /* !defined(SQLITE_AMALGAMATION) */
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef sqlite3_int64 i64;
|
||||
typedef sqlite3_uint64 u64;
|
||||
@@ -1819,6 +1819,88 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Define and possibly pretend to use a useless collation sequence.
|
||||
** This pretense allows expert to accept SQL using custom collations.
|
||||
*/
|
||||
int dummyCompare(void *up1, int up2, const void *up3, int up4, const void *up5){
|
||||
(void)up1;
|
||||
(void)up2;
|
||||
(void)up3;
|
||||
(void)up4;
|
||||
(void)up5;
|
||||
assert(0); /* VDBE should never be run. */
|
||||
return 0;
|
||||
}
|
||||
/* And a callback to register above upon actual need */
|
||||
void useDummyCS(void *up1, sqlite3 *db, int etr, const char *zName){
|
||||
(void)up1;
|
||||
sqlite3_create_collation_v2(db, zName, etr, 0, dummyCompare, 0);
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \
|
||||
&& !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
|
||||
/*
|
||||
** dummy functions for no-op implementation of UDFs during expert's work
|
||||
*/
|
||||
void dummyUDF(sqlite3_context *up1, int up2, sqlite3_value **up3){
|
||||
(void)up1;
|
||||
(void)up2;
|
||||
(void)up3;
|
||||
assert(0); /* VDBE should never be run. */
|
||||
}
|
||||
void dummyUDFvalue(sqlite3_context *up1){
|
||||
(void)up1;
|
||||
assert(0); /* VDBE should never be run. */
|
||||
}
|
||||
|
||||
/*
|
||||
** Register UDFs from user database with another.
|
||||
*/
|
||||
int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc = sqlite3_prepare_v2(dbSrc,
|
||||
"SELECT name,type,enc,narg,flags "
|
||||
"FROM pragma_function_list() "
|
||||
"WHERE builtin==0", -1, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
|
||||
int nargs = sqlite3_column_int(pStmt,3);
|
||||
int flags = sqlite3_column_int(pStmt,4);
|
||||
const char *name = (char*)sqlite3_column_text(pStmt,0);
|
||||
const char *type = (char*)sqlite3_column_text(pStmt,1);
|
||||
const char *enc = (char*)sqlite3_column_text(pStmt,2);
|
||||
if( name==0 || type==0 || enc==0 ){
|
||||
/* no-op. Only happens on OOM */
|
||||
}else{
|
||||
int ienc = SQLITE_UTF8;
|
||||
int rcf = SQLITE_ERROR;
|
||||
if( strcmp(enc,"utf16le")==0 ) ienc = SQLITE_UTF16LE;
|
||||
else if( strcmp(enc,"utf16be")==0 ) ienc = SQLITE_UTF16BE;
|
||||
ienc |= (flags & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY));
|
||||
if( strcmp(type,"w")==0 ){
|
||||
rcf = sqlite3_create_window_function(dbDst,name,nargs,ienc,0,
|
||||
dummyUDF,dummyUDFvalue,0,0,0);
|
||||
}else if( strcmp(type,"a")==0 ){
|
||||
rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0,
|
||||
0,dummyUDF,dummyUDFvalue);
|
||||
}else if( strcmp(type,"s")==0 ){
|
||||
rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0,
|
||||
dummyUDF,0,0);
|
||||
}
|
||||
if( rcf!=SQLITE_OK ){
|
||||
rc = rcf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Allocate a new sqlite3expert object.
|
||||
*/
|
||||
@@ -1845,7 +1927,21 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){
|
||||
sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Allow custom collations to be dealt with through prepare. */
|
||||
if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbm,0,useDummyCS);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbv,0,useDummyCS);
|
||||
|
||||
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \
|
||||
&& !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
|
||||
/* Register UDFs from database [db] with [dbm] and [dbv]. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = registerUDFs(pNew->db, pNew->dbm);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = registerUDFs(pNew->db, pNew->dbv);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Copy the entire schema of database [db] into [dbm]. */
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -1921,6 +2017,10 @@ int sqlite3_expert_sql(
|
||||
|
||||
while( rc==SQLITE_OK && zStmt && zStmt[0] ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
/* Ensure that the provided statement compiles against user's DB. */
|
||||
rc = idxPrepareStmt(p->db, &pStmt, pzErr, zStmt);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
sqlite3_finalize(pStmt);
|
||||
rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pStmt ){
|
||||
|
||||
+61
-22
@@ -640,6 +640,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
|
||||
|
||||
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
|
||||
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
|
||||
sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
|
||||
|
||||
/* Create a list of user columns for the virtual table */
|
||||
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
|
||||
@@ -3889,6 +3890,8 @@ static int fts3RenameMethod(
|
||||
rc = sqlite3Fts3PendingTermsFlush(p);
|
||||
}
|
||||
|
||||
p->bIgnoreSavepoint = 1;
|
||||
|
||||
if( p->zContentTbl==0 ){
|
||||
fts3DbExec(&rc, db,
|
||||
"ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
|
||||
@@ -3916,6 +3919,8 @@ static int fts3RenameMethod(
|
||||
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
|
||||
p->zDb, p->zName, zName
|
||||
);
|
||||
|
||||
p->bIgnoreSavepoint = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3926,12 +3931,28 @@ static int fts3RenameMethod(
|
||||
*/
|
||||
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
int rc = SQLITE_OK;
|
||||
UNUSED_PARAMETER(iSavepoint);
|
||||
assert( ((Fts3Table *)pVtab)->inTransaction );
|
||||
assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
|
||||
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
|
||||
if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
|
||||
rc = fts3SyncMethod(pVtab);
|
||||
Fts3Table *pTab = (Fts3Table*)pVtab;
|
||||
assert( pTab->inTransaction );
|
||||
assert( pTab->mxSavepoint<=iSavepoint );
|
||||
TESTONLY( pTab->mxSavepoint = iSavepoint );
|
||||
|
||||
if( pTab->bIgnoreSavepoint==0 ){
|
||||
if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){
|
||||
char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
|
||||
pTab->zDb, pTab->zName, pTab->zName
|
||||
);
|
||||
if( zSql ){
|
||||
pTab->bIgnoreSavepoint = 1;
|
||||
rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
|
||||
pTab->bIgnoreSavepoint = 0;
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab->iSavepoint = iSavepoint+1;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -3942,12 +3963,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
** This is a no-op.
|
||||
*/
|
||||
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
|
||||
UNUSED_PARAMETER(iSavepoint);
|
||||
UNUSED_PARAMETER(pVtab);
|
||||
assert( p->inTransaction );
|
||||
assert( p->mxSavepoint >= iSavepoint );
|
||||
TESTONLY( p->mxSavepoint = iSavepoint-1 );
|
||||
Fts3Table *pTab = (Fts3Table*)pVtab;
|
||||
assert( pTab->inTransaction );
|
||||
assert( pTab->mxSavepoint >= iSavepoint );
|
||||
TESTONLY( pTab->mxSavepoint = iSavepoint-1 );
|
||||
pTab->iSavepoint = iSavepoint;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -3957,11 +3977,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
** Discard the contents of the pending terms table.
|
||||
*/
|
||||
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
Fts3Table *pTab = (Fts3Table*)pVtab;
|
||||
UNUSED_PARAMETER(iSavepoint);
|
||||
assert( p->inTransaction );
|
||||
TESTONLY( p->mxSavepoint = iSavepoint );
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
assert( pTab->inTransaction );
|
||||
TESTONLY( pTab->mxSavepoint = iSavepoint );
|
||||
if( (iSavepoint+1)<=pTab->iSavepoint ){
|
||||
sqlite3Fts3PendingTermsClear(pTab);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -3984,22 +4006,39 @@ static int fts3ShadowName(const char *zName){
|
||||
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
|
||||
** table.
|
||||
*/
|
||||
static int fts3Integrity(sqlite3_vtab *pVtab, char **pzErr){
|
||||
static int fts3Integrity(
|
||||
sqlite3_vtab *pVtab, /* The virtual table to be checked */
|
||||
const char *zSchema, /* Name of schema in which pVtab lives */
|
||||
const char *zTabname, /* Name of the pVTab table */
|
||||
int isQuick, /* True if this is a quick_check */
|
||||
char **pzErr /* Write error message here */
|
||||
){
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
char *zSql;
|
||||
int rc;
|
||||
char *zErr = 0;
|
||||
|
||||
assert( pzErr!=0 );
|
||||
assert( *pzErr==0 );
|
||||
UNUSED_PARAMETER(isQuick);
|
||||
zSql = sqlite3_mprintf(
|
||||
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
|
||||
p->zDb, p->zName, p->zName);
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
|
||||
zSchema, zTabname, zTabname);
|
||||
if( zSql==0 ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, &zErr);
|
||||
sqlite3_free(zSql);
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
|
||||
p->bFts4 ? 4 : 3, p->zDb, p->zName);
|
||||
rc = SQLITE_OK;
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname);
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS%d table %s.%s: %s",
|
||||
p->bFts4 ? 4 : 3, zSchema, zTabname, zErr);
|
||||
}
|
||||
return rc;
|
||||
sqlite3_free(zErr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ struct Fts3Table {
|
||||
int nPgsz; /* Page size for host database */
|
||||
char *zSegmentsTbl; /* Name of %_segments table */
|
||||
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
|
||||
int iSavepoint;
|
||||
|
||||
/*
|
||||
** The following array of hash tables is used to buffer pending index
|
||||
|
||||
+19
-7
@@ -3325,7 +3325,6 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
|
||||
rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
|
||||
/* Determine the auto-incr-merge setting if unknown. If enabled,
|
||||
** estimate the number of leaf blocks of content to be written
|
||||
@@ -3347,6 +3346,10 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
|
||||
rc = sqlite3_reset(pStmt);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -3978,6 +3981,8 @@ static int fts3AppendToNode(
|
||||
|
||||
blobGrowBuffer(pPrev, nTerm, &rc);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
assert( pPrev!=0 );
|
||||
assert( pPrev->a!=0 );
|
||||
|
||||
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
|
||||
nSuffix = nTerm - nPrefix;
|
||||
@@ -4034,9 +4039,13 @@ static int fts3IncrmergeAppend(
|
||||
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
|
||||
|
||||
/* If the current block is not empty, and if adding this term/doclist
|
||||
** to the current block would make it larger than Fts3Table.nNodeSize
|
||||
** bytes, write this block out to the database. */
|
||||
if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
|
||||
** to the current block would make it larger than Fts3Table.nNodeSize bytes,
|
||||
** and if there is still room for another leaf page, write this block out to
|
||||
** the database. */
|
||||
if( pLeaf->block.n>0
|
||||
&& (pLeaf->block.n + nSpace)>p->nNodeSize
|
||||
&& pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
|
||||
){
|
||||
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
|
||||
pWriter->nWork++;
|
||||
|
||||
@@ -4368,7 +4377,7 @@ static int fts3IncrmergeLoad(
|
||||
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
|
||||
blobGrowBuffer(&pNode->block,
|
||||
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
|
||||
);
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
memcpy(pNode->block.a, aBlock, nBlock);
|
||||
pNode->block.n = nBlock;
|
||||
@@ -5433,8 +5442,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
rc = fts3DoIncrmerge(p, &zVal[6]);
|
||||
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
|
||||
rc = fts3DoAutoincrmerge(p, &zVal[10]);
|
||||
}else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){
|
||||
rc = sqlite3Fts3PendingTermsFlush(p);
|
||||
}
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||
}else{
|
||||
else{
|
||||
int v;
|
||||
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
|
||||
v = atoi(&zVal[9]);
|
||||
@@ -5452,8 +5464,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
||||
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
+47
-12
@@ -110,15 +110,19 @@ static int fts5CInstIterInit(
|
||||
*/
|
||||
typedef struct HighlightContext HighlightContext;
|
||||
struct HighlightContext {
|
||||
CInstIter iter; /* Coalesced Instance Iterator */
|
||||
int iPos; /* Current token offset in zIn[] */
|
||||
/* Constant parameters to fts5HighlightCb() */
|
||||
int iRangeStart; /* First token to include */
|
||||
int iRangeEnd; /* If non-zero, last token to include */
|
||||
const char *zOpen; /* Opening highlight */
|
||||
const char *zClose; /* Closing highlight */
|
||||
const char *zIn; /* Input text */
|
||||
int nIn; /* Size of input text in bytes */
|
||||
int iOff; /* Current offset within zIn[] */
|
||||
|
||||
/* Variables modified by fts5HighlightCb() */
|
||||
CInstIter iter; /* Coalesced Instance Iterator */
|
||||
int iPos; /* Current token offset in zIn[] */
|
||||
int iOff; /* Have copied up to this offset in zIn[] */
|
||||
int bOpen; /* True if highlight is open */
|
||||
char *zOut; /* Output value */
|
||||
};
|
||||
|
||||
@@ -151,8 +155,8 @@ static int fts5HighlightCb(
|
||||
int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
const char *pToken, /* Buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
int iStartOff, /* Start offset of token */
|
||||
int iEndOff /* End offset of token */
|
||||
int iStartOff, /* Start byte offset of token */
|
||||
int iEndOff /* End byte offset of token */
|
||||
){
|
||||
HighlightContext *p = (HighlightContext*)pContext;
|
||||
int rc = SQLITE_OK;
|
||||
@@ -168,30 +172,55 @@ static int fts5HighlightCb(
|
||||
if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
|
||||
}
|
||||
|
||||
if( iPos==p->iter.iStart ){
|
||||
/* If the parenthesis is open, and this token is not part of the current
|
||||
** phrase, and the starting byte offset of this token is past the point
|
||||
** that has currently been copied into the output buffer, close the
|
||||
** parenthesis. */
|
||||
if( p->bOpen
|
||||
&& (iPos<=p->iter.iStart || p->iter.iStart<0)
|
||||
&& iStartOff>p->iOff
|
||||
){
|
||||
fts5HighlightAppend(&rc, p, p->zClose, -1);
|
||||
p->bOpen = 0;
|
||||
}
|
||||
|
||||
/* If this is the start of a new phrase, and the highlight is not open:
|
||||
**
|
||||
** * copy text from the input up to the start of the phrase, and
|
||||
** * open the highlight.
|
||||
*/
|
||||
if( iPos==p->iter.iStart && p->bOpen==0 ){
|
||||
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
|
||||
fts5HighlightAppend(&rc, p, p->zOpen, -1);
|
||||
p->iOff = iStartOff;
|
||||
p->bOpen = 1;
|
||||
}
|
||||
|
||||
if( iPos==p->iter.iEnd ){
|
||||
if( p->iRangeEnd>=0 && p->iter.iStart<p->iRangeStart ){
|
||||
if( p->bOpen==0 ){
|
||||
assert( p->iRangeEnd>=0 );
|
||||
fts5HighlightAppend(&rc, p, p->zOpen, -1);
|
||||
p->bOpen = 1;
|
||||
}
|
||||
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
|
||||
fts5HighlightAppend(&rc, p, p->zClose, -1);
|
||||
p->iOff = iEndOff;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5CInstIterNext(&p->iter);
|
||||
}
|
||||
}
|
||||
|
||||
if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){
|
||||
if( iPos==p->iRangeEnd ){
|
||||
if( p->bOpen ){
|
||||
if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
|
||||
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
|
||||
p->iOff = iEndOff;
|
||||
}
|
||||
fts5HighlightAppend(&rc, p, p->zClose, -1);
|
||||
p->bOpen = 0;
|
||||
}
|
||||
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
|
||||
p->iOff = iEndOff;
|
||||
if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
|
||||
fts5HighlightAppend(&rc, p, p->zClose, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -232,6 +261,9 @@ static void fts5HighlightFunction(
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
|
||||
}
|
||||
if( ctx.bOpen ){
|
||||
fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
|
||||
}
|
||||
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -510,6 +542,9 @@ static void fts5SnippetFunction(
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
|
||||
}
|
||||
if( ctx.bOpen ){
|
||||
fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
|
||||
}
|
||||
if( ctx.iRangeEnd>=(nColSize-1) ){
|
||||
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
|
||||
}else{
|
||||
|
||||
@@ -432,10 +432,8 @@ static Fts5HashEntry *fts5HashEntryMerge(
|
||||
}
|
||||
|
||||
/*
|
||||
** Extract all tokens from hash table iHash and link them into a list
|
||||
** in sorted order. The hash table is cleared before returning. It is
|
||||
** the responsibility of the caller to free the elements of the returned
|
||||
** list.
|
||||
** Link all tokens from hash table iHash into a list in sorted order. The
|
||||
** tokens are not removed from the hash table.
|
||||
*/
|
||||
static int fts5HashEntrySort(
|
||||
Fts5Hash *pHash,
|
||||
|
||||
+63
-34
@@ -2719,6 +2719,14 @@ static void fts5SegIterHashInit(
|
||||
pLeaf->p = (u8*)pList;
|
||||
}
|
||||
}
|
||||
|
||||
/* The call to sqlite3Fts5HashScanInit() causes the hash table to
|
||||
** fill the size field of all existing position lists. This means they
|
||||
** can no longer be appended to. Since the only scenario in which they
|
||||
** can be appended to is if the previous operation on this table was
|
||||
** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this
|
||||
** possibility altogether. */
|
||||
p->bDelete = 0;
|
||||
}else{
|
||||
p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
|
||||
(const char*)pTerm, nTerm, (void**)&pLeaf, &nList
|
||||
@@ -2904,7 +2912,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
|
||||
assert_nc( i2!=0 );
|
||||
pRes->bTermEq = 1;
|
||||
if( p1->iRowid==p2->iRowid ){
|
||||
p1->bDel = p2->bDel;
|
||||
return i2;
|
||||
}
|
||||
res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
|
||||
@@ -5132,34 +5139,39 @@ static void fts5DoSecureDelete(
|
||||
|
||||
/* Set variable bLastInDoclist to true if this entry happens to be
|
||||
** the last rowid in the doclist for its term. */
|
||||
if( iNextOff>=iPgIdx ){
|
||||
int pgno = pSeg->iLeafPgno+1;
|
||||
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
|
||||
iNextOff = iPgIdx;
|
||||
}else{
|
||||
/* Loop through the page-footer. If iNextOff (offset of the
|
||||
** entry following the one we are removing) is equal to the
|
||||
** offset of a key on this page, then the entry is the last
|
||||
** in its doclist. */
|
||||
int iKeyOff = 0;
|
||||
for(iIdx=0; iIdx<nIdx; /* no-op */){
|
||||
u32 iVal = 0;
|
||||
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
|
||||
iKeyOff += iVal;
|
||||
if( iKeyOff==iNextOff ){
|
||||
bLastInDoclist = 1;
|
||||
if( pSeg->bDel==0 ){
|
||||
if( iNextOff>=iPgIdx ){
|
||||
int pgno = pSeg->iLeafPgno+1;
|
||||
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
|
||||
iNextOff = iPgIdx;
|
||||
}else{
|
||||
/* Loop through the page-footer. If iNextOff (offset of the
|
||||
** entry following the one we are removing) is equal to the
|
||||
** offset of a key on this page, then the entry is the last
|
||||
** in its doclist. */
|
||||
int iKeyOff = 0;
|
||||
for(iIdx=0; iIdx<nIdx; /* no-op */){
|
||||
u32 iVal = 0;
|
||||
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
|
||||
iKeyOff += iVal;
|
||||
if( iKeyOff==iNextOff ){
|
||||
bLastInDoclist = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is (a) the first rowid on a page and (b) is not followed by
|
||||
** another position list on the same page, set the "first-rowid" field
|
||||
** of the header to 0. */
|
||||
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
|
||||
fts5PutU16(&aPg[0], 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is (a) the first rowid on a page and (b) is not followed by
|
||||
** another position list on the same page, set the "first-rowid" field
|
||||
** of the header to 0. */
|
||||
if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
|
||||
fts5PutU16(&aPg[0], 0);
|
||||
}
|
||||
|
||||
if( bLastInDoclist==0 ){
|
||||
if( pSeg->bDel ){
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta);
|
||||
aPg[iOff++] = 0x01;
|
||||
}else if( bLastInDoclist==0 ){
|
||||
if( iNextOff!=iPgIdx ){
|
||||
u64 iNextDelta = 0;
|
||||
iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
|
||||
@@ -5271,6 +5283,15 @@ static void fts5DoSecureDelete(
|
||||
}
|
||||
}
|
||||
|
||||
/* Assuming no error has occurred, this block does final edits to the
|
||||
** leaf page before writing it back to disk. Input variables are:
|
||||
**
|
||||
** nPg: Total initial size of leaf page.
|
||||
** iPgIdx: Initial offset of page footer.
|
||||
**
|
||||
** iOff: Offset to move data to
|
||||
** iNextOff: Offset to move data from
|
||||
*/
|
||||
if( p->rc==SQLITE_OK ){
|
||||
const int nMove = nPg - iNextOff; /* Number of bytes to move */
|
||||
int nShift = iNextOff - iOff; /* Distance to move them */
|
||||
@@ -5471,10 +5492,16 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
fts5WriteFlushLeaf(p, &writer);
|
||||
}
|
||||
}else{
|
||||
int bDummy;
|
||||
int nPos;
|
||||
int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
|
||||
nCopy += nPos;
|
||||
int bDel = 0;
|
||||
int nPos = 0;
|
||||
int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel);
|
||||
if( bDel && bSecureDelete ){
|
||||
fts5BufferAppendVarint(&p->rc, pBuf, nPos*2);
|
||||
iOff += nCopy;
|
||||
nCopy = nPos;
|
||||
}else{
|
||||
nCopy += nPos;
|
||||
}
|
||||
if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
|
||||
/* The entire poslist will fit on the current leaf. So copy
|
||||
** it in one go. */
|
||||
@@ -5512,7 +5539,6 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
assert( pBuf->n<=pBuf->nSpace );
|
||||
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
|
||||
}
|
||||
sqlite3Fts5HashClear(pHash);
|
||||
fts5WriteFinish(p, &writer, &pgnoLast);
|
||||
|
||||
assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
|
||||
@@ -5545,7 +5571,6 @@ static void fts5FlushOneHash(Fts5Index *p){
|
||||
fts5IndexCrisismerge(p, &pStruct);
|
||||
fts5StructureWrite(p, pStruct);
|
||||
fts5StructureRelease(pStruct);
|
||||
p->nContentlessDelete = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5556,8 +5581,12 @@ static void fts5IndexFlush(Fts5Index *p){
|
||||
if( p->nPendingData || p->nContentlessDelete ){
|
||||
assert( p->pHash );
|
||||
fts5FlushOneHash(p);
|
||||
p->nPendingData = 0;
|
||||
p->nPendingRow = 0;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3Fts5HashClear(p->pHash);
|
||||
p->nPendingData = 0;
|
||||
p->nPendingRow = 0;
|
||||
p->nContentlessDelete = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6183,7 +6212,7 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
|
||||
/* Flush the hash table to disk if required */
|
||||
if( iRowid<p->iWriteRowid
|
||||
|| (iRowid==p->iWriteRowid && p->bDelete==0)
|
||||
|| (p->nPendingData > p->pConfig->nHashSize)
|
||||
|| (p->nPendingData > p->pConfig->nHashSize)
|
||||
){
|
||||
fts5IndexFlush(p);
|
||||
}
|
||||
|
||||
+79
-16
@@ -117,6 +117,8 @@ struct Fts5FullTable {
|
||||
Fts5Storage *pStorage; /* Document store */
|
||||
Fts5Global *pGlobal; /* Global (connection wide) data */
|
||||
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
|
||||
int iSavepoint; /* Successful xSavepoint()+1 */
|
||||
int bInSavepoint;
|
||||
#ifdef SQLITE_DEBUG
|
||||
struct Fts5TransactionState ts;
|
||||
#endif
|
||||
@@ -408,6 +410,9 @@ static int fts5InitVtab(
|
||||
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5FreeVtab(pTab);
|
||||
@@ -1530,6 +1535,7 @@ static int fts5SpecialInsert(
|
||||
Fts5Config *pConfig = pTab->p.pConfig;
|
||||
int rc = SQLITE_OK;
|
||||
int bError = 0;
|
||||
int bLoadConfig = 0;
|
||||
|
||||
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
@@ -1541,6 +1547,7 @@ static int fts5SpecialInsert(
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
|
||||
}
|
||||
bLoadConfig = 1;
|
||||
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NONE ){
|
||||
fts5SetVtabError(pTab,
|
||||
@@ -1550,6 +1557,7 @@ static int fts5SpecialInsert(
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
|
||||
}
|
||||
bLoadConfig = 1;
|
||||
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
|
||||
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
|
||||
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
|
||||
@@ -1562,6 +1570,8 @@ static int fts5SpecialInsert(
|
||||
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
|
||||
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
|
||||
#endif
|
||||
}else if( 0==sqlite3_stricmp("flush", zCmd) ){
|
||||
rc = sqlite3Fts5FlushToDisk(&pTab->p);
|
||||
}else{
|
||||
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -1575,6 +1585,12 @@ static int fts5SpecialInsert(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && bLoadConfig ){
|
||||
pTab->p.pConfig->iCookie--;
|
||||
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2597,8 +2613,12 @@ static int fts5RenameMethod(
|
||||
sqlite3_vtab *pVtab, /* Virtual table handle */
|
||||
const char *zName /* New name of table */
|
||||
){
|
||||
int rc;
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
return sqlite3Fts5StorageRename(pTab->pStorage, zName);
|
||||
pTab->bInSavepoint = 1;
|
||||
rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
|
||||
pTab->bInSavepoint = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
|
||||
@@ -2612,9 +2632,29 @@ int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
|
||||
** Flush the contents of the pending-terms table to disk.
|
||||
*/
|
||||
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
|
||||
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
|
||||
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
char *zSql = 0;
|
||||
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
|
||||
|
||||
if( pTab->bInSavepoint==0 ){
|
||||
zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
|
||||
pTab->p.pConfig->zDb, pTab->p.pConfig->zName, pTab->p.pConfig->zName
|
||||
);
|
||||
if( zSql ){
|
||||
pTab->bInSavepoint = 1;
|
||||
rc = sqlite3_exec(pTab->p.pConfig->db, zSql, 0, 0, 0);
|
||||
pTab->bInSavepoint = 0;
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab->iSavepoint = iSavepoint+1;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2623,9 +2663,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
** This is a no-op.
|
||||
*/
|
||||
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
|
||||
fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
|
||||
return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
int rc = SQLITE_OK;
|
||||
fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
|
||||
if( (iSavepoint+1)<pTab->iSavepoint ){
|
||||
rc = sqlite3Fts5FlushToDisk(&pTab->p);
|
||||
if( rc==SQLITE_OK ){
|
||||
pTab->iSavepoint = iSavepoint;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2635,11 +2682,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
*/
|
||||
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
|
||||
int rc = SQLITE_OK;
|
||||
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
|
||||
fts5TripCursors(pTab);
|
||||
pTab->p.pConfig->pgsz = 0;
|
||||
return sqlite3Fts5StorageRollback(pTab->pStorage);
|
||||
if( (iSavepoint+1)<=pTab->iSavepoint ){
|
||||
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2864,23 +2914,36 @@ static int fts5ShadowName(const char *zName){
|
||||
** if anything is found amiss. Return a NULL pointer if everything is
|
||||
** OK.
|
||||
*/
|
||||
static int fts5Integrity(sqlite3_vtab *pVtab, char **pzErr){
|
||||
static int fts5Integrity(
|
||||
sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
|
||||
const char *zSchema, /* Name of schema in which this table lives */
|
||||
const char *zTabname, /* Name of the table itself */
|
||||
int isQuick, /* True if this is a quick-check */
|
||||
char **pzErr /* Write error message here */
|
||||
){
|
||||
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
|
||||
Fts5Config *pConfig = pTab->p.pConfig;
|
||||
char *zSql;
|
||||
char *zErr = 0;
|
||||
int rc;
|
||||
assert( pzErr!=0 && *pzErr==0 );
|
||||
UNUSED_PARAM(isQuick);
|
||||
zSql = sqlite3_mprintf(
|
||||
"INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
|
||||
pConfig->zDb, pConfig->zName, pConfig->zName);
|
||||
rc = sqlite3_exec(pConfig->db, zSql, 0, 0, 0);
|
||||
zSchema, zTabname, pConfig->zName);
|
||||
if( zSql==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_exec(pConfig->db, zSql, 0, 0, &zErr);
|
||||
sqlite3_free(zSql);
|
||||
if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
|
||||
pConfig->zDb, pConfig->zName);
|
||||
rc = SQLITE_OK;
|
||||
zSchema, zTabname);
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
|
||||
" FTS5 table %s.%s: %s",
|
||||
zSchema, zTabname, zErr);
|
||||
}
|
||||
return rc;
|
||||
|
||||
sqlite3_free(zErr);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static int fts5Init(sqlite3 *db){
|
||||
|
||||
@@ -1184,7 +1184,9 @@ int sqlite3Fts5StorageSync(Fts5Storage *p){
|
||||
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
|
||||
if( p->bTotalsValid ){
|
||||
rc = fts5StorageSaveTotals(p);
|
||||
p->bTotalsValid = 0;
|
||||
if( rc==SQLITE_OK ){
|
||||
p->bTotalsValid = 0;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5IndexSync(p->pIndex);
|
||||
|
||||
+70
-27
@@ -229,6 +229,12 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|
||||
|
||||
#endif /* ifndef SQLITE_AMALGAMATION */
|
||||
|
||||
#define FTS5_SKIP_UTF8(zIn) { \
|
||||
if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
|
||||
while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
|
||||
struct Unicode61Tokenizer {
|
||||
unsigned char aTokenChar[128]; /* ASCII range token characters */
|
||||
@@ -1264,6 +1270,7 @@ static int fts5PorterTokenize(
|
||||
typedef struct TrigramTokenizer TrigramTokenizer;
|
||||
struct TrigramTokenizer {
|
||||
int bFold; /* True to fold to lower-case */
|
||||
int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1290,6 +1297,7 @@ static int fts5TriCreate(
|
||||
}else{
|
||||
int i;
|
||||
pNew->bFold = 1;
|
||||
pNew->iFoldParam = 0;
|
||||
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
|
||||
const char *zArg = azArg[i+1];
|
||||
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
|
||||
@@ -1298,10 +1306,21 @@ static int fts5TriCreate(
|
||||
}else{
|
||||
pNew->bFold = (zArg[0]=='0');
|
||||
}
|
||||
}else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
|
||||
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
fts5TriDelete((Fts5Tokenizer*)pNew);
|
||||
pNew = 0;
|
||||
@@ -1324,40 +1343,62 @@ static int fts5TriTokenize(
|
||||
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
|
||||
int rc = SQLITE_OK;
|
||||
char aBuf[32];
|
||||
char *zOut = aBuf;
|
||||
int ii;
|
||||
const unsigned char *zIn = (const unsigned char*)pText;
|
||||
const unsigned char *zEof = &zIn[nText];
|
||||
u32 iCode;
|
||||
int aStart[3]; /* Input offset of each character in aBuf[] */
|
||||
|
||||
UNUSED_PARAM(unusedFlags);
|
||||
while( 1 ){
|
||||
char *zOut = aBuf;
|
||||
int iStart = zIn - (const unsigned char*)pText;
|
||||
const unsigned char *zNext;
|
||||
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
zNext = zIn;
|
||||
if( zIn<zEof ){
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
/* Populate aBuf[] with the characters for the first trigram. */
|
||||
for(ii=0; ii<3; ii++){
|
||||
do {
|
||||
aStart[ii] = zIn - (const unsigned char*)pText;
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) return SQLITE_OK;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
|
||||
}while( iCode==0 );
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
}
|
||||
|
||||
/* At the start of each iteration of this loop:
|
||||
**
|
||||
** aBuf: Contains 3 characters. The 3 characters of the next trigram.
|
||||
** zOut: Points to the byte following the last character in aBuf.
|
||||
** aStart[3]: Contains the byte offset in the input text corresponding
|
||||
** to the start of each of the three characters in the buffer.
|
||||
*/
|
||||
assert( zIn<=zEof );
|
||||
while( 1 ){
|
||||
int iNext; /* Start of character following current tri */
|
||||
const char *z1;
|
||||
|
||||
/* Read characters from the input up until the first non-diacritic */
|
||||
do {
|
||||
iNext = zIn - (const unsigned char*)pText;
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
if( zIn<zEof ){
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
READ_UTF8(zIn, zEof, iCode);
|
||||
if( iCode==0 ) break;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
zIn = zNext;
|
||||
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
|
||||
}while( iCode==0 );
|
||||
|
||||
/* Pass the current trigram back to fts5 */
|
||||
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
|
||||
if( iCode==0 || rc!=SQLITE_OK ) break;
|
||||
|
||||
/* Remove the first character from buffer aBuf[]. Append the character
|
||||
** with codepoint iCode. */
|
||||
z1 = aBuf;
|
||||
FTS5_SKIP_UTF8(z1);
|
||||
memmove(aBuf, z1, zOut - z1);
|
||||
zOut -= (z1 - aBuf);
|
||||
WRITE_UTF8(zOut, iCode);
|
||||
|
||||
/* Update the aStart[] array */
|
||||
aStart[0] = aStart[1];
|
||||
aStart[1] = aStart[2];
|
||||
aStart[2] = iNext;
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -1380,7 +1421,9 @@ int sqlite3Fts5TokenizerPattern(
|
||||
){
|
||||
if( xCreate==fts5TriCreate ){
|
||||
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
|
||||
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
|
||||
if( p->iFoldParam==0 ){
|
||||
return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
|
||||
}
|
||||
}
|
||||
return FTS5_PATTERN_NONE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 2010 June 15
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5faultG
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set ::testprefix fts5faultG
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
INSERT INTO t1 VALUES('test renaming the table');
|
||||
INSERT INTO t1 VALUES(' after it has been written');
|
||||
INSERT INTO t1 VALUES(' actually other stuff instead');
|
||||
}
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 1 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
DELETE FROM t1 WHERE rowid=2;
|
||||
}
|
||||
} -body {
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
}
|
||||
} -test {
|
||||
catchsql { COMMIT }
|
||||
faultsim_integrity_check
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -77,6 +77,9 @@ do_catchsql_test 4.2 {
|
||||
UPDATE aa_docsize SET sz = X'44' WHERE rowid = 3;
|
||||
INSERT INTO aa(aa) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 4.2.1 {
|
||||
PRAGMA integrity_check(aa);
|
||||
} {{malformed inverted index for FTS5 table main.aa}}
|
||||
|
||||
do_catchsql_test 4.3 {
|
||||
ROLLBACK;
|
||||
@@ -317,4 +320,39 @@ do_catchsql_test 10.5.3 {
|
||||
INSERT INTO vt0(vt0) VALUES('integrity-check');
|
||||
} {0 {}}
|
||||
|
||||
reset_db
|
||||
proc slang {in} {return [string map {th d e eh} $in]}
|
||||
db function slang -deterministic -innocuous slang
|
||||
do_execsql_test 11.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT AS (slang(b)));
|
||||
INSERT INTO t1(b) VALUES('the quick fox jumps over the lazy brown dog');
|
||||
SELECT c FROM t1;
|
||||
} {{deh quick fox jumps ovehr deh lazy brown dog}}
|
||||
|
||||
do_execsql_test 11.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(content="t1", c);
|
||||
INSERT INTO t2(t2) VALUES('rebuild');
|
||||
SELECT rowid FROM t2 WHERE t2 MATCH 'deh';
|
||||
} {1}
|
||||
|
||||
do_execsql_test 11.2 {
|
||||
PRAGMA integrity_check(t2);
|
||||
} {ok}
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
# FIX ME?
|
||||
#
|
||||
# FTS5 integrity-check does not care if the content table is unreadable or
|
||||
# does not exist. It only looks for internal inconsistencies in the
|
||||
# inverted index.
|
||||
#
|
||||
do_execsql_test 11.3 {
|
||||
PRAGMA integrity_check(t2);
|
||||
} {ok}
|
||||
do_execsql_test 11.4 {
|
||||
DROP TABLE t1;
|
||||
PRAGMA integrity_check(t2);
|
||||
} {ok}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -424,10 +424,12 @@ do_execsql_test -db db2 15.3 {
|
||||
SAVEPOINT one;
|
||||
} {}
|
||||
do_execsql_test 15.4 END
|
||||
do_test 15.4 {
|
||||
do_test 15.5 {
|
||||
list [catch { db2 eval COMMIT } msg] $msg
|
||||
} {0 {}}
|
||||
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
@@ -469,6 +471,8 @@ do_execsql_test -db db2 16.6 {
|
||||
SELECT * FROM x1
|
||||
} {abc def}
|
||||
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 17.1 {
|
||||
|
||||
@@ -52,6 +52,36 @@ do_execsql_test 2.1 {
|
||||
SELECT * FROM t2('to*');
|
||||
} {top to tommy}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn newrowid} {
|
||||
1 122
|
||||
2 123
|
||||
3 124
|
||||
} {
|
||||
reset_db
|
||||
do_execsql_test 3.$tn.0 {
|
||||
CREATE VIRTUAL TABLE t12 USING fts5(x);
|
||||
INSERT INTO t12(rowid, x) VALUES(123, 'wwww');
|
||||
}
|
||||
do_execsql_test 3.$tn.1 {
|
||||
BEGIN;
|
||||
DELETE FROM t12 WHERE rowid=123;
|
||||
SELECT * FROM t12('wwww*');
|
||||
INSERT INTO t12(rowid, x) VALUES($newrowid, 'wwww');
|
||||
SELECT * FROM t12('wwww*');
|
||||
END;
|
||||
} {wwww}
|
||||
do_execsql_test 3.$tn.2 {
|
||||
INSERT INTO t12(t12) VALUES('integrity-check');
|
||||
}
|
||||
do_execsql_test 3.$tn.3 {
|
||||
SELECT rowid FROM t12('wwww*');
|
||||
} $newrowid
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -71,7 +71,7 @@ ifcapable fts3 {
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
DROP TABLE vt1;
|
||||
} {1 {SQL logic error}}
|
||||
} {0 {}}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
SAVEPOINT x;
|
||||
|
||||
@@ -18,7 +18,7 @@ db progress 1 progress_handler
|
||||
set ::PHC 0
|
||||
proc progress_handler {args} {
|
||||
incr ::PHC
|
||||
if {($::PHC % 100000)==0} breakpoint
|
||||
# if {($::PHC % 100000)==0} breakpoint
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -73,12 +73,12 @@ do_execsql_test 2.2 {
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', $sd)
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
do_execsql_test 3.1 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x)
|
||||
VALUES(51869, 'when whenever where weress what turn'),
|
||||
@@ -91,5 +91,51 @@ do_execsql_test 3.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
INSERT INTO t1(rowid, x) VALUES(10, 'one two');
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
UPDATE t1 SET x = 'one three' WHERE rowid=10;
|
||||
INSERT INTO t1(t1, rank) VALUES('secure-delete', 1);
|
||||
}
|
||||
do_execsql_test 4.2 {
|
||||
DELETE FROM t1 WHERE rowid=10;
|
||||
}
|
||||
do_execsql_test 4.3 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(content);
|
||||
|
||||
INSERT INTO t1(t1,rank) VALUES('secure-delete',1);
|
||||
INSERT INTO t1 VALUES('active'),('boomer'),('atom'),('atomic'),
|
||||
('alpha channel backup abandon test aback boomer atom alpha active');
|
||||
DELETE FROM t1 WHERE t1 MATCH 'abandon';
|
||||
}
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
INSERT INTO t1(t1) VALUES('rebuild');
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
DELETE FROM t1 WHERE rowid NOTNULL<5;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# 2023 Nov 03
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests focusing on the built-in fts5 tokenizers.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5tokenizer2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
sqlite3_fts5_create_tokenizer db tst get_tst_tokenizer
|
||||
proc get_tst_tokenizer {args} {
|
||||
return "tst_tokenizer"
|
||||
}
|
||||
proc tst_tokenizer {flags txt} {
|
||||
set token ""
|
||||
set lTok [list]
|
||||
|
||||
foreach c [split $txt {}] {
|
||||
if {$token==""} {
|
||||
append token $c
|
||||
} else {
|
||||
set t1 [string is upper $token]
|
||||
set t2 [string is upper $c]
|
||||
|
||||
if {$t1!=$t2} {
|
||||
lappend lTok $token
|
||||
set token ""
|
||||
}
|
||||
append token $c
|
||||
}
|
||||
}
|
||||
if {$token!=""} { lappend lTok $token }
|
||||
|
||||
set iOff 0
|
||||
foreach t $lTok {
|
||||
set n [string length $t]
|
||||
sqlite3_fts5_token $t $iOff [expr $iOff+$n]
|
||||
incr iOff $n
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(t, tokenize=tst);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO t1 VALUES('AAdontBBmess');
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT snippet(t1, 0, '>', '<', '...', 4) FROM t1('BB');
|
||||
} {AAdont>BB<mess}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('BB');
|
||||
} {AAdont>BB<mess}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('AA');
|
||||
} {>AA<dontBBmess}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('dont');
|
||||
} {AA>dont<BBmess}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('mess');
|
||||
} {AAdontBB>mess<}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
SELECT highlight(t1, 0, '>', '<') FROM t1('BB mess');
|
||||
} {AAdont>BBmess<}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -215,4 +215,42 @@ do_execsql_test 7.2 {
|
||||
SELECT rowid FROM f WHERE filename GLOB '*ир*';
|
||||
} {20}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize=trigram);
|
||||
INSERT INTO t1 VALUES('abcdefghijklm');
|
||||
}
|
||||
|
||||
foreach {tn match res} {
|
||||
1 "abc ghi" "(abc)def(ghi)jklm"
|
||||
2 "def ghi" "abc(defghi)jklm"
|
||||
3 "efg ghi" "abcd(efghi)jklm"
|
||||
4 "efghi" "abcd(efghi)jklm"
|
||||
5 "abcd jklm" "(abcd)efghi(jklm)"
|
||||
6 "ijkl jklm" "abcdefgh(ijklm)"
|
||||
7 "ijk ijkl hijk" "abcdefg(hijkl)m"
|
||||
|
||||
} {
|
||||
do_execsql_test 8.1.$tn {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1($match)
|
||||
} $res
|
||||
}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(a, tokenize="trigram");
|
||||
INSERT INTO ft2 VALUES('abc x cde');
|
||||
INSERT INTO ft2 VALUES('abc cde');
|
||||
INSERT INTO ft2 VALUES('abcde');
|
||||
}
|
||||
|
||||
do_execsql_test 8.3 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'abc AND cde';
|
||||
} {
|
||||
{[abc] x [cde]}
|
||||
{[abc] [cde]}
|
||||
{[abcde]}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# 2023 October 24
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
# Tests for the fts5 "trigram" tokenizer.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
ifcapable !fts5 { finish_test ; return }
|
||||
set ::testprefix fts5trigram2
|
||||
|
||||
do_execsql_test 1.0 "
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(y, tokenize='trigram remove_diacritics 1');
|
||||
INSERT INTO t1 VALUES('abc\u0303defghijklm');
|
||||
INSERT INTO t1 VALUES('a\u0303b\u0303c\u0303defghijklm');
|
||||
"
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('abc');
|
||||
} [list \
|
||||
"(abc\u0303)defghijklm" \
|
||||
"(a\u0303b\u0303c\u0303)defghijklm" \
|
||||
]
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('bcde');
|
||||
} [list \
|
||||
"a(bc\u0303de)fghijklm" \
|
||||
"a\u0303(b\u0303c\u0303de)fghijklm" \
|
||||
]
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('cdef');
|
||||
} [list \
|
||||
"ab(c\u0303def)ghijklm" \
|
||||
"a\u0303b\u0303(c\u0303def)ghijklm" \
|
||||
]
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT highlight(t1, 0, '(', ')') FROM t1('def');
|
||||
} [list \
|
||||
"abc\u0303(def)ghijklm" \
|
||||
"a\u0303b\u0303c\u0303(def)ghijklm" \
|
||||
]
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_catchsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(
|
||||
z, tokenize='trigram case_sensitive 1 remove_diacritics 1'
|
||||
);
|
||||
} {1 {error in tokenizer constructor}}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(
|
||||
z, tokenize='trigram case_sensitive 0 remove_diacritics 1'
|
||||
);
|
||||
}
|
||||
do_execsql_test 2.2 "
|
||||
INSERT INTO t2 VALUES('\u00E3bcdef');
|
||||
INSERT INTO t2 VALUES('b\u00E3cdef');
|
||||
INSERT INTO t2 VALUES('bc\u00E3def');
|
||||
INSERT INTO t2 VALUES('bcd\u00E3ef');
|
||||
"
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('abc');
|
||||
} "(\u00E3bc)def"
|
||||
do_execsql_test 2.4 {
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('bac');
|
||||
} "(b\u00E3c)def"
|
||||
do_execsql_test 2.5 {
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('bca');
|
||||
} "(bc\u00E3)def"
|
||||
do_execsql_test 2.6 "
|
||||
SELECT highlight(t2, 0, '(', ')') FROM t2('\u00E3bc');
|
||||
" "(\u00E3bc)def"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(
|
||||
z, tokenize='trigram remove_diacritics 1'
|
||||
);
|
||||
} {}
|
||||
do_execsql_test 3.1 "
|
||||
INSERT INTO t3 VALUES ('\u0303abc\u0303');
|
||||
"
|
||||
do_execsql_test 3.2 {
|
||||
SELECT highlight(t3, 0, '(', ')') FROM t3('abc');
|
||||
} "\u0303(abc\u0303)"
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(z, tokenize=trigram);
|
||||
} {}
|
||||
|
||||
breakpoint
|
||||
do_execsql_test 4.1 {
|
||||
INSERT INTO t4 VALUES('ABCD');
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
+52
-29
@@ -26,6 +26,7 @@ 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.capi := $(dir.src.jni)/capi
|
||||
dir.src.fts5 := $(dir.src.jni)/fts5
|
||||
dir.tests := $(dir.src)/tests
|
||||
mkdir ?= mkdir -p
|
||||
@@ -34,6 +35,8 @@ $(dir.bld.c):
|
||||
|
||||
javac.flags ?= -Xlint:unchecked -Xlint:deprecation
|
||||
java.flags ?=
|
||||
javac.flags += -encoding utf8
|
||||
# -------------^^^^^^^^^^^^^^ required for Windows builds
|
||||
jnicheck ?= 1
|
||||
ifeq (1,$(jnicheck))
|
||||
java.flags += -Xcheck:jni
|
||||
@@ -45,9 +48,9 @@ DISTCLEAN_FILES := $(dir.jni)/*~ $(dir.src.c)/*~ $(dir.src.jni)/*~
|
||||
|
||||
sqlite3-jni.h := $(dir.src.c)/sqlite3-jni.h
|
||||
.NOTPARALLEL: $(sqlite3-jni.h)
|
||||
SQLite3Jni.java := src/org/sqlite/jni/SQLite3Jni.java
|
||||
SQLTester.java := src/org/sqlite/jni/SQLTester.java
|
||||
SQLite3Jni.class := $(SQLite3Jni.java:.java=.class)
|
||||
CApi.java := $(dir.src.capi)/CApi.java
|
||||
SQLTester.java := $(dir.src.capi)/SQLTester.java
|
||||
CApi.class := $(CApi.java:.java=.class)
|
||||
SQLTester.class := $(SQLTester.java:.java=.class)
|
||||
|
||||
########################################################################
|
||||
@@ -77,10 +80,11 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile
|
||||
|
||||
# Be explicit about which Java files to compile so that we can work on
|
||||
# in-progress files without requiring them to be in a compilable statae.
|
||||
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
|
||||
annotation/Canonical.java \
|
||||
annotation/NotNull.java \
|
||||
annotation/Nullable.java \
|
||||
JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/annotation/%,\
|
||||
Experimental.java \
|
||||
NotNull.java \
|
||||
Nullable.java \
|
||||
) $(patsubst %,$(dir.src.capi)/%,\
|
||||
AbstractCollationCallback.java \
|
||||
AggregateFunction.java \
|
||||
AuthorizerCallback.java \
|
||||
@@ -90,7 +94,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
|
||||
CollationNeededCallback.java \
|
||||
CommitHookCallback.java \
|
||||
ConfigLogCallback.java \
|
||||
ConfigSqllogCallback.java \
|
||||
ConfigSqlLogCallback.java \
|
||||
NativePointerHolder.java \
|
||||
OutputPointer.java \
|
||||
PrepareMultiCallback.java \
|
||||
@@ -101,22 +105,34 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
|
||||
ScalarFunction.java \
|
||||
SQLFunction.java \
|
||||
CallbackProxy.java \
|
||||
SQLite3Jni.java \
|
||||
CApi.java \
|
||||
TableColumnMetadata.java \
|
||||
TraceV2Callback.java \
|
||||
UpdateHookCallback.java \
|
||||
ValueHolder.java \
|
||||
WindowFunction.java \
|
||||
XDestroyCallback.java \
|
||||
sqlite3.java \
|
||||
sqlite3_blob.java \
|
||||
sqlite3_context.java \
|
||||
sqlite3_stmt.java \
|
||||
sqlite3_value.java \
|
||||
) $(patsubst %,$(dir.src.jni)/wrapper1/%,\
|
||||
AggregateFunction.java \
|
||||
ScalarFunction.java \
|
||||
SqlFunction.java \
|
||||
Sqlite.java \
|
||||
SqliteException.java \
|
||||
ValueHolder.java \
|
||||
WindowFunction.java \
|
||||
)
|
||||
|
||||
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
|
||||
Tester1.java \
|
||||
capi/Tester1.java \
|
||||
wrapper1/Tester2.java \
|
||||
)
|
||||
ifeq (1,$(enable.fts5))
|
||||
JAVA_FILES.unittest += $(patsubst %,$(dir.src.jni)/%,\
|
||||
JAVA_FILES.unittest += $(patsubst %,$(dir.src.fts5)/%,\
|
||||
TesterFts5.java \
|
||||
)
|
||||
JAVA_FILES.main += $(patsubst %,$(dir.src.fts5)/%,\
|
||||
@@ -148,12 +164,13 @@ endif
|
||||
CLASS_FILES :=
|
||||
define CLASSFILE_DEPS
|
||||
all: $(1).class
|
||||
$(1).class: $(1).java
|
||||
CLASS_FILES += $(1).class
|
||||
endef
|
||||
$(foreach B,$(basename \
|
||||
$(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.tester)),\
|
||||
$(eval $(call CLASSFILE_DEPS,$(B))))
|
||||
$(CLASS_FILES): $(JAVA_FILES) $(MAKEFILE)
|
||||
$(CLASS_FILES): $(MAKEFILE)
|
||||
$(bin.javac) $(javac.flags) -h $(dir.bld.c) -cp $(classpath) $(JAVA_FILES)
|
||||
|
||||
#.PHONY: classfiles
|
||||
@@ -204,7 +221,7 @@ SQLITE_OPT = \
|
||||
-DSQLITE_JNI_FATAL_OOM=$(opt.fatal-oom) \
|
||||
-DSQLITE_JNI_ENABLE_METRICS=$(opt.metrics)
|
||||
|
||||
opt.extras ?= 0
|
||||
opt.extras ?= 1
|
||||
ifeq (1,$(opt.extras))
|
||||
SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
@@ -215,7 +232,8 @@ SQLITE_OPT += -DSQLITE_ENABLE_RTREE \
|
||||
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
|
||||
-DSQLITE_ENABLE_PREUPDATE_HOOK \
|
||||
-DSQLITE_ENABLE_NORMALIZE \
|
||||
-DSQLITE_ENABLE_SQLLOG
|
||||
-DSQLITE_ENABLE_SQLLOG \
|
||||
-DSQLITE_ENABLE_COLUMN_METADATA
|
||||
endif
|
||||
|
||||
ifeq (1,$(opt.debug))
|
||||
@@ -243,8 +261,8 @@ $$(dir.bld.c)/org_sqlite_jni$(3)_$(2).h: $(1)/$(2).java
|
||||
endef
|
||||
# Invoke ADD_JNI_H once for each Java file which includes JNI
|
||||
# declarations:
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.jni),SQLite3Jni,))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.jni),SQLTester,))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.capi),CApi,_capi))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.capi),SQLTester,_capi))
|
||||
ifeq (1,$(enable.fts5))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.fts5),Fts5ExtensionApi,_fts5))
|
||||
$(eval $(call ADD_JNI_H,$(dir.src.fts5),fts5_api,_fts5))
|
||||
@@ -295,19 +313,23 @@ $(package.dll): $(sqlite3-jni.c) $(MAKEFILE)
|
||||
all: $(package.dll)
|
||||
|
||||
.PHONY: test test-one
|
||||
test.flags ?=
|
||||
test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.Tester1
|
||||
Tester1.flags ?=
|
||||
Tester2.flags ?=
|
||||
test.flags.jvm = -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath)
|
||||
test.deps := $(CLASS_FILES) $(package.dll)
|
||||
test-one: $(test.deps)
|
||||
$(bin.java) $(test.main.flags) $(test.flags)
|
||||
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags)
|
||||
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 $(Tester2.flags)
|
||||
test-sqllog: $(test.deps)
|
||||
@echo "Testing with -sqllog..."
|
||||
$(bin.java) $(test.main.flags) -sqllog
|
||||
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 $(Tester1.flags) -sqllog
|
||||
test-mt: $(test.deps)
|
||||
@echo "Testing in multi-threaded mode:";
|
||||
$(bin.java) $(test.main.flags) -t 7 -r 50 -shuffle $(test.flags)
|
||||
$(bin.java) $(test.flags.jvm) org.sqlite.jni.capi.Tester1 \
|
||||
-t 7 -r 50 -shuffle $(Tester1.flags)
|
||||
$(bin.java) $(test.flags.jvm) org.sqlite.jni.wrapper1.Tester2 \
|
||||
-t 7 -r 50 -shuffle $(Tester2.flags)
|
||||
|
||||
test: test-one test-mt
|
||||
tests: test test-sqllog
|
||||
@@ -319,7 +341,7 @@ ifeq (1,$(enable.tester))
|
||||
tester-local: $(CLASS_FILES.tester) $(package.dll)
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.SQLTester $(tester.flags) $(tester.scripts)
|
||||
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.scripts)
|
||||
tester: tester-local
|
||||
else
|
||||
tester:
|
||||
@@ -333,7 +355,7 @@ ifneq (,$(tester.extern-scripts))
|
||||
tester-ext:
|
||||
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
|
||||
$(java.flags) -cp $(classpath) \
|
||||
org.sqlite.jni.SQLTester $(tester.flags) $(tester.extern-scripts)
|
||||
org.sqlite.jni.capi.SQLTester $(tester.flags) $(tester.extern-scripts)
|
||||
else
|
||||
tester-ext:
|
||||
@echo "******************************************************"; \
|
||||
@@ -373,13 +395,12 @@ JAVA_FILES.jar := $(JAVA_FILES.main) $(JAVA_FILES.unittest) $(JAVA_FILES.package
|
||||
CLASS_FILES.jar := $(filter-out %/package-info.class,$(JAVA_FILES.jar:.java=.class))
|
||||
$(package.jar.in): $(package.dll) $(MAKEFILE)
|
||||
ls -1 \
|
||||
$(dir.src.jni)/*.java $(dir.src.jni)/*.class \
|
||||
$(dir.src.jni)/annotation/*.java $(dir.src.jni)/annotation/*.class \
|
||||
$(dir.src.jni)/*/*.java $(dir.src.jni)/*/*.class \
|
||||
| sed -e 's,^$(dir.src)/,,' | sort > $@
|
||||
|
||||
$(package.jar): $(CLASS_FILES.jar) $(MAKEFILE) $(package.jar.in)
|
||||
@rm -f $(dir.src)/c/*~ $(dir.src.jni)/*~
|
||||
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.Tester1 @$(package.jar.in)
|
||||
cd $(dir.src); $(bin.jar) -cfe ../$@ org.sqlite.jni.capi.Tester1 @$(package.jar.in)
|
||||
@ls -la $@
|
||||
@echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag."
|
||||
@echo "e.g. java -Djava.library.path=bld -jar $@"
|
||||
@@ -396,6 +417,8 @@ javadoc.exclude := -exclude org.sqlite.jni.fts5
|
||||
# ^^^^ 2023-09-13: elide the fts5 parts from the public docs for
|
||||
# the time being, as it's not clear where the Java bindings for
|
||||
# those bits are going.
|
||||
# javadoc.exclude += -exclude org.sqlite.jni.capi
|
||||
# ^^^^ exclude the capi API only for certain builds (TBD)
|
||||
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
|
||||
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
|
||||
$(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
|
||||
@@ -416,7 +439,7 @@ docserve: $(doc.index)
|
||||
# Clean up...
|
||||
CLEAN_FILES += $(dir.bld.c)/* \
|
||||
$(dir.src.jni)/*.class \
|
||||
$(dir.src.jni.tester)/*.class \
|
||||
$(dir.src.jni)/*/*.class \
|
||||
$(package.dll) \
|
||||
hs_err_pid*.log
|
||||
|
||||
|
||||
+23
-20
@@ -16,9 +16,8 @@ Technical support is available in the 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.
|
||||
bindings released with version 3.43 are a "tech preview." Once
|
||||
finalized, strong backward compatibility guarantees will apply.
|
||||
|
||||
Project goals/requirements:
|
||||
|
||||
@@ -43,11 +42,13 @@ Non-goals:
|
||||
- Creation of high-level OO wrapper APIs. Clients are free to create
|
||||
them off of the C-style API.
|
||||
|
||||
- Virtual tables are unlikely to be supported due to the amount of
|
||||
glue code needed to fit them into Java.
|
||||
|
||||
- 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.
|
||||
code. Such cases would be a minefield of potential mis-interactions
|
||||
between this project's JNI bindings and mixed-mode client code.
|
||||
|
||||
|
||||
Hello World
|
||||
@@ -55,7 +56,7 @@ Hello World
|
||||
|
||||
```java
|
||||
import org.sqlite.jni.*;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
import static org.sqlite.jni.CApi.*;
|
||||
|
||||
...
|
||||
|
||||
@@ -123,15 +124,13 @@ 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
|
||||
In a 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
|
||||
@@ -150,6 +149,9 @@ 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.
|
||||
|
||||
Classes for which it is sensible support Java's `AutoCloseable`
|
||||
interface so can be used with try-with-resources constructs.
|
||||
|
||||
|
||||
Golden Rule #2: _Never_ Throw from Callbacks (Unless...)
|
||||
------------------------------------------------------------------------
|
||||
@@ -159,14 +161,15 @@ 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`.
|
||||
may cause a `NullPointerException`. The APIs clearly mark function
|
||||
parameters which should not be null, but does not generally 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
|
||||
explitly 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
|
||||
@@ -292,14 +295,14 @@ int sqlite3_create_function(sqlite3 db, String funcName, int nArgs,
|
||||
`SQLFunction` is not used directly, but is instead instantiated via
|
||||
one of its three subclasses:
|
||||
|
||||
- `SQLFunction.Scalar` implements simple scalar functions using but a
|
||||
- `ScalarFunction` implements simple scalar functions using but a
|
||||
single callback.
|
||||
- `SQLFunction.Aggregate` implements aggregate functions using two
|
||||
- `AggregateFunction` implements aggregate functions using two
|
||||
callbacks.
|
||||
- `SQLFunction.Window` implements window functions using four
|
||||
- `WindowFunction` implements window functions using four
|
||||
callbacks.
|
||||
|
||||
Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/Tester1.java) for
|
||||
Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/capi/Tester1.java) for
|
||||
`SQLFunction` for how it's used.
|
||||
|
||||
Reminder: see the disclaimer at the top of this document regarding the
|
||||
|
||||
+902
-304
File diff suppressed because it is too large
Load Diff
+2019
-1933
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;
|
||||
|
||||
|
||||
/**
|
||||
A SQLFunction implementation for aggregate functions. Its T is the
|
||||
data type of its "accumulator" state, an instance of which is
|
||||
intended to be be managed using the getAggregateState() and
|
||||
takeAggregateState() methods.
|
||||
*/
|
||||
public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
|
||||
/**
|
||||
As for the xStep() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, the
|
||||
exception is not propagated and a warning might be emitted to a
|
||||
debugging channel.
|
||||
*/
|
||||
public abstract void xStep(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
/**
|
||||
As for the xFinal() argument of the C API's sqlite3_create_function().
|
||||
If this function throws, it is translated into an sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xFinal(sqlite3_context cx);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final SQLFunction.PerContextState<T> map =
|
||||
new SQLFunction.PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
as the xValue() and xInverse() methods of the {@link WindowFunction}
|
||||
subclass, to fetch the current per-call UDF state. On the
|
||||
first call to this method for any given sqlite3_context
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see SQLFunction.PerContextState#getAggregateState
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
return map.getAggregateState(cx, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
To be called from the implementation's xFinal() method to fetch
|
||||
the final state of the UDF and remove its mapping.
|
||||
|
||||
see SQLFunction.PerContextState#takeAggregateState
|
||||
*/
|
||||
protected final T takeAggregateState(sqlite3_context cx){
|
||||
return map.takeAggregateState(cx);
|
||||
}
|
||||
}
|
||||
@@ -1,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,48 +0,0 @@
|
||||
/*
|
||||
** 2023-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the Canonical annotaion for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
|
||||
/**
|
||||
This annotation is for marking functions as "canonical", meaning
|
||||
that they map directly to a function in the core sqlite3 C API. The
|
||||
intent is to distinguish them from functions added specifically to
|
||||
the Java API.
|
||||
|
||||
<p>Canonical functions, unless specifically documented, have the
|
||||
same semantics as their counterparts in <a
|
||||
href="https://sqlite.org/c3ref/intro.html">the C API
|
||||
documentation</a>, despite their signatures perhaps differing
|
||||
slightly. Canonical forms may be native or implemented in Java.
|
||||
Sometimes multiple overloads are labeled as Canonical because one
|
||||
or more of them are just type- or encoding-related conversion
|
||||
wrappers but provide identical semantics (e.g. from a String to a
|
||||
byte[]). The Java API adds a number of convenience overloads to
|
||||
simplify use, as well as a few Java-specific functions, and those
|
||||
are never flagged as @Canonical.
|
||||
|
||||
<p>In some cases, the canonical version of a function is private
|
||||
and exposed to Java via public overloads.
|
||||
|
||||
<p>The comment property can be used to add a comment.
|
||||
*/
|
||||
@java.lang.annotation.Documented
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD)
|
||||
public @interface Canonical{
|
||||
/**
|
||||
Brief comments about the binding, e.g. noting any major
|
||||
semantic differences.
|
||||
*/
|
||||
String comment() default "";
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
** 2023-09-27
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the Experimental annotation for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
This annotation is for flagging methods, constructors, and types
|
||||
which are expressly experimental and subject to any amount of
|
||||
change or outright removal. Client code should not rely on such
|
||||
features.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.TYPE
|
||||
})
|
||||
public @interface Experimental{}
|
||||
@@ -9,21 +9,22 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the NotNull annotaion for the sqlite3 C API.
|
||||
** This file houses the NotNull annotation for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
This annotation is for flagging parameters which may not legally be
|
||||
null or point to closed/finalized C-side resources.
|
||||
|
||||
<p>In the case of Java types which map directly to C struct types
|
||||
(e.g. {@link org.sqlite.jni.sqlite3}, {@link
|
||||
org.sqlite.jni.sqlite3_stmt}, and {@link
|
||||
org.sqlite.jni.sqlite3_context}), a closed/finalized resource is
|
||||
also considered to be null for purposes this annotation because the
|
||||
C-side effect of passing such a handle is the same as if null is
|
||||
passed.</p>
|
||||
(e.g. {@link org.sqlite.jni.capi.sqlite3}, {@link
|
||||
org.sqlite.jni.capi.sqlite3_stmt}, and {@link
|
||||
org.sqlite.jni.capi.sqlite3_context}), a closed/finalized resource
|
||||
is also considered to be null for purposes this annotation because
|
||||
the C-side effect of passing such a handle is the same as if null
|
||||
is passed.</p>
|
||||
|
||||
<p>When used in the context of Java interfaces which are called
|
||||
from the C APIs, this annotation communicates that the C API will
|
||||
@@ -31,12 +32,23 @@ package org.sqlite.jni.annotation;
|
||||
|
||||
<p>Passing a null, for this annotation's definition of null, for
|
||||
any parameter marked with this annoation specifically invokes
|
||||
undefined behavior.</p>
|
||||
undefined behavior (see below).</p>
|
||||
|
||||
<p>Passing 0 (i.e. C NULL) or a negative value for any long-type
|
||||
parameter marked with this annoation specifically invokes undefined
|
||||
behavior. Such values are treated as C pointers in the JNI
|
||||
layer.</p>
|
||||
behavior (see below). Such values are treated as C pointers in the
|
||||
JNI layer.</p>
|
||||
|
||||
<p><b>Undefined behaviour:</b> the JNI build uses the {@code
|
||||
SQLITE_ENABLE_API_ARMOR} build flag, meaning that the C code
|
||||
invoked with invalid NULL pointers and the like will not invoke
|
||||
undefined behavior in the conventional C sense, but may, for
|
||||
example, return result codes which are not documented for the
|
||||
affected APIs or may otherwise behave unpredictably. In no known
|
||||
cases will such arguments result in C-level code dereferencing a
|
||||
NULL pointer or accessing out-of-bounds (or otherwise invalid)
|
||||
memory. In other words, they may cause unexpected behavior but
|
||||
should never cause an outright crash or security issue.</p>
|
||||
|
||||
<p>Note that the C-style API does not throw any exceptions on its
|
||||
own because it has a no-throw policy in order to retain its C-style
|
||||
@@ -48,12 +60,12 @@ package org.sqlite.jni.annotation;
|
||||
code.</p>
|
||||
|
||||
<p>This annotation is solely for the use by the classes in the
|
||||
org.sqlite package and subpackages, but is made public so that
|
||||
org.sqlite.jni package and subpackages, but is made public so that
|
||||
javadoc will link to it from the annotated functions. It is not
|
||||
part of the public API and client-level code must not rely on
|
||||
it.</p>
|
||||
*/
|
||||
@java.lang.annotation.Documented
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
|
||||
@java.lang.annotation.Target(java.lang.annotation.ElementType.PARAMETER)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface NotNull{}
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This file houses the Nullable annotaion for the sqlite3 C API.
|
||||
** This file houses the Nullable annotation for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.annotation;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
This annotation is for flagging parameters which may legally be
|
||||
@@ -26,7 +27,7 @@ package org.sqlite.jni.annotation;
|
||||
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)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.PARAMETER)
|
||||
public @interface Nullable{}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
import org.sqlite.jni.annotation.NotNull;
|
||||
|
||||
/**
|
||||
+54
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
** 2023-07-22
|
||||
** 2023-08-25
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
@@ -11,27 +11,36 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
|
||||
/**
|
||||
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.
|
||||
A SQLFunction implementation for aggregate functions. Its T is the
|
||||
data type of its "accumulator" state, an instance of which is
|
||||
intended to be be managed using the getAggregateState() and
|
||||
takeAggregateState() methods.
|
||||
*/
|
||||
public interface SQLFunction {
|
||||
public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
|
||||
/**
|
||||
As for the xStep() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, the
|
||||
exception is not propagated and a warning might be emitted to a
|
||||
debugging channel.
|
||||
*/
|
||||
public abstract void xStep(sqlite3_context cx, sqlite3_value[] args);
|
||||
|
||||
/**
|
||||
As for the xFinal() argument of the C API's sqlite3_create_function().
|
||||
If this function throws, it is translated into an sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xFinal(sqlite3_context cx);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
@@ -100,4 +109,30 @@ public interface SQLFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
as the xValue() and xInverse() methods of the {@link WindowFunction}
|
||||
subclass, to fetch the current per-call UDF state. On the
|
||||
first call to this method for any given sqlite3_context
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see SQLFunction.PerContextState#getAggregateState
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
return map.getAggregateState(cx, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
To be called from the implementation's xFinal() method to fetch
|
||||
the final state of the UDF and remove its mapping.
|
||||
|
||||
see SQLFunction.PerContextState#takeAggregateState
|
||||
*/
|
||||
protected final T takeAggregateState(sqlite3_context cx){
|
||||
return map.takeAggregateState(cx);
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -11,16 +11,17 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_set_authorizer}.
|
||||
Callback for use with {@link CApi#sqlite3_set_authorizer}.
|
||||
*/
|
||||
public interface AuthorizerCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level
|
||||
sqlite3_set_authorizer() callback.
|
||||
sqlite3_set_authorizer() callback. If it throws, the error is
|
||||
converted to a db-level error and the exception is suppressed.
|
||||
*/
|
||||
int call(int opId, @Nullable String s1, @Nullable String s2,
|
||||
@Nullable String s3, @Nullable String s4);
|
||||
+2
-2
@@ -11,10 +11,10 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with the {@link SQLite3Jni#sqlite3_auto_extension}
|
||||
Callback for use with the {@link CApi#sqlite3_auto_extension}
|
||||
family of APIs.
|
||||
*/
|
||||
public interface AutoExtensionCallback extends CallbackProxy {
|
||||
+2
-2
@@ -11,10 +11,10 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_busy_handler}.
|
||||
Callback for use with {@link CApi#sqlite3_busy_handler}.
|
||||
*/
|
||||
public interface BusyHandlerCallback extends CallbackProxy {
|
||||
/**
|
||||
+685
-464
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
/**
|
||||
This marker interface exists soley for use as a documentation and
|
||||
class-grouping tool. It should be applied to interfaces or
|
||||
@@ -24,8 +24,9 @@ package org.sqlite.jni;
|
||||
propagated. For callback interfaces which support returning error
|
||||
info to the core, the JNI binding will convert any exceptions to
|
||||
C-level error information. For callback interfaces which do not
|
||||
support, all exceptions will necessarily be suppressed in order to
|
||||
retain the C-style no-throw semantics.
|
||||
support returning error information, all exceptions will
|
||||
necessarily be suppressed in order to retain the C-style no-throw
|
||||
semantics and avoid invoking undefined behavior in the C layer.
|
||||
|
||||
<p>Callbacks of this style follow a common naming convention:
|
||||
|
||||
+2
-2
@@ -11,11 +11,11 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
import org.sqlite.jni.annotation.NotNull;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_create_collation}.
|
||||
Callback for use with {@link CApi#sqlite3_create_collation}.
|
||||
|
||||
@see AbstractCollationCallback
|
||||
*/
|
||||
+6
-5
@@ -11,18 +11,19 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_collation_needed}.
|
||||
Callback for use with {@link CApi#sqlite3_collation_needed}.
|
||||
*/
|
||||
public interface CollationNeededCallback extends CallbackProxy {
|
||||
/**
|
||||
Has the same semantics as the C-level sqlite3_create_collation()
|
||||
callback.
|
||||
|
||||
<p>If it throws, the exception message is passed on to the db and
|
||||
the exception is suppressed.
|
||||
<p>Because the C API has no mechanism for reporting errors
|
||||
from this callbacks, any exceptions thrown by this callback
|
||||
are suppressed.
|
||||
*/
|
||||
int call(sqlite3 db, int eTextRep, String collationName);
|
||||
void call(sqlite3 db, int eTextRep, String collationName);
|
||||
}
|
||||
+4
-3
@@ -11,15 +11,16 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_commit_hook}.
|
||||
Callback for use with {@link CApi#sqlite3_commit_hook}.
|
||||
*/
|
||||
public interface CommitHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_commit_hook()
|
||||
callback. Must not throw.
|
||||
callback. If it throws, the exception is translated into
|
||||
a db-level error.
|
||||
*/
|
||||
int call();
|
||||
}
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_config().
|
||||
@@ -19,7 +19,7 @@ package org.sqlite.jni;
|
||||
public interface ConfigLogCallback {
|
||||
/**
|
||||
Must function as described for a C-level callback for
|
||||
{@link SQLite3Jni#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
|
||||
{@link CApi#sqlite3_config(ConfigLogCallback)}, with the slight signature change.
|
||||
*/
|
||||
void call(int errCode, String msg);
|
||||
}
|
||||
+3
-3
@@ -11,15 +11,15 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_config().
|
||||
*/
|
||||
public interface ConfigSqllogCallback {
|
||||
public interface ConfigSqlLogCallback {
|
||||
/**
|
||||
Must function as described for a C-level callback for
|
||||
{@link SQLite3Jni#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change.
|
||||
{@link CApi#sqlite3_config(ConfigSqlLogCallback)}, with the slight signature change.
|
||||
*/
|
||||
void call(sqlite3 db, String msg, int msgType );
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A helper for passing pointers between JNI C code and Java, in
|
||||
+39
-17
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Helper classes for handling JNI output pointers.
|
||||
@@ -49,16 +49,16 @@ public final class OutputPointer {
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3 {
|
||||
private org.sqlite.jni.sqlite3 value;
|
||||
private org.sqlite.jni.capi.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;}
|
||||
public final org.sqlite.jni.capi.sqlite3 get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.sqlite3 take(){
|
||||
final org.sqlite.jni.sqlite3 v = value;
|
||||
public final org.sqlite.jni.capi.sqlite3 take(){
|
||||
final org.sqlite.jni.capi.sqlite3 v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
@@ -70,16 +70,16 @@ public final class OutputPointer {
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_blob {
|
||||
private org.sqlite.jni.sqlite3_blob value;
|
||||
private org.sqlite.jni.capi.sqlite3_blob value;
|
||||
/** Initializes with a null value. */
|
||||
public sqlite3_blob(){value = null;}
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.sqlite3_blob get(){return value;}
|
||||
public final org.sqlite.jni.capi.sqlite3_blob get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.sqlite3_blob take(){
|
||||
final org.sqlite.jni.sqlite3_blob v = value;
|
||||
public final org.sqlite.jni.capi.sqlite3_blob take(){
|
||||
final org.sqlite.jni.capi.sqlite3_blob v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
@@ -92,16 +92,16 @@ public final class OutputPointer {
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_stmt {
|
||||
private org.sqlite.jni.sqlite3_stmt value;
|
||||
private org.sqlite.jni.capi.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;}
|
||||
public final org.sqlite.jni.capi.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;
|
||||
public final org.sqlite.jni.capi.sqlite3_stmt take(){
|
||||
final org.sqlite.jni.capi.sqlite3_stmt v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
@@ -114,16 +114,16 @@ public final class OutputPointer {
|
||||
code.
|
||||
*/
|
||||
public static final class sqlite3_value {
|
||||
private org.sqlite.jni.sqlite3_value value;
|
||||
private org.sqlite.jni.capi.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;}
|
||||
public final org.sqlite.jni.capi.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;
|
||||
public final org.sqlite.jni.capi.sqlite3_value take(){
|
||||
final org.sqlite.jni.capi.sqlite3_value v = value;
|
||||
value = null;
|
||||
return v;
|
||||
}
|
||||
@@ -228,4 +228,26 @@ public final class OutputPointer {
|
||||
/** Sets the current value. */
|
||||
public final void set(byte[] v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
Output pointer for use with native routines which return
|
||||
blobs via java.nio.ByteBuffer.
|
||||
|
||||
See {@link org.sqlite.jni.capi.CApi#sqlite3_jni_supports_nio}
|
||||
*/
|
||||
public static final class ByteBuffer {
|
||||
/**
|
||||
This is public for ease of use. Accessors are provided for
|
||||
consistency with the higher-level types.
|
||||
*/
|
||||
public java.nio.ByteBuffer value;
|
||||
/** Initializes with the value null. */
|
||||
public ByteBuffer(){this(null);}
|
||||
/** Initializes with the value v. */
|
||||
public ByteBuffer(java.nio.ByteBuffer v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final java.nio.ByteBuffer get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(java.nio.ByteBuffer v){value = v;}
|
||||
}
|
||||
}
|
||||
+11
-8
@@ -11,10 +11,10 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_prepare_multi}.
|
||||
Callback for use with {@link CApi#sqlite3_prepare_multi}.
|
||||
*/
|
||||
public interface PrepareMultiCallback extends CallbackProxy {
|
||||
|
||||
@@ -25,7 +25,10 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
sqlite3_prepare_multi() will _not_ finalize st - it is up
|
||||
to the call() implementation how st is handled.
|
||||
|
||||
Must return 0 on success or an SQLITE_... code on error.
|
||||
Must return 0 on success or an SQLITE_... code on error. If it
|
||||
throws, sqlite3_prepare_multi() will transform the exception into
|
||||
a db-level error in order to retain the C-style error semantics
|
||||
of the API.
|
||||
|
||||
See the {@link Finalize} class for a wrapper which finalizes the
|
||||
statement after calling a proxy PrepareMultiCallback.
|
||||
@@ -37,7 +40,7 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
any sqlite3_stmt passed to its callback.
|
||||
*/
|
||||
public static final class Finalize implements PrepareMultiCallback {
|
||||
private PrepareMultiCallback p;
|
||||
private final PrepareMultiCallback p;
|
||||
/**
|
||||
p is the proxy to call() when this.call() is called.
|
||||
*/
|
||||
@@ -53,7 +56,7 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
try {
|
||||
return this.p.call(st);
|
||||
}finally{
|
||||
SQLite3Jni.sqlite3_finalize(st);
|
||||
CApi.sqlite3_finalize(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,9 +73,9 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
else the result of the final step is returned.
|
||||
*/
|
||||
@Override public int call(sqlite3_stmt st){
|
||||
int rc = SQLite3Jni.SQLITE_DONE;
|
||||
while( SQLite3Jni.SQLITE_ROW == (rc = SQLite3Jni.sqlite3_step(st)) ){}
|
||||
return SQLite3Jni.SQLITE_DONE==rc ? 0 : rc;
|
||||
int rc = CApi.SQLITE_DONE;
|
||||
while( CApi.SQLITE_ROW == (rc = CApi.sqlite3_step(st)) ){}
|
||||
return CApi.SQLITE_DONE==rc ? 0 : rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -11,15 +11,16 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_preupdate_hook}.
|
||||
Callback for use with {@link CApi#sqlite3_preupdate_hook}.
|
||||
*/
|
||||
public interface PreupdateHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level sqlite3_preupdate_hook()
|
||||
callback.
|
||||
callback. If it throws, the exception is translated to a
|
||||
db-level error and the exception is suppressed.
|
||||
*/
|
||||
void call(sqlite3 db, int op, String dbName, String dbTable,
|
||||
long iKey1, long iKey2 );
|
||||
+2
-2
@@ -11,10 +11,10 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_progress_handler}.
|
||||
Callback for use with {@link CApi#sqlite3_progress_handler}.
|
||||
*/
|
||||
public interface ProgressHandlerCallback extends CallbackProxy {
|
||||
/**
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
** 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.capi;
|
||||
|
||||
/**
|
||||
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(CApi.SQLITE_OK),
|
||||
SQLITE_ERROR(CApi.SQLITE_ERROR),
|
||||
SQLITE_INTERNAL(CApi.SQLITE_INTERNAL),
|
||||
SQLITE_PERM(CApi.SQLITE_PERM),
|
||||
SQLITE_ABORT(CApi.SQLITE_ABORT),
|
||||
SQLITE_BUSY(CApi.SQLITE_BUSY),
|
||||
SQLITE_LOCKED(CApi.SQLITE_LOCKED),
|
||||
SQLITE_NOMEM(CApi.SQLITE_NOMEM),
|
||||
SQLITE_READONLY(CApi.SQLITE_READONLY),
|
||||
SQLITE_INTERRUPT(CApi.SQLITE_INTERRUPT),
|
||||
SQLITE_IOERR(CApi.SQLITE_IOERR),
|
||||
SQLITE_CORRUPT(CApi.SQLITE_CORRUPT),
|
||||
SQLITE_NOTFOUND(CApi.SQLITE_NOTFOUND),
|
||||
SQLITE_FULL(CApi.SQLITE_FULL),
|
||||
SQLITE_CANTOPEN(CApi.SQLITE_CANTOPEN),
|
||||
SQLITE_PROTOCOL(CApi.SQLITE_PROTOCOL),
|
||||
SQLITE_EMPTY(CApi.SQLITE_EMPTY),
|
||||
SQLITE_SCHEMA(CApi.SQLITE_SCHEMA),
|
||||
SQLITE_TOOBIG(CApi.SQLITE_TOOBIG),
|
||||
SQLITE_CONSTRAINT(CApi.SQLITE_CONSTRAINT),
|
||||
SQLITE_MISMATCH(CApi.SQLITE_MISMATCH),
|
||||
SQLITE_MISUSE(CApi.SQLITE_MISUSE),
|
||||
SQLITE_NOLFS(CApi.SQLITE_NOLFS),
|
||||
SQLITE_AUTH(CApi.SQLITE_AUTH),
|
||||
SQLITE_FORMAT(CApi.SQLITE_FORMAT),
|
||||
SQLITE_RANGE(CApi.SQLITE_RANGE),
|
||||
SQLITE_NOTADB(CApi.SQLITE_NOTADB),
|
||||
SQLITE_NOTICE(CApi.SQLITE_NOTICE),
|
||||
SQLITE_WARNING(CApi.SQLITE_WARNING),
|
||||
SQLITE_ROW(CApi.SQLITE_ROW),
|
||||
SQLITE_DONE(CApi.SQLITE_DONE),
|
||||
SQLITE_ERROR_MISSING_COLLSEQ(CApi.SQLITE_ERROR_MISSING_COLLSEQ),
|
||||
SQLITE_ERROR_RETRY(CApi.SQLITE_ERROR_RETRY),
|
||||
SQLITE_ERROR_SNAPSHOT(CApi.SQLITE_ERROR_SNAPSHOT),
|
||||
SQLITE_IOERR_READ(CApi.SQLITE_IOERR_READ),
|
||||
SQLITE_IOERR_SHORT_READ(CApi.SQLITE_IOERR_SHORT_READ),
|
||||
SQLITE_IOERR_WRITE(CApi.SQLITE_IOERR_WRITE),
|
||||
SQLITE_IOERR_FSYNC(CApi.SQLITE_IOERR_FSYNC),
|
||||
SQLITE_IOERR_DIR_FSYNC(CApi.SQLITE_IOERR_DIR_FSYNC),
|
||||
SQLITE_IOERR_TRUNCATE(CApi.SQLITE_IOERR_TRUNCATE),
|
||||
SQLITE_IOERR_FSTAT(CApi.SQLITE_IOERR_FSTAT),
|
||||
SQLITE_IOERR_UNLOCK(CApi.SQLITE_IOERR_UNLOCK),
|
||||
SQLITE_IOERR_RDLOCK(CApi.SQLITE_IOERR_RDLOCK),
|
||||
SQLITE_IOERR_DELETE(CApi.SQLITE_IOERR_DELETE),
|
||||
SQLITE_IOERR_BLOCKED(CApi.SQLITE_IOERR_BLOCKED),
|
||||
SQLITE_IOERR_NOMEM(CApi.SQLITE_IOERR_NOMEM),
|
||||
SQLITE_IOERR_ACCESS(CApi.SQLITE_IOERR_ACCESS),
|
||||
SQLITE_IOERR_CHECKRESERVEDLOCK(CApi.SQLITE_IOERR_CHECKRESERVEDLOCK),
|
||||
SQLITE_IOERR_LOCK(CApi.SQLITE_IOERR_LOCK),
|
||||
SQLITE_IOERR_CLOSE(CApi.SQLITE_IOERR_CLOSE),
|
||||
SQLITE_IOERR_DIR_CLOSE(CApi.SQLITE_IOERR_DIR_CLOSE),
|
||||
SQLITE_IOERR_SHMOPEN(CApi.SQLITE_IOERR_SHMOPEN),
|
||||
SQLITE_IOERR_SHMSIZE(CApi.SQLITE_IOERR_SHMSIZE),
|
||||
SQLITE_IOERR_SHMLOCK(CApi.SQLITE_IOERR_SHMLOCK),
|
||||
SQLITE_IOERR_SHMMAP(CApi.SQLITE_IOERR_SHMMAP),
|
||||
SQLITE_IOERR_SEEK(CApi.SQLITE_IOERR_SEEK),
|
||||
SQLITE_IOERR_DELETE_NOENT(CApi.SQLITE_IOERR_DELETE_NOENT),
|
||||
SQLITE_IOERR_MMAP(CApi.SQLITE_IOERR_MMAP),
|
||||
SQLITE_IOERR_GETTEMPPATH(CApi.SQLITE_IOERR_GETTEMPPATH),
|
||||
SQLITE_IOERR_CONVPATH(CApi.SQLITE_IOERR_CONVPATH),
|
||||
SQLITE_IOERR_VNODE(CApi.SQLITE_IOERR_VNODE),
|
||||
SQLITE_IOERR_AUTH(CApi.SQLITE_IOERR_AUTH),
|
||||
SQLITE_IOERR_BEGIN_ATOMIC(CApi.SQLITE_IOERR_BEGIN_ATOMIC),
|
||||
SQLITE_IOERR_COMMIT_ATOMIC(CApi.SQLITE_IOERR_COMMIT_ATOMIC),
|
||||
SQLITE_IOERR_ROLLBACK_ATOMIC(CApi.SQLITE_IOERR_ROLLBACK_ATOMIC),
|
||||
SQLITE_IOERR_DATA(CApi.SQLITE_IOERR_DATA),
|
||||
SQLITE_IOERR_CORRUPTFS(CApi.SQLITE_IOERR_CORRUPTFS),
|
||||
SQLITE_LOCKED_SHAREDCACHE(CApi.SQLITE_LOCKED_SHAREDCACHE),
|
||||
SQLITE_LOCKED_VTAB(CApi.SQLITE_LOCKED_VTAB),
|
||||
SQLITE_BUSY_RECOVERY(CApi.SQLITE_BUSY_RECOVERY),
|
||||
SQLITE_BUSY_SNAPSHOT(CApi.SQLITE_BUSY_SNAPSHOT),
|
||||
SQLITE_BUSY_TIMEOUT(CApi.SQLITE_BUSY_TIMEOUT),
|
||||
SQLITE_CANTOPEN_NOTEMPDIR(CApi.SQLITE_CANTOPEN_NOTEMPDIR),
|
||||
SQLITE_CANTOPEN_ISDIR(CApi.SQLITE_CANTOPEN_ISDIR),
|
||||
SQLITE_CANTOPEN_FULLPATH(CApi.SQLITE_CANTOPEN_FULLPATH),
|
||||
SQLITE_CANTOPEN_CONVPATH(CApi.SQLITE_CANTOPEN_CONVPATH),
|
||||
SQLITE_CANTOPEN_SYMLINK(CApi.SQLITE_CANTOPEN_SYMLINK),
|
||||
SQLITE_CORRUPT_VTAB(CApi.SQLITE_CORRUPT_VTAB),
|
||||
SQLITE_CORRUPT_SEQUENCE(CApi.SQLITE_CORRUPT_SEQUENCE),
|
||||
SQLITE_CORRUPT_INDEX(CApi.SQLITE_CORRUPT_INDEX),
|
||||
SQLITE_READONLY_RECOVERY(CApi.SQLITE_READONLY_RECOVERY),
|
||||
SQLITE_READONLY_CANTLOCK(CApi.SQLITE_READONLY_CANTLOCK),
|
||||
SQLITE_READONLY_ROLLBACK(CApi.SQLITE_READONLY_ROLLBACK),
|
||||
SQLITE_READONLY_DBMOVED(CApi.SQLITE_READONLY_DBMOVED),
|
||||
SQLITE_READONLY_CANTINIT(CApi.SQLITE_READONLY_CANTINIT),
|
||||
SQLITE_READONLY_DIRECTORY(CApi.SQLITE_READONLY_DIRECTORY),
|
||||
SQLITE_ABORT_ROLLBACK(CApi.SQLITE_ABORT_ROLLBACK),
|
||||
SQLITE_CONSTRAINT_CHECK(CApi.SQLITE_CONSTRAINT_CHECK),
|
||||
SQLITE_CONSTRAINT_COMMITHOOK(CApi.SQLITE_CONSTRAINT_COMMITHOOK),
|
||||
SQLITE_CONSTRAINT_FOREIGNKEY(CApi.SQLITE_CONSTRAINT_FOREIGNKEY),
|
||||
SQLITE_CONSTRAINT_FUNCTION(CApi.SQLITE_CONSTRAINT_FUNCTION),
|
||||
SQLITE_CONSTRAINT_NOTNULL(CApi.SQLITE_CONSTRAINT_NOTNULL),
|
||||
SQLITE_CONSTRAINT_PRIMARYKEY(CApi.SQLITE_CONSTRAINT_PRIMARYKEY),
|
||||
SQLITE_CONSTRAINT_TRIGGER(CApi.SQLITE_CONSTRAINT_TRIGGER),
|
||||
SQLITE_CONSTRAINT_UNIQUE(CApi.SQLITE_CONSTRAINT_UNIQUE),
|
||||
SQLITE_CONSTRAINT_VTAB(CApi.SQLITE_CONSTRAINT_VTAB),
|
||||
SQLITE_CONSTRAINT_ROWID(CApi.SQLITE_CONSTRAINT_ROWID),
|
||||
SQLITE_CONSTRAINT_PINNED(CApi.SQLITE_CONSTRAINT_PINNED),
|
||||
SQLITE_CONSTRAINT_DATATYPE(CApi.SQLITE_CONSTRAINT_DATATYPE),
|
||||
SQLITE_NOTICE_RECOVER_WAL(CApi.SQLITE_NOTICE_RECOVER_WAL),
|
||||
SQLITE_NOTICE_RECOVER_ROLLBACK(CApi.SQLITE_NOTICE_RECOVER_ROLLBACK),
|
||||
SQLITE_WARNING_AUTOINDEX(CApi.SQLITE_WARNING_AUTOINDEX),
|
||||
SQLITE_AUTH_USER(CApi.SQLITE_AUTH_USER),
|
||||
SQLITE_OK_LOAD_PERMANENTLY(CApi.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); }
|
||||
}
|
||||
|
||||
}
|
||||
+5
-4
@@ -11,15 +11,16 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_rollback_hook}.
|
||||
Callback for use with {@link CApi#sqlite3_rollback_hook}.
|
||||
*/
|
||||
public interface RollbackHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Works as documented for the C-level sqlite3_rollback_hook()
|
||||
callback.
|
||||
Must function as documented for the C-level sqlite3_rollback_hook()
|
||||
callback. If it throws, the exception is translated into
|
||||
a db-level error.
|
||||
*/
|
||||
void call();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
** 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.capi;
|
||||
|
||||
/**
|
||||
SQLFunction is used in conjunction with the
|
||||
sqlite3_create_function() JNI-bound API to give that native code
|
||||
access to the callback functions needed in order to implement SQL
|
||||
functions in Java.
|
||||
|
||||
<p>
|
||||
|
||||
This class is not used by itself, but is a marker base class. The
|
||||
three UDF types are modelled by the inner classes Scalar,
|
||||
Aggregate<T>, and Window<T>. Most simply, clients may subclass
|
||||
those, or create anonymous classes from them, to implement
|
||||
UDFs. Clients are free to create their own classes for use with
|
||||
UDFs, so long as they conform to the public interfaces defined by
|
||||
those three classes. The JNI layer only actively relies on the
|
||||
SQLFunction base class and the method names and signatures used by
|
||||
the UDF callback interfaces.
|
||||
*/
|
||||
public interface SQLFunction {
|
||||
|
||||
}
|
||||
+4
-4
@@ -12,13 +12,13 @@
|
||||
** This file contains the main application entry pointer for the
|
||||
** SQLTester framework.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.*;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
import static org.sqlite.jni.capi.CApi.*;
|
||||
|
||||
/**
|
||||
Modes for how to escape (or not) column values and names from
|
||||
@@ -457,7 +457,7 @@ public class SQLTester {
|
||||
}
|
||||
|
||||
private void appendDbErr(sqlite3 db, StringBuilder sb, int rc){
|
||||
sb.append(org.sqlite.jni.ResultCode.getEntryForInt(rc)).append(' ');
|
||||
sb.append(org.sqlite.jni.capi.ResultCode.getEntryForInt(rc)).append(' ');
|
||||
final String msg = escapeSqlValue(sqlite3_errmsg(db));
|
||||
if( '{' == msg.charAt(0) ){
|
||||
sb.append(msg);
|
||||
@@ -668,7 +668,7 @@ public class SQLTester {
|
||||
static {
|
||||
System.loadLibrary("sqlite3-jni")
|
||||
/* Interestingly, when SQLTester is the main app, we have to
|
||||
load that lib from here. The same load from SQLite3Jni does
|
||||
load that lib from here. The same load from CApi does
|
||||
not happen early enough. Without this,
|
||||
installCustomExtensions() is an unresolved symbol. */;
|
||||
}
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ public abstract class ScalarFunction implements SQLFunction {
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite. This implementation does nothing.
|
||||
SQLite. This default implementation does nothing.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A wrapper object for use with sqlite3_table_column_metadata().
|
||||
+322
-83
@@ -11,8 +11,8 @@
|
||||
*************************************************************************
|
||||
** This file contains a set of tests for the sqlite3 JNI bindings.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
package org.sqlite.jni.capi;
|
||||
import static org.sqlite.jni.capi.CApi.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
@@ -39,15 +39,12 @@ import java.util.concurrent.Future;
|
||||
@interface SingleThreadOnly{}
|
||||
|
||||
/**
|
||||
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.
|
||||
Annotation for Tester1 tests which must only be run if
|
||||
sqlite3_jni_supports_nio() is true.
|
||||
*/
|
||||
class ValueHolder<T> {
|
||||
public T value;
|
||||
public ValueHolder(){}
|
||||
public ValueHolder(T v){value = v;}
|
||||
}
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
|
||||
@interface RequiresJniNio{}
|
||||
|
||||
public class Tester1 implements Runnable {
|
||||
//! True when running in multi-threaded mode.
|
||||
@@ -57,7 +54,7 @@ public class Tester1 implements Runnable {
|
||||
//! True to shuffle the order of the tests.
|
||||
private static boolean shuffle = false;
|
||||
//! True to dump the list of to-run tests to stdout.
|
||||
private static boolean listRunTests = false;
|
||||
private static int listRunTests = 0;
|
||||
//! True to squelch all out() and outln() output.
|
||||
private static boolean quietMode = false;
|
||||
//! Total number of runTests() calls.
|
||||
@@ -255,6 +252,12 @@ public class Tester1 implements Runnable {
|
||||
and this call is here to ensure that the build fails
|
||||
if it cannot find both names. */;
|
||||
|
||||
affirm( 0==sqlite3_db_readonly(db,"main") );
|
||||
affirm( 0==sqlite3_db_readonly(db,null) );
|
||||
affirm( 0>sqlite3_db_readonly(db,"nope") );
|
||||
affirm( 0>sqlite3_db_readonly(null,null) );
|
||||
affirm( 0==sqlite3_last_insert_rowid(null) );
|
||||
|
||||
// These interrupt checks are only to make sure that the JNI binding
|
||||
// has the proper exported symbol names. They don't actually test
|
||||
// anything useful.
|
||||
@@ -332,7 +335,7 @@ public class Tester1 implements Runnable {
|
||||
|
||||
|
||||
rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)",
|
||||
SQLITE_PREPARE_NORMALIZE, outStmt);
|
||||
0, outStmt);
|
||||
affirm(0 == rc);
|
||||
stmt = outStmt.get();
|
||||
affirm(0 != stmt.getNativePointer());
|
||||
@@ -387,6 +390,15 @@ public class Tester1 implements Runnable {
|
||||
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
|
||||
affirm( sqlite3_stmt_readonly(stmt) );
|
||||
affirm( !sqlite3_stmt_busy(stmt) );
|
||||
if( sqlite3_compileoption_used("ENABLE_COLUMN_METADATA") ){
|
||||
/* Unlike in native C code, JNI won't trigger an
|
||||
UnsatisfiedLinkError until these are called (on Linux, at
|
||||
least). */
|
||||
affirm("t".equals(sqlite3_column_table_name(stmt,0)));
|
||||
affirm("main".equals(sqlite3_column_database_name(stmt,0)));
|
||||
affirm("a".equals(sqlite3_column_origin_name(stmt,0)));
|
||||
}
|
||||
|
||||
int total2 = 0;
|
||||
while( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
affirm( sqlite3_stmt_busy(stmt) );
|
||||
@@ -482,6 +494,7 @@ public class Tester1 implements Runnable {
|
||||
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
|
||||
StringBuilder sbuf = new StringBuilder();
|
||||
n = 0;
|
||||
final boolean tryNio = sqlite3_jni_supports_nio();
|
||||
while( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
final sqlite3_value sv = sqlite3_value_dup(sqlite3_column_value(stmt,0));
|
||||
final String txt = sqlite3_column_text16(stmt, 0);
|
||||
@@ -496,6 +509,15 @@ public class Tester1 implements Runnable {
|
||||
StandardCharsets.UTF_8)) );
|
||||
affirm( txt.length() == sqlite3_value_bytes16(sv)/2 );
|
||||
affirm( txt.equals(sqlite3_value_text16(sv)) );
|
||||
if( tryNio ){
|
||||
java.nio.ByteBuffer bu = sqlite3_value_nio_buffer(sv);
|
||||
byte ba[] = sqlite3_value_blob(sv);
|
||||
affirm( ba.length == bu.capacity() );
|
||||
int i = 0;
|
||||
for( byte b : ba ){
|
||||
affirm( b == bu.get(i++) );
|
||||
}
|
||||
}
|
||||
sqlite3_value_free(sv);
|
||||
++n;
|
||||
}
|
||||
@@ -553,6 +575,79 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
@RequiresJniNio
|
||||
private void testBindByteBuffer(){
|
||||
/* TODO: these tests need to be much more extensive to check the
|
||||
begin/end range handling. */
|
||||
|
||||
java.nio.ByteBuffer zeroCheck =
|
||||
java.nio.ByteBuffer.allocateDirect(0);
|
||||
affirm( null != zeroCheck );
|
||||
zeroCheck = null;
|
||||
sqlite3 db = createNewDb();
|
||||
execSql(db, "CREATE TABLE t(a)");
|
||||
|
||||
final java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(10);
|
||||
buf.put((byte)0x31)/*note that we'll skip this one*/
|
||||
.put((byte)0x32)
|
||||
.put((byte)0x33)
|
||||
.put((byte)0x34)
|
||||
.put((byte)0x35)/*we'll skip this one too*/;
|
||||
|
||||
final int expectTotal = buf.get(1) + buf.get(2) + buf.get(3);
|
||||
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
|
||||
affirm( SQLITE_ERROR == sqlite3_bind_blob(stmt, 1, buf, -1, 0),
|
||||
"Buffer offset may not be negative." );
|
||||
affirm( 0 == sqlite3_bind_blob(stmt, 1, buf, 1, 3) );
|
||||
affirm( SQLITE_DONE == sqlite3_step(stmt) );
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = prepare(db, "SELECT a FROM t;");
|
||||
int total = 0;
|
||||
affirm( SQLITE_ROW == sqlite3_step(stmt) );
|
||||
byte blob[] = sqlite3_column_blob(stmt, 0);
|
||||
java.nio.ByteBuffer nioBlob =
|
||||
sqlite3_column_nio_buffer(stmt, 0);
|
||||
affirm(3 == blob.length);
|
||||
affirm(blob.length == nioBlob.capacity());
|
||||
affirm(blob.length == nioBlob.limit());
|
||||
int i = 0;
|
||||
for(byte b : blob){
|
||||
affirm( i<=3 );
|
||||
affirm(b == buf.get(1 + i));
|
||||
affirm(b == nioBlob.get(i));
|
||||
++i;
|
||||
total += b;
|
||||
}
|
||||
affirm( SQLITE_DONE == sqlite3_step(stmt) );
|
||||
sqlite3_finalize(stmt);
|
||||
affirm(total == expectTotal);
|
||||
|
||||
SQLFunction func =
|
||||
new ScalarFunction(){
|
||||
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
|
||||
sqlite3_result_blob(cx, buf, 1, 3);
|
||||
}
|
||||
};
|
||||
|
||||
affirm( 0 == sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func) );
|
||||
stmt = prepare(db, "SELECT myfunc()");
|
||||
affirm( SQLITE_ROW == sqlite3_step(stmt) );
|
||||
blob = sqlite3_column_blob(stmt, 0);
|
||||
affirm(3 == blob.length);
|
||||
i = 0;
|
||||
total = 0;
|
||||
for(byte b : blob){
|
||||
affirm( i<=3 );
|
||||
affirm(b == buf.get(1 + i++));
|
||||
total += b;
|
||||
}
|
||||
affirm( SQLITE_DONE == sqlite3_step(stmt) );
|
||||
sqlite3_finalize(stmt);
|
||||
affirm(total == expectTotal);
|
||||
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
private void testSql(){
|
||||
sqlite3 db = createNewDb();
|
||||
sqlite3_stmt stmt = prepare(db, "SELECT 1");
|
||||
@@ -563,7 +658,7 @@ public class Tester1 implements Runnable {
|
||||
final String expect = "SELECT 'hell😃'";
|
||||
affirm( expect.equals(sqlite3_expanded_sql(stmt)) );
|
||||
String n = sqlite3_normalized_sql(stmt);
|
||||
affirm( null==n || expect.equals(n) );
|
||||
affirm( null==n || "SELECT?;".equals(n) );
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
}
|
||||
@@ -598,9 +693,9 @@ public class Tester1 implements Runnable {
|
||||
};
|
||||
final CollationNeededCallback collLoader = new CollationNeededCallback(){
|
||||
@Override
|
||||
public int call(sqlite3 dbArg, int eTextRep, String collationName){
|
||||
public void call(sqlite3 dbArg, int eTextRep, String collationName){
|
||||
affirm(dbArg == db/* as opposed to a temporary object*/);
|
||||
return sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation);
|
||||
sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation);
|
||||
}
|
||||
};
|
||||
int rc = sqlite3_collation_needed(db, collLoader);
|
||||
@@ -688,6 +783,8 @@ public class Tester1 implements Runnable {
|
||||
// These ValueHolders are just to confirm that the func did what we want...
|
||||
final ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
|
||||
final ValueHolder<Integer> xFuncAccum = new ValueHolder<>(0);
|
||||
final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null);
|
||||
final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null);
|
||||
|
||||
// Create an SQLFunction instance using one of its 3 subclasses:
|
||||
// Scalar, Aggregate, or Window:
|
||||
@@ -698,6 +795,15 @@ public class Tester1 implements Runnable {
|
||||
new ScalarFunction(){
|
||||
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
|
||||
affirm(db == sqlite3_context_db_handle(cx));
|
||||
if( null==neverEverDoThisInClientCode.value ){
|
||||
/* !!!NEVER!!! hold a reference to an sqlite3_value or
|
||||
sqlite3_context object like this in client code! They
|
||||
are ONLY legal for the duration of their single
|
||||
call. We do it here ONLY to test that the defenses
|
||||
against clients doing this are working. */
|
||||
neverEverDoThisInClientCode2.value = cx;
|
||||
neverEverDoThisInClientCode.value = args;
|
||||
}
|
||||
int result = 0;
|
||||
for( sqlite3_value v : args ) result += sqlite3_value_int(v);
|
||||
xFuncAccum.value += result;// just for post-run testing
|
||||
@@ -723,6 +829,13 @@ public class Tester1 implements Runnable {
|
||||
affirm(1 == n);
|
||||
affirm(6 == xFuncAccum.value);
|
||||
affirm( !xDestroyCalled.value );
|
||||
affirm( null!=neverEverDoThisInClientCode.value );
|
||||
affirm( null!=neverEverDoThisInClientCode2.value );
|
||||
affirm( 0<neverEverDoThisInClientCode.value.length );
|
||||
affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() );
|
||||
for( sqlite3_value sv : neverEverDoThisInClientCode.value ){
|
||||
affirm( 0==sv.getNativePointer() );
|
||||
}
|
||||
sqlite3_close_v2(db);
|
||||
affirm( xDestroyCalled.value );
|
||||
}
|
||||
@@ -790,13 +903,17 @@ public class Tester1 implements Runnable {
|
||||
affirm( 0==rc );
|
||||
int n = 0;
|
||||
if( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
affirm( testResult.value == sqlite3_column_java_object(stmt, 0) );
|
||||
affirm( testResult.value == sqlite3_column_java_object(stmt, 0, sqlite3.class) );
|
||||
affirm( null == sqlite3_column_java_object(stmt, 0, sqlite3_stmt.class) );
|
||||
affirm( null == sqlite3_column_java_object(stmt,1) );
|
||||
final sqlite3_value v = sqlite3_column_value(stmt, 0);
|
||||
affirm( testResult.value == sqlite3_value_java_object(v) );
|
||||
affirm( testResult.value == sqlite3_value_java_casted(v, sqlite3.class) );
|
||||
affirm( testResult.value == sqlite3_value_java_object(v, sqlite3.class) );
|
||||
affirm( testResult.value ==
|
||||
sqlite3_value_java_casted(v, testResult.value.getClass()) );
|
||||
affirm( testResult.value == sqlite3_value_java_casted(v, Object.class) );
|
||||
affirm( null == sqlite3_value_java_casted(v, String.class) );
|
||||
sqlite3_value_java_object(v, testResult.value.getClass()) );
|
||||
affirm( testResult.value == sqlite3_value_java_object(v, Object.class) );
|
||||
affirm( null == sqlite3_value_java_object(v, String.class) );
|
||||
++n;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
@@ -811,15 +928,28 @@ public class Tester1 implements Runnable {
|
||||
// To confirm that xFinal() is called with no aggregate state
|
||||
// when the corresponding result set is empty.
|
||||
new ValueHolder<>(false);
|
||||
final ValueHolder<sqlite3_value[]> neverEverDoThisInClientCode = new ValueHolder<>(null);
|
||||
final ValueHolder<sqlite3_context> neverEverDoThisInClientCode2 = new ValueHolder<>(null);
|
||||
SQLFunction func = new AggregateFunction<Integer>(){
|
||||
@Override
|
||||
public void xStep(sqlite3_context cx, sqlite3_value[] args){
|
||||
if( null==neverEverDoThisInClientCode.value ){
|
||||
/* !!!NEVER!!! hold a reference to an sqlite3_value or
|
||||
sqlite3_context object like this in client code! They
|
||||
are ONLY legal for the duration of their single
|
||||
call. We do it here ONLY to test that the defenses
|
||||
against clients doing this are working. */
|
||||
neverEverDoThisInClientCode.value = args;
|
||||
}
|
||||
final ValueHolder<Integer> agg = this.getAggregateState(cx, 0);
|
||||
agg.value += sqlite3_value_int(args[0]);
|
||||
affirm( agg == this.getAggregateState(cx, 0) );
|
||||
}
|
||||
@Override
|
||||
public void xFinal(sqlite3_context cx){
|
||||
if( null==neverEverDoThisInClientCode2.value ){
|
||||
neverEverDoThisInClientCode2.value = cx;
|
||||
}
|
||||
final Integer v = this.takeAggregateState(cx);
|
||||
if(null == v){
|
||||
xFinalNull.value = true;
|
||||
@@ -844,6 +974,10 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
affirm( 1==n );
|
||||
affirm(!xFinalNull.value);
|
||||
affirm( null!=neverEverDoThisInClientCode.value );
|
||||
affirm( null!=neverEverDoThisInClientCode2.value );
|
||||
affirm( 0<neverEverDoThisInClientCode.value.length );
|
||||
affirm( 0==neverEverDoThisInClientCode2.value.getNativePointer() );
|
||||
sqlite3_reset(stmt);
|
||||
affirm( 1==sqlite3_stmt_status(stmt, SQLITE_STMTSTATUS_RUN, false) );
|
||||
// Ensure that the accumulator is reset on subsequent calls...
|
||||
@@ -912,7 +1046,6 @@ public class Tester1 implements Runnable {
|
||||
"ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+
|
||||
") AS sum_y "+
|
||||
"FROM twin ORDER BY x;");
|
||||
affirm( 0 == rc );
|
||||
int n = 0;
|
||||
while( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
final String s = sqlite3_column_text16(stmt, 0);
|
||||
@@ -934,7 +1067,7 @@ public class Tester1 implements Runnable {
|
||||
private void listBoundMethods(){
|
||||
if(false){
|
||||
final java.lang.reflect.Field[] declaredFields =
|
||||
SQLite3Jni.class.getDeclaredFields();
|
||||
CApi.class.getDeclaredFields();
|
||||
outln("Bound constants:\n");
|
||||
for(java.lang.reflect.Field field : declaredFields) {
|
||||
if(java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
|
||||
@@ -943,7 +1076,7 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
}
|
||||
final java.lang.reflect.Method[] declaredMethods =
|
||||
SQLite3Jni.class.getDeclaredMethods();
|
||||
CApi.class.getDeclaredMethods();
|
||||
final java.util.List<String> funcList = new java.util.ArrayList<>();
|
||||
for(java.lang.reflect.Method m : declaredMethods){
|
||||
if((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0){
|
||||
@@ -1015,45 +1148,48 @@ public class Tester1 implements Runnable {
|
||||
@SingleThreadOnly /* because threads inherently break this test */
|
||||
private static void testBusy(){
|
||||
final String dbName = "_busy-handler.db";
|
||||
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
|
||||
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
|
||||
|
||||
int rc = sqlite3_open(dbName, outDb);
|
||||
++metrics.dbOpen;
|
||||
affirm( 0 == rc );
|
||||
final sqlite3 db1 = outDb.get();
|
||||
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
|
||||
rc = sqlite3_open(dbName, outDb);
|
||||
++metrics.dbOpen;
|
||||
affirm( 0 == rc );
|
||||
affirm( outDb.get() != db1 );
|
||||
final sqlite3 db2 = outDb.get();
|
||||
rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo");
|
||||
affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) );
|
||||
|
||||
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
|
||||
BusyHandlerCallback handler = new BusyHandlerCallback(){
|
||||
@Override public int call(int n){
|
||||
//outln("busy handler #"+n);
|
||||
return n > 2 ? 0 : ++xBusyCalled.value;
|
||||
}
|
||||
};
|
||||
rc = sqlite3_busy_handler(db2, handler);
|
||||
affirm(0 == rc);
|
||||
|
||||
// Force a locked condition...
|
||||
execSql(db1, "BEGIN EXCLUSIVE");
|
||||
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
|
||||
affirm( SQLITE_BUSY == rc);
|
||||
affirm( null == outStmt.get() );
|
||||
affirm( 3 == xBusyCalled.value );
|
||||
sqlite3_close_v2(db1);
|
||||
sqlite3_close_v2(db2);
|
||||
try{
|
||||
final java.io.File f = new java.io.File(dbName);
|
||||
f.delete();
|
||||
}catch(Exception e){
|
||||
/* ignore */
|
||||
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
|
||||
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
|
||||
|
||||
int rc = sqlite3_open(dbName, outDb);
|
||||
++metrics.dbOpen;
|
||||
affirm( 0 == rc );
|
||||
final sqlite3 db1 = outDb.get();
|
||||
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
|
||||
rc = sqlite3_open(dbName, outDb);
|
||||
++metrics.dbOpen;
|
||||
affirm( 0 == rc );
|
||||
affirm( outDb.get() != db1 );
|
||||
final sqlite3 db2 = outDb.get();
|
||||
|
||||
affirm( "main".equals( sqlite3_db_name(db1, 0) ) );
|
||||
rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo");
|
||||
affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) );
|
||||
affirm( "foo".equals( sqlite3_db_name(db1, 0) ) );
|
||||
affirm( SQLITE_MISUSE == sqlite3_db_config(db1, 0, 0, null) );
|
||||
|
||||
final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
|
||||
BusyHandlerCallback handler = new BusyHandlerCallback(){
|
||||
@Override public int call(int n){
|
||||
//outln("busy handler #"+n);
|
||||
return n > 2 ? 0 : ++xBusyCalled.value;
|
||||
}
|
||||
};
|
||||
rc = sqlite3_busy_handler(db2, handler);
|
||||
affirm(0 == rc);
|
||||
|
||||
// Force a locked condition...
|
||||
execSql(db1, "BEGIN EXCLUSIVE");
|
||||
rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
|
||||
affirm( SQLITE_BUSY == rc);
|
||||
affirm( null == outStmt.get() );
|
||||
affirm( 3 == xBusyCalled.value );
|
||||
sqlite3_close_v2(db1);
|
||||
sqlite3_close_v2(db2);
|
||||
}finally{
|
||||
try{(new java.io.File(dbName)).delete();}
|
||||
catch(Exception e){/* ignore */}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1077,6 +1213,7 @@ public class Tester1 implements Runnable {
|
||||
|
||||
private void testCommitHook(){
|
||||
final sqlite3 db = createNewDb();
|
||||
sqlite3_extended_result_codes(db, true);
|
||||
final ValueHolder<Integer> counter = new ValueHolder<>(0);
|
||||
final ValueHolder<Integer> hookResult = new ValueHolder<>(0);
|
||||
final CommitHookCallback theHook = new CommitHookCallback(){
|
||||
@@ -1119,7 +1256,7 @@ public class Tester1 implements Runnable {
|
||||
affirm( 5 == counter.value );
|
||||
hookResult.value = SQLITE_ERROR;
|
||||
int rc = execSql(db, false, "BEGIN; update t set a='j' where a='i'; COMMIT;");
|
||||
affirm( SQLITE_CONSTRAINT == rc );
|
||||
affirm( SQLITE_CONSTRAINT_COMMITHOOK == rc );
|
||||
affirm( 6 == counter.value );
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
@@ -1296,7 +1433,7 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
Exception err = null;
|
||||
try {
|
||||
Class t = Class.forName("org.sqlite.jni.TesterFts5");
|
||||
Class t = Class.forName("org.sqlite.jni.fts5.TesterFts5");
|
||||
java.lang.reflect.Constructor ctor = t.getConstructor();
|
||||
ctor.setAccessible(true);
|
||||
final long timeStart = System.currentTimeMillis();
|
||||
@@ -1338,6 +1475,9 @@ public class Tester1 implements Runnable {
|
||||
authRc.value = SQLITE_DENY;
|
||||
int rc = execSql(db, false, "UPDATE t SET a=2");
|
||||
affirm( SQLITE_AUTH==rc );
|
||||
sqlite3_set_authorizer(db, null);
|
||||
rc = execSql(db, false, "UPDATE t SET a=2");
|
||||
affirm( 0==rc );
|
||||
// TODO: expand these tests considerably
|
||||
sqlite3_close(db);
|
||||
}
|
||||
@@ -1399,7 +1539,7 @@ public class Tester1 implements Runnable {
|
||||
|
||||
val.value = 0;
|
||||
final AutoExtensionCallback ax2 = new AutoExtensionCallback(){
|
||||
@Override public synchronized int call(sqlite3 db){
|
||||
@Override public int call(sqlite3 db){
|
||||
++val.value;
|
||||
return 0;
|
||||
}
|
||||
@@ -1456,7 +1596,7 @@ public class Tester1 implements Runnable {
|
||||
affirm( "noCase".equals(zCollSeq.value) );
|
||||
affirm( "duck".equals(zDataType.value) );
|
||||
|
||||
final TableColumnMetadata m =
|
||||
TableColumnMetadata m =
|
||||
sqlite3_table_column_metadata(db, "main", "t", "a");
|
||||
affirm( null != m );
|
||||
affirm( bPrimaryKey.value == m.isPrimaryKey() );
|
||||
@@ -1466,6 +1606,16 @@ public class Tester1 implements Runnable {
|
||||
affirm( zDataType.value.equals(m.getDataType()) );
|
||||
|
||||
affirm( null == sqlite3_table_column_metadata(db, "nope", "t", "a") );
|
||||
affirm( null == sqlite3_table_column_metadata(db, "main", "nope", "a") );
|
||||
|
||||
m = sqlite3_table_column_metadata(db, "main", "t", null)
|
||||
/* Check only for existence of table */;
|
||||
affirm( null != m );
|
||||
affirm( m.isPrimaryKey() );
|
||||
affirm( !m.isAutoincrement() );
|
||||
affirm( !m.isNotNull() );
|
||||
affirm( "BINARY".equalsIgnoreCase(m.getCollation()) );
|
||||
affirm( "INTEGER".equalsIgnoreCase(m.getDataType()) );
|
||||
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
@@ -1600,7 +1750,7 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
b = sqlite3_blob_open(db, "main", "t", "a",
|
||||
sqlite3_last_insert_rowid(db), 1);
|
||||
sqlite3_last_insert_rowid(db), 0);
|
||||
affirm( null!=b );
|
||||
rc = sqlite3_blob_reopen(b, 2);
|
||||
affirm( 0==rc );
|
||||
@@ -1610,6 +1760,86 @@ public class Tester1 implements Runnable {
|
||||
affirm( 100==tgt[0] && 101==tgt[1] && 102==tgt[2], "DEF" );
|
||||
rc = sqlite3_blob_close(b);
|
||||
affirm( 0==rc );
|
||||
|
||||
if( !sqlite3_jni_supports_nio() ){
|
||||
outln("WARNING: skipping tests for ByteBuffer-using sqlite3_blob APIs ",
|
||||
"because this platform lacks that support.");
|
||||
sqlite3_close_v2(db);
|
||||
return;
|
||||
}
|
||||
/* Sanity checks for the java.nio.ByteBuffer-taking overloads of
|
||||
sqlite3_blob_read/write(). */
|
||||
execSql(db, "UPDATE t SET a=zeroblob(10)");
|
||||
b = sqlite3_blob_open(db, "main", "t", "a", 1, 1);
|
||||
affirm( null!=b );
|
||||
java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocateDirect(10);
|
||||
for( byte i = 0; i < 10; ++i ){
|
||||
bb.put((int)i, (byte)(48+i & 0xff));
|
||||
}
|
||||
rc = sqlite3_blob_write(b, 1, bb, 1, 10);
|
||||
affirm( rc==SQLITE_ERROR, "b length < (srcOffset + bb length)" );
|
||||
rc = sqlite3_blob_write(b, -1, bb);
|
||||
affirm( rc==SQLITE_ERROR, "Target offset may not be negative" );
|
||||
rc = sqlite3_blob_write(b, 0, bb, -1, -1);
|
||||
affirm( rc==SQLITE_ERROR, "Source offset may not be negative" );
|
||||
rc = sqlite3_blob_write(b, 1, bb, 1, 8);
|
||||
affirm( rc==0 );
|
||||
// b's contents: 0 49 50 51 52 53 54 55 56 0
|
||||
// ascii: 0 '1' '2' '3' '4' '5' '6' '7' '8' 0
|
||||
byte br[] = new byte[10];
|
||||
java.nio.ByteBuffer bbr =
|
||||
java.nio.ByteBuffer.allocateDirect(bb.limit());
|
||||
rc = sqlite3_blob_read( b, br, 0 );
|
||||
affirm( rc==0 );
|
||||
rc = sqlite3_blob_read( b, bbr );
|
||||
affirm( rc==0 );
|
||||
java.nio.ByteBuffer bbr2 = sqlite3_blob_read_nio_buffer(b, 0, 12);
|
||||
affirm( null==bbr2, "Read size is too big");
|
||||
bbr2 = sqlite3_blob_read_nio_buffer(b, -1, 3);
|
||||
affirm( null==bbr2, "Source offset is negative");
|
||||
bbr2 = sqlite3_blob_read_nio_buffer(b, 5, 6);
|
||||
affirm( null==bbr2, "Read pos+size is too big");
|
||||
bbr2 = sqlite3_blob_read_nio_buffer(b, 4, 7);
|
||||
affirm( null==bbr2, "Read pos+size is too big");
|
||||
bbr2 = sqlite3_blob_read_nio_buffer(b, 4, 6);
|
||||
affirm( null!=bbr2 );
|
||||
java.nio.ByteBuffer bbr3 =
|
||||
java.nio.ByteBuffer.allocateDirect(2 * bb.limit());
|
||||
java.nio.ByteBuffer bbr4 =
|
||||
java.nio.ByteBuffer.allocateDirect(5);
|
||||
rc = sqlite3_blob_read( b, bbr3 );
|
||||
affirm( rc==0 );
|
||||
rc = sqlite3_blob_read( b, bbr4 );
|
||||
affirm( rc==0 );
|
||||
affirm( sqlite3_blob_bytes(b)==bbr3.limit() );
|
||||
affirm( 5==bbr4.limit() );
|
||||
sqlite3_blob_close(b);
|
||||
affirm( 0==br[0] );
|
||||
affirm( 0==br[9] );
|
||||
affirm( 0==bbr.get(0) );
|
||||
affirm( 0==bbr.get(9) );
|
||||
affirm( bbr2.limit() == 6 );
|
||||
affirm( 0==bbr3.get(0) );
|
||||
{
|
||||
Exception ex = null;
|
||||
try{ bbr3.get(11); }
|
||||
catch(Exception e){ex = e;}
|
||||
affirm( ex instanceof IndexOutOfBoundsException,
|
||||
"bbr3.limit() was reset by read()" );
|
||||
ex = null;
|
||||
}
|
||||
affirm( 0==bbr4.get(0) );
|
||||
for( int i = 1; i < 9; ++i ){
|
||||
affirm( br[i] == 48 + i );
|
||||
affirm( br[i] == bbr.get(i) );
|
||||
affirm( br[i] == bbr3.get(i) );
|
||||
if( i>3 ){
|
||||
affirm( br[i] == bbr2.get(i-4) );
|
||||
}
|
||||
if( i < bbr4.limit() ){
|
||||
affirm( br[i] == bbr4.get(i) );
|
||||
}
|
||||
}
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
@@ -1622,9 +1852,13 @@ public class Tester1 implements Runnable {
|
||||
};
|
||||
final List<sqlite3_stmt> liStmt = new ArrayList<sqlite3_stmt>();
|
||||
final PrepareMultiCallback proxy = new PrepareMultiCallback.StepAll();
|
||||
final ValueHolder<String> toss = new ValueHolder<>(null);
|
||||
PrepareMultiCallback m = new PrepareMultiCallback() {
|
||||
@Override public int call(sqlite3_stmt st){
|
||||
liStmt.add(st);
|
||||
if( null!=toss.value ){
|
||||
throw new RuntimeException(toss.value);
|
||||
}
|
||||
return proxy.call(st);
|
||||
}
|
||||
};
|
||||
@@ -1634,6 +1868,10 @@ public class Tester1 implements Runnable {
|
||||
for( sqlite3_stmt st : liStmt ){
|
||||
sqlite3_finalize(st);
|
||||
}
|
||||
toss.value = "This is an exception.";
|
||||
rc = sqlite3_prepare_multi(db, "SELECT 1", m);
|
||||
affirm( SQLITE_ERROR==rc );
|
||||
affirm( sqlite3_errmsg(db).indexOf(toss.value)>0 );
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
@@ -1672,7 +1910,7 @@ public class Tester1 implements Runnable {
|
||||
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
|
||||
java.util.Collections.shuffle(mlist);
|
||||
}
|
||||
if( listRunTests ){
|
||||
if( (!fromThread && listRunTests>0) || listRunTests>1 ){
|
||||
synchronized(this.getClass()){
|
||||
if( !fromThread ){
|
||||
out("Initial test"," list: ");
|
||||
@@ -1734,8 +1972,11 @@ public class Tester1 implements Runnable {
|
||||
-naps: sleep small random intervals between tests in order to add
|
||||
some chaos for cross-thread contention.
|
||||
|
||||
|
||||
-list-tests: outputs the list of tests being run, minus some
|
||||
which are hard-coded. This is noisy in multi-threaded mode.
|
||||
which are hard-coded. In multi-threaded mode, use this twice to
|
||||
to emit the list run by each thread (which may differ from the initial
|
||||
list, in particular if -shuffle is used).
|
||||
|
||||
-fail: forces an exception to be thrown during the test run. Use
|
||||
with -shuffle to make its appearance unpredictable.
|
||||
@@ -1764,7 +2005,7 @@ public class Tester1 implements Runnable {
|
||||
}else if(arg.equals("shuffle")){
|
||||
shuffle = true;
|
||||
}else if(arg.equals("list-tests")){
|
||||
listRunTests = true;
|
||||
++listRunTests;
|
||||
}else if(arg.equals("fail")){
|
||||
forceFail = true;
|
||||
}else if(arg.equals("sqllog")){
|
||||
@@ -1783,7 +2024,7 @@ public class Tester1 implements Runnable {
|
||||
|
||||
if( sqlLog ){
|
||||
if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){
|
||||
final ConfigSqllogCallback log = new ConfigSqllogCallback() {
|
||||
final ConfigSqlLogCallback log = new ConfigSqlLogCallback() {
|
||||
@Override public void call(sqlite3 db, String msg, int op){
|
||||
switch(op){
|
||||
case 0: outln("Opening db: ",db); break;
|
||||
@@ -1794,7 +2035,7 @@ public class Tester1 implements Runnable {
|
||||
};
|
||||
int rc = sqlite3_config( log );
|
||||
affirm( 0==rc );
|
||||
rc = sqlite3_config( (ConfigSqllogCallback)null );
|
||||
rc = sqlite3_config( (ConfigSqlLogCallback)null );
|
||||
affirm( 0==rc );
|
||||
rc = sqlite3_config( log );
|
||||
affirm( 0==rc );
|
||||
@@ -1832,18 +2073,20 @@ public class Tester1 implements Runnable {
|
||||
if( forceFail ){
|
||||
testMethods.add(m);
|
||||
}
|
||||
}else if( m.isAnnotationPresent( RequiresJniNio.class )
|
||||
&& !sqlite3_jni_supports_nio() ){
|
||||
outln("Skipping test for lack of JNI java.nio.ByteBuffer support: ",
|
||||
name,"()\n");
|
||||
++nSkipped;
|
||||
}else if( !m.isAnnotationPresent( ManualTest.class ) ){
|
||||
if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
|
||||
if( 0==nSkipped++ ){
|
||||
out("Skipping tests in multi-thread mode:");
|
||||
}
|
||||
out(" "+name+"()");
|
||||
out("Skipping test in multi-thread mode: ",name,"()\n");
|
||||
++nSkipped;
|
||||
}else if( name.startsWith("test") ){
|
||||
testMethods.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( nSkipped>0 ) out("\n");
|
||||
}
|
||||
|
||||
final long timeStart = System.currentTimeMillis();
|
||||
@@ -1875,6 +2118,7 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_libversion_number(),"\n",
|
||||
sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n",
|
||||
"SQLITE_THREADSAFE=",sqlite3_threadsafe());
|
||||
outln("JVM NIO support? ",sqlite3_jni_supports_nio() ? "YES" : "NO");
|
||||
final boolean showLoopCount = (nRepeat>1 && nThread>1);
|
||||
if( showLoopCount ){
|
||||
outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each.");
|
||||
@@ -1925,29 +2169,24 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_shutdown();
|
||||
int nMethods = 0;
|
||||
int nNatives = 0;
|
||||
int nCanonical = 0;
|
||||
final java.lang.reflect.Method[] declaredMethods =
|
||||
SQLite3Jni.class.getDeclaredMethods();
|
||||
CApi.class.getDeclaredMethods();
|
||||
for(java.lang.reflect.Method m : declaredMethods){
|
||||
final int mod = m.getModifiers();
|
||||
if( 0!=(mod & java.lang.reflect.Modifier.STATIC) ){
|
||||
final String name = m.getName();
|
||||
if(name.startsWith("sqlite3_")){
|
||||
++nMethods;
|
||||
if( m.isAnnotationPresent( org.sqlite.jni.annotation.Canonical.class ) ){
|
||||
++nCanonical;
|
||||
}
|
||||
if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
|
||||
++nNatives;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
outln("\tSQLite3Jni.sqlite3_*() methods: "+
|
||||
outln("\tCApi.sqlite3_*() methods: "+
|
||||
nMethods+" total, with "+
|
||||
nNatives+" native, "+
|
||||
(nMethods - nNatives)+" Java, ",
|
||||
nCanonical," @Canonical"
|
||||
(nMethods - nNatives)+" Java"
|
||||
);
|
||||
outln("\tTotal test time = "
|
||||
+(timeEnd - timeStart)+"ms");
|
||||
+2
-2
@@ -11,11 +11,11 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
import org.sqlite.jni.annotation.Nullable;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_trace_v2}.
|
||||
Callback for use with {@link CApi#sqlite3_trace_v2}.
|
||||
*/
|
||||
public interface TraceV2Callback extends CallbackProxy {
|
||||
/**
|
||||
+4
-3
@@ -11,15 +11,16 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for use with {@link SQLite3Jni#sqlite3_update_hook}.
|
||||
Callback for use with {@link CApi#sqlite3_update_hook}.
|
||||
*/
|
||||
public interface UpdateHookCallback extends CallbackProxy {
|
||||
/**
|
||||
Must function as described for the C-level sqlite3_update_hook()
|
||||
callback.
|
||||
callback. If it throws, the exception is translated into
|
||||
a db-level error.
|
||||
*/
|
||||
void call(int opId, String dbName, String tableName, long rowId);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
** 2023-10-16
|
||||
**
|
||||
** 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 the ValueHolder utility class for the sqlite3
|
||||
** JNI bindings.
|
||||
*/
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A helper class which simply holds a single value. Its primary use
|
||||
is for communicating values out of anonymous classes, as doing so
|
||||
requires a "final" reference, as well as communicating aggregate
|
||||
SQL function state across calls to such functions.
|
||||
*/
|
||||
public class ValueHolder<T> {
|
||||
public T value;
|
||||
public ValueHolder(){}
|
||||
public ValueHolder(T v){value = v;}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
|
||||
/**
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file declares JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
Callback for a hook called by SQLite when certain client-provided
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
This package houses a JNI binding to the SQLite3 C API.
|
||||
|
||||
<p>The primary interfaces are in {@link
|
||||
org.sqlite.jni.SQLite3Jni}.</p>
|
||||
org.sqlite.jni.capi.CApi}.</p>
|
||||
|
||||
<h1>API Goals and Requirements</h1>
|
||||
|
||||
@@ -86,4 +86,4 @@
|
||||
undefined behavior.</p>
|
||||
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
+3
-8
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (sqlite3*) instances with
|
||||
@@ -30,19 +30,14 @@ public final class sqlite3 extends NativePointerHolder<sqlite3>
|
||||
if( 0==ptr ){
|
||||
return sqlite3.class.getSimpleName()+"@null";
|
||||
}
|
||||
final String fn = SQLite3Jni.sqlite3_db_filename(this, "main");
|
||||
final String fn = CApi.sqlite3_db_filename(this, "main");
|
||||
return sqlite3.class.getSimpleName()
|
||||
+"@"+String.format("0x%08x",ptr)
|
||||
+"["+((null == fn) ? "<unnamed>" : fn)+"]"
|
||||
;
|
||||
}
|
||||
|
||||
@Override protected void finalize(){
|
||||
//System.out.println(this+".finalize()");
|
||||
SQLite3Jni.sqlite3_close_v2(this.clearNativePointer());
|
||||
}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_close_v2(this.clearNativePointer());
|
||||
CApi.sqlite3_close_v2(this);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A wrapper for passing C-level (sqlite3_backup*) instances around in
|
||||
@@ -25,7 +25,7 @@ public final class sqlite3_backup extends NativePointerHolder<sqlite3_backup>
|
||||
private sqlite3_backup(){}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_backup_finish(this);
|
||||
CApi.sqlite3_backup_finish(this);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-3
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A wrapper for passing C-level (sqlite3_blob*) instances around in
|
||||
@@ -25,7 +25,6 @@ public final class sqlite3_blob extends NativePointerHolder<sqlite3_blob>
|
||||
private sqlite3_blob(){}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_blob_close(this.clearNativePointer());
|
||||
CApi.sqlite3_blob_close(this);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
sqlite3_context instances are used in conjunction with user-defined
|
||||
@@ -71,7 +71,7 @@ public final class sqlite3_context extends NativePointerHolder<sqlite3_context>
|
||||
*/
|
||||
public synchronized Long getAggregateContext(boolean initIfNeeded){
|
||||
if( aggregateContext==null ){
|
||||
aggregateContext = SQLite3Jni.sqlite3_aggregate_context(this, initIfNeeded);
|
||||
aggregateContext = CApi.sqlite3_aggregate_context(this, initIfNeeded);
|
||||
if( !initIfNeeded && null==aggregateContext ) aggregateContext = 0L;
|
||||
}
|
||||
return (null==aggregateContext || 0!=aggregateContext) ? aggregateContext : null;
|
||||
+2
-7
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (sqlite3_stmt*) instances with
|
||||
@@ -24,12 +24,7 @@ public final class sqlite3_stmt extends NativePointerHolder<sqlite3_stmt>
|
||||
// Only invoked from JNI.
|
||||
private sqlite3_stmt(){}
|
||||
|
||||
//For as-yet-unknown reasons, this triggers a JVM crash.
|
||||
//@Override protected void finalize(){
|
||||
// SQLite3Jni.sqlite3_finalize(this.clearNativePointer());
|
||||
//}
|
||||
|
||||
@Override public void close(){
|
||||
SQLite3Jni.sqlite3_finalize(this.clearNativePointer());
|
||||
CApi.sqlite3_finalize(this);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
package org.sqlite.jni.capi;
|
||||
|
||||
public final class sqlite3_value extends NativePointerHolder<sqlite3_value> {
|
||||
//! Invoked only from JNI.
|
||||
@@ -12,7 +12,7 @@
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.*;
|
||||
import org.sqlite.jni.capi.*;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (Fts5Context*) instances with
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.sqlite.jni.*;
|
||||
import org.sqlite.jni.capi.*;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
|
||||
/**
|
||||
@@ -33,67 +33,51 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
*/
|
||||
public static native Fts5ExtensionApi getInstance();
|
||||
|
||||
@Canonical
|
||||
public native int xColumnCount(@NotNull Fts5Context fcx);
|
||||
|
||||
@Canonical
|
||||
public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.Int32 pnToken);
|
||||
|
||||
@Canonical
|
||||
public native int xColumnText(@NotNull Fts5Context cx, int iCol,
|
||||
@NotNull OutputPointer.String txt);
|
||||
|
||||
@Canonical
|
||||
public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
|
||||
@NotNull OutputPointer.Int64 pnToken);
|
||||
|
||||
@Canonical
|
||||
public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
|
||||
|
||||
@Canonical
|
||||
public native int xInst(@NotNull Fts5Context cx, int iIdx,
|
||||
@NotNull OutputPointer.Int32 piPhrase,
|
||||
@NotNull OutputPointer.Int32 piCol,
|
||||
@NotNull OutputPointer.Int32 piOff);
|
||||
|
||||
@Canonical
|
||||
public native int xInstCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int32 pnInst);
|
||||
|
||||
@Canonical
|
||||
public native int xPhraseCount(@NotNull Fts5Context fcx);
|
||||
|
||||
@Canonical
|
||||
public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
|
||||
@Canonical
|
||||
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
@Canonical
|
||||
public native void xPhraseNext(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
@Canonical
|
||||
public native void xPhraseNextColumn(@NotNull Fts5Context cx,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
@Canonical
|
||||
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
|
||||
|
||||
@Canonical
|
||||
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull XQueryPhraseCallback callback);
|
||||
@Canonical
|
||||
public native int xRowCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int64 nRow);
|
||||
|
||||
@Canonical
|
||||
public native long xRowid(@NotNull Fts5Context cx);
|
||||
/* Note that the JNI binding lacks the C version's xDelete()
|
||||
callback argument. Instead, if pAux has an xDestroy() method, it
|
||||
@@ -102,14 +86,11 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
pAux held by the JNI layer will be relinquished regardless of
|
||||
whether pAux has an xDestroy() method. */
|
||||
|
||||
@Canonical
|
||||
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
|
||||
|
||||
@Canonical
|
||||
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
|
||||
@NotNull XTokenizeCallback callback);
|
||||
|
||||
@Canonical
|
||||
public native Object xUserData(Fts5Context cx);
|
||||
//^^^ returns the pointer passed as the 3rd arg to the C-level
|
||||
// fts5_api::xCreateFunction().
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.NativePointerHolder;
|
||||
import org.sqlite.jni.capi.NativePointerHolder;
|
||||
|
||||
/**
|
||||
A wrapper for C-level Fts5PhraseIter. They are only modified and
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.NativePointerHolder;
|
||||
import org.sqlite.jni.capi.NativePointerHolder;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
+4
-4
@@ -11,10 +11,10 @@
|
||||
*************************************************************************
|
||||
** This file contains a set of tests for the sqlite3 JNI bindings.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
import static org.sqlite.jni.SQLite3Jni.*;
|
||||
import static org.sqlite.jni.Tester1.*;
|
||||
import org.sqlite.jni.*;
|
||||
package org.sqlite.jni.fts5;
|
||||
import static org.sqlite.jni.capi.CApi.*;
|
||||
import static org.sqlite.jni.capi.Tester1.*;
|
||||
import org.sqlite.jni.capi.*;
|
||||
import org.sqlite.jni.fts5.*;
|
||||
|
||||
import java.util.*;
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
import org.sqlite.jni.*;
|
||||
import org.sqlite.jni.capi.*;
|
||||
|
||||
/**
|
||||
A wrapper for communicating C-level (fts5_api*) instances with
|
||||
@@ -33,7 +33,6 @@ public final class fts5_api extends NativePointerHolder<fts5_api> {
|
||||
*/
|
||||
public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
|
||||
|
||||
@Canonical
|
||||
public synchronized native int xCreateFunction(@NotNull String name,
|
||||
@Nullable Object userData,
|
||||
@NotNull fts5_extension_function xFunction);
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.sqlite3_context;
|
||||
import org.sqlite.jni.sqlite3_value;
|
||||
import org.sqlite.jni.capi.sqlite3_context;
|
||||
import org.sqlite.jni.capi.sqlite3_value;
|
||||
|
||||
/**
|
||||
JNI-level wrapper for C's fts5_extension_function type.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import org.sqlite.jni.NativePointerHolder;
|
||||
import org.sqlite.jni.capi.NativePointerHolder;
|
||||
import org.sqlite.jni.annotation.NotNull;
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
** 2023-10-16
|
||||
**
|
||||
** 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 wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
|
||||
/**
|
||||
EXPERIMENTAL/INCOMPLETE/UNTESTED
|
||||
|
||||
A SqlFunction implementation for aggregate functions. The T type
|
||||
represents the type of data accumulated by this aggregate while it
|
||||
works. e.g. a SUM()-like UDF might use Integer or Long and a
|
||||
CONCAT()-like UDF might use a StringBuilder or a List<String>.
|
||||
*/
|
||||
public abstract class AggregateFunction<T> implements SqlFunction {
|
||||
|
||||
/**
|
||||
As for the xStep() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, the
|
||||
exception is reported via sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xStep(SqlFunction.Arguments args);
|
||||
|
||||
/**
|
||||
As for the xFinal() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, it is
|
||||
translated into sqlite3_result_error().
|
||||
|
||||
Note that the passed-in object will not actually contain any
|
||||
arguments for xFinal() but will contain the context object needed
|
||||
for setting the call's result or error state.
|
||||
*/
|
||||
public abstract void xFinal(SqlFunction.Arguments args);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
/**
|
||||
PerContextState assists aggregate and window functions in
|
||||
managing their accumulator state across calls to the UDF's
|
||||
callbacks.
|
||||
|
||||
<p>T must be of a type which can be legally stored as a value in
|
||||
java.util.HashMap<KeyType,T>.
|
||||
|
||||
<p>If a given aggregate or window function is called multiple times
|
||||
in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)...,
|
||||
then the clients need some way of knowing which call is which so
|
||||
that they can map their state between their various UDF callbacks
|
||||
and reset it via xFinal(). This class takes care of such
|
||||
mappings.
|
||||
|
||||
<p>This class works by mapping
|
||||
sqlite3_context.getAggregateContext() to a single piece of
|
||||
state, of a client-defined type (the T part of this class), which
|
||||
persists across a "matching set" of the UDF's callbacks.
|
||||
|
||||
<p>This class is a helper providing commonly-needed functionality
|
||||
- it is not required for use with aggregate or window functions.
|
||||
Client UDFs are free to perform such mappings using custom
|
||||
approaches. The provided {@link AggregateFunction} and {@link
|
||||
WindowFunction} classes use this.
|
||||
*/
|
||||
public static final class PerContextState<T> {
|
||||
private final java.util.Map<Long,ValueHolder<T>> map
|
||||
= new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
Should be called from a UDF's xStep(), xValue(), and xInverse()
|
||||
methods, passing it that method's first argument and an initial
|
||||
value for the persistent state. If there is currently no
|
||||
mapping for the given context within the map, one is created
|
||||
using the given initial value, else the existing one is used
|
||||
and the 2nd argument is ignored. It returns a ValueHolder<T>
|
||||
which can be used to modify that state directly without
|
||||
requiring that the client update the underlying map's entry.
|
||||
|
||||
<p>The caller is obligated to eventually call
|
||||
takeAggregateState() to clear the mapping.
|
||||
*/
|
||||
public ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
|
||||
final Long key = args.getContext().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 with the arguments' aggregate context 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(SqlFunction.Arguments args){
|
||||
final ValueHolder<T> h = map.remove(args.getContext().getAggregateContext(false));
|
||||
return null==h ? null : h.value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Per-invocation state for the UDF. */
|
||||
private final PerContextState<T> map = new PerContextState<>();
|
||||
|
||||
/**
|
||||
To be called from the implementation's xStep() method, as well
|
||||
as the xValue() and xInverse() methods of the {@link WindowFunction}
|
||||
subclass, to fetch the current per-call UDF state. On the
|
||||
first call to this method for any given sqlite3_context
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see SQLFunction.PerContextState#getAggregateState
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(SqlFunction.Arguments args, T initialValue){
|
||||
return map.getAggregateState(args, 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(SqlFunction.Arguments args){
|
||||
return map.takeAggregateState(args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
** 2023-10-16
|
||||
**
|
||||
** 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 wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
import org.sqlite.jni.capi.CApi;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
import org.sqlite.jni.capi.sqlite3_context;
|
||||
import org.sqlite.jni.capi.sqlite3_value;
|
||||
|
||||
/**
|
||||
The SqlFunction type for scalar SQL functions.
|
||||
*/
|
||||
public abstract class ScalarFunction implements SqlFunction {
|
||||
/**
|
||||
As for the xFunc() argument of the C API's
|
||||
sqlite3_create_function(). If this function throws, it is
|
||||
translated into an sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xFunc(SqlFunction.Arguments args);
|
||||
|
||||
/**
|
||||
Optionally override to be notified when the UDF is finalized by
|
||||
SQLite. This default implementation does nothing.
|
||||
*/
|
||||
public void xDestroy() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
** 2023-10-16
|
||||
**
|
||||
** 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 wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
import org.sqlite.jni.capi.CApi;
|
||||
import org.sqlite.jni.capi.sqlite3_context;
|
||||
import org.sqlite.jni.capi.sqlite3_value;
|
||||
|
||||
/**
|
||||
Base marker interface for SQLite's three types of User-Defined SQL
|
||||
Functions (UDFs): Scalar, Aggregate, and Window functions.
|
||||
*/
|
||||
public interface SqlFunction {
|
||||
|
||||
public static final int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC;
|
||||
public static final int INNOCUOUS = CApi.SQLITE_INNOCUOUS;
|
||||
public static final int DIRECTONLY = CApi.SQLITE_DIRECTONLY;
|
||||
public static final int SUBTYPE = CApi.SQLITE_SUBTYPE;
|
||||
public static final int RESULT_SUBTYPE = CApi.SQLITE_RESULT_SUBTYPE;
|
||||
public static final int UTF8 = CApi.SQLITE_UTF8;
|
||||
public static final int UTF16 = CApi.SQLITE_UTF16;
|
||||
|
||||
/**
|
||||
The Arguments type is an abstraction on top of the lower-level
|
||||
UDF function argument types. It provides _most_ of the functionality
|
||||
of the lower-level interface, insofar as possible without "leaking"
|
||||
those types into this API.
|
||||
*/
|
||||
public final static class Arguments implements Iterable<SqlFunction.Arguments.Arg>{
|
||||
private final sqlite3_context cx;
|
||||
private final sqlite3_value args[];
|
||||
public final int length;
|
||||
|
||||
/**
|
||||
Must be passed the context and arguments for the UDF call this
|
||||
object is wrapping. Intended to be used by internal proxy
|
||||
classes which "convert" the lower-level interface into this
|
||||
package's higher-level interface, e.g. ScalarAdapter and
|
||||
AggregateAdapter.
|
||||
|
||||
Passing null for the args is equivalent to passing a length-0
|
||||
array.
|
||||
*/
|
||||
Arguments(sqlite3_context cx, sqlite3_value args[]){
|
||||
this.cx = cx;
|
||||
this.args = args==null ? new sqlite3_value[0] : args;
|
||||
this.length = this.args.length;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the sqlite3_value at the given argument index or throws
|
||||
an IllegalArgumentException exception if ndx is out of range.
|
||||
*/
|
||||
private sqlite3_value valueAt(int ndx){
|
||||
if(ndx<0 || ndx>=args.length){
|
||||
throw new IllegalArgumentException(
|
||||
"SQL function argument index "+ndx+" is out of range."
|
||||
);
|
||||
}
|
||||
return args[ndx];
|
||||
}
|
||||
|
||||
//! Returns the underlying sqlite3_context for these arguments.
|
||||
sqlite3_context getContext(){return cx;}
|
||||
|
||||
/**
|
||||
Returns the Sqlite (db) object associated with this UDF call,
|
||||
or null if the UDF is somehow called without such an object or
|
||||
the db has been closed in an untimely manner (e.g. closed by a
|
||||
UDF call).
|
||||
*/
|
||||
public Sqlite getDb(){
|
||||
return Sqlite.fromNative( CApi.sqlite3_context_db_handle(cx) );
|
||||
}
|
||||
|
||||
public int getArgCount(){ return args.length; }
|
||||
|
||||
public int getInt(int argNdx){return CApi.sqlite3_value_int(valueAt(argNdx));}
|
||||
public long getInt64(int argNdx){return CApi.sqlite3_value_int64(valueAt(argNdx));}
|
||||
public double getDouble(int argNdx){return CApi.sqlite3_value_double(valueAt(argNdx));}
|
||||
public byte[] getBlob(int argNdx){return CApi.sqlite3_value_blob(valueAt(argNdx));}
|
||||
public byte[] getText(int argNdx){return CApi.sqlite3_value_text(valueAt(argNdx));}
|
||||
public String getText16(int argNdx){return CApi.sqlite3_value_text16(valueAt(argNdx));}
|
||||
public int getBytes(int argNdx){return CApi.sqlite3_value_bytes(valueAt(argNdx));}
|
||||
public int getBytes16(int argNdx){return CApi.sqlite3_value_bytes16(valueAt(argNdx));}
|
||||
public Object getObject(int argNdx){return CApi.sqlite3_value_java_object(valueAt(argNdx));}
|
||||
public <T> T getObject(int argNdx, Class<T> type){
|
||||
return CApi.sqlite3_value_java_object(valueAt(argNdx), type);
|
||||
}
|
||||
|
||||
public int getType(int argNdx){return CApi.sqlite3_value_type(valueAt(argNdx));}
|
||||
public int getSubtype(int argNdx){return CApi.sqlite3_value_subtype(valueAt(argNdx));}
|
||||
public int getNumericType(int argNdx){return CApi.sqlite3_value_numeric_type(valueAt(argNdx));}
|
||||
public int getNoChange(int argNdx){return CApi.sqlite3_value_nochange(valueAt(argNdx));}
|
||||
public boolean getFromBind(int argNdx){return CApi.sqlite3_value_frombind(valueAt(argNdx));}
|
||||
public int getEncoding(int argNdx){return CApi.sqlite3_value_encoding(valueAt(argNdx));}
|
||||
|
||||
public void resultInt(int v){ CApi.sqlite3_result_int(cx, v); }
|
||||
public void resultInt64(long v){ CApi.sqlite3_result_int64(cx, v); }
|
||||
public void resultDouble(double v){ CApi.sqlite3_result_double(cx, v); }
|
||||
public void resultError(String msg){CApi.sqlite3_result_error(cx, msg);}
|
||||
public void resultError(Exception e){CApi.sqlite3_result_error(cx, e);}
|
||||
public void resultErrorTooBig(){CApi.sqlite3_result_error_toobig(cx);}
|
||||
public void resultErrorCode(int rc){CApi.sqlite3_result_error_code(cx, rc);}
|
||||
public void resultObject(Object o){CApi.sqlite3_result_java_object(cx, o);}
|
||||
public void resultNull(){CApi.sqlite3_result_null(cx);}
|
||||
/**
|
||||
Analog to sqlite3_result_value(), using the Value object at the
|
||||
given argument index.
|
||||
*/
|
||||
public void resultArg(int argNdx){CApi.sqlite3_result_value(cx, valueAt(argNdx));}
|
||||
public void resultSubtype(int subtype){CApi.sqlite3_result_subtype(cx, subtype);}
|
||||
public void resultZeroBlob(long n){
|
||||
// Throw on error? If n is too big,
|
||||
// sqlite3_result_error_toobig() is automatically called.
|
||||
CApi.sqlite3_result_zeroblob64(cx, n);
|
||||
}
|
||||
|
||||
public void resultBlob(byte[] blob){CApi.sqlite3_result_blob(cx, blob);}
|
||||
public void resultText(byte[] utf8){CApi.sqlite3_result_text(cx, utf8);}
|
||||
public void resultText(String txt){CApi.sqlite3_result_text(cx, txt);}
|
||||
public void resultText16(byte[] utf16){CApi.sqlite3_result_text16(cx, utf16);}
|
||||
public void resultText16(String txt){CApi.sqlite3_result_text16(cx, txt);}
|
||||
|
||||
/**
|
||||
Callbacks should invoke this on OOM errors, instead of throwing
|
||||
OutOfMemoryError, because the latter cannot be propagated
|
||||
through the C API.
|
||||
*/
|
||||
public void resultNoMem(){CApi.sqlite3_result_error_nomem(cx);}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_set_auxdata() but throws if argNdx is out of
|
||||
range.
|
||||
*/
|
||||
public void setAuxData(int argNdx, Object o){
|
||||
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
|
||||
|
||||
The value of the N parameter to these interfaces should be
|
||||
non-negative. Future enhancements may make use of negative N
|
||||
values to define new kinds of function caching behavior.
|
||||
*/
|
||||
valueAt(argNdx);
|
||||
CApi.sqlite3_set_auxdata(cx, argNdx, o);
|
||||
}
|
||||
|
||||
/**
|
||||
Analog to sqlite3_get_auxdata() but throws if argNdx is out of
|
||||
range.
|
||||
*/
|
||||
public Object getAuxData(int argNdx){
|
||||
valueAt(argNdx);
|
||||
return CApi.sqlite3_get_auxdata(cx, argNdx);
|
||||
}
|
||||
|
||||
/**
|
||||
Represents a single SqlFunction argument. Primarily intended
|
||||
for use with the Arguments class's Iterable interface.
|
||||
*/
|
||||
public final static class Arg {
|
||||
private final Arguments a;
|
||||
private final int ndx;
|
||||
/* Only for use by the Arguments class. */
|
||||
private Arg(Arguments a, int ndx){
|
||||
this.a = a;
|
||||
this.ndx = ndx;
|
||||
}
|
||||
/** Returns this argument's index in its parent argument list. */
|
||||
public int getIndex(){return ndx;}
|
||||
public int getInt(){return a.getInt(ndx);}
|
||||
public long getInt64(){return a.getInt64(ndx);}
|
||||
public double getDouble(){return a.getDouble(ndx);}
|
||||
public byte[] getBlob(){return a.getBlob(ndx);}
|
||||
public byte[] getText(){return a.getText(ndx);}
|
||||
public String getText16(){return a.getText16(ndx);}
|
||||
public int getBytes(){return a.getBytes(ndx);}
|
||||
public int getBytes16(){return a.getBytes16(ndx);}
|
||||
public Object getObject(){return a.getObject(ndx);}
|
||||
public <T> T getObject(Class<T> type){ return a.getObject(ndx, type); }
|
||||
public int getType(){return a.getType(ndx);}
|
||||
public Object getAuxData(){return a.getAuxData(ndx);}
|
||||
public void setAuxData(Object o){a.setAuxData(ndx, o);}
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.Iterator<SqlFunction.Arguments.Arg> iterator(){
|
||||
final Arg[] proxies = new Arg[args.length];
|
||||
for( int i = 0; i < args.length; ++i ){
|
||||
proxies[i] = new Arg(this, i);
|
||||
}
|
||||
return java.util.Arrays.stream(proxies).iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Internal-use adapter for wrapping this package's ScalarFunction
|
||||
for use with the org.sqlite.jni.capi.ScalarFunction interface.
|
||||
*/
|
||||
static final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction {
|
||||
private final ScalarFunction impl;
|
||||
ScalarAdapter(ScalarFunction impl){
|
||||
this.impl = impl;
|
||||
}
|
||||
/**
|
||||
Proxies this.impl.xFunc(), adapting the call arguments to that
|
||||
function's signature. If the proxy throws, it's translated to
|
||||
sqlite_result_error() with the exception's message.
|
||||
*/
|
||||
public void xFunc(sqlite3_context cx, sqlite3_value[] args){
|
||||
try{
|
||||
impl.xFunc( new SqlFunction.Arguments(cx, args) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void xDestroy(){
|
||||
impl.xDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Internal-use adapter for wrapping this package's AggregateFunction
|
||||
for use with the org.sqlite.jni.capi.AggregateFunction interface.
|
||||
*/
|
||||
static /*cannot be final without duplicating the whole body in WindowAdapter*/
|
||||
class AggregateAdapter extends org.sqlite.jni.capi.AggregateFunction {
|
||||
private final AggregateFunction impl;
|
||||
AggregateAdapter(AggregateFunction impl){
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
Proxies this.impl.xStep(), adapting the call arguments to that
|
||||
function's signature. If the proxied function throws, it is
|
||||
translated to sqlite_result_error() with the exception's
|
||||
message.
|
||||
*/
|
||||
public void xStep(sqlite3_context cx, sqlite3_value[] args){
|
||||
try{
|
||||
impl.xStep( new SqlFunction.Arguments(cx, args) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
As for the xFinal() argument of the C API's
|
||||
sqlite3_create_function(). If the proxied function throws, it
|
||||
is translated into a sqlite3_result_error().
|
||||
*/
|
||||
public void xFinal(sqlite3_context cx){
|
||||
try{
|
||||
impl.xFinal( new SqlFunction.Arguments(cx, null) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void xDestroy(){
|
||||
impl.xDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Internal-use adapter for wrapping this package's WindowFunction
|
||||
for use with the org.sqlite.jni.capi.WindowFunction interface.
|
||||
*/
|
||||
static final class WindowAdapter extends AggregateAdapter {
|
||||
private final WindowFunction impl;
|
||||
WindowAdapter(WindowFunction impl){
|
||||
super(impl);
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
Proxies this.impl.xInverse(), adapting the call arguments to that
|
||||
function's signature. If the proxied function throws, it is
|
||||
translated to sqlite_result_error() with the exception's
|
||||
message.
|
||||
*/
|
||||
public void xInverse(sqlite3_context cx, sqlite3_value[] args){
|
||||
try{
|
||||
impl.xInverse( new SqlFunction.Arguments(cx, args) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's sqlite3_create_window_function().
|
||||
If the proxied function throws, it is translated into a sqlite3_result_error().
|
||||
*/
|
||||
public void xValue(sqlite3_context cx){
|
||||
try{
|
||||
impl.xValue( new SqlFunction.Arguments(cx, null) );
|
||||
}catch(Exception e){
|
||||
CApi.sqlite3_result_error(cx, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void xDestroy(){
|
||||
impl.xDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
** 2023-10-09
|
||||
**
|
||||
** 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 wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
import org.sqlite.jni.capi.CApi;
|
||||
import org.sqlite.jni.capi.sqlite3;
|
||||
|
||||
/**
|
||||
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 SqliteException extends java.lang.RuntimeException {
|
||||
private int errCode = CApi.SQLITE_ERROR;
|
||||
private int xerrCode = CApi.SQLITE_ERROR;
|
||||
private int errOffset = -1;
|
||||
private int sysErrno = 0;
|
||||
|
||||
/**
|
||||
Records the given error string and uses SQLITE_ERROR for both the
|
||||
error code and extended error code.
|
||||
*/
|
||||
public SqliteException(String msg){
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
Uses sqlite3_errstr(sqlite3ResultCode) for the error string and
|
||||
sets both the error code and extended error code to the given
|
||||
value. This approach includes no database-level information and
|
||||
systemErrno() will be 0, so is intended only for use with sqlite3
|
||||
APIs for which a result code is not an error but which the
|
||||
higher-level wrapper should treat as one.
|
||||
*/
|
||||
public SqliteException(int sqlite3ResultCode){
|
||||
super(CApi.sqlite3_errstr(sqlite3ResultCode));
|
||||
errCode = xerrCode = sqlite3ResultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
Records the current error state of db (which must not be null and
|
||||
must refer to an opened db object). Note that this does not close
|
||||
the db.
|
||||
|
||||
Design note: closing the db on error is really only useful during
|
||||
a failed db-open operation, and the place(s) where that can
|
||||
happen are inside this library, not client-level code.
|
||||
*/
|
||||
SqliteException(sqlite3 db){
|
||||
super(CApi.sqlite3_errmsg(db));
|
||||
errCode = CApi.sqlite3_errcode(db);
|
||||
xerrCode = CApi.sqlite3_extended_errcode(db);
|
||||
errOffset = CApi.sqlite3_error_offset(db);
|
||||
sysErrno = CApi.sqlite3_system_errno(db);
|
||||
}
|
||||
|
||||
/**
|
||||
Records the current error state of db (which must not be null and
|
||||
must refer to an open database).
|
||||
*/
|
||||
public SqliteException(Sqlite db){
|
||||
this(db.nativeHandle());
|
||||
}
|
||||
|
||||
public SqliteException(Sqlite.Stmt stmt){
|
||||
this(stmt.getDb());
|
||||
}
|
||||
|
||||
public int errcode(){ return errCode; }
|
||||
public int extendedErrcode(){ return xerrCode; }
|
||||
public int errorOffset(){ return errOffset; }
|
||||
public int systemErrno(){ return sysErrno; }
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** 2023-10-16
|
||||
**
|
||||
** 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 the ValueHolder utility class.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
|
||||
/**
|
||||
A helper class which simply holds a single value. Its primary use
|
||||
is for communicating values out of anonymous callbacks, as doing so
|
||||
requires a "final" reference.
|
||||
*/
|
||||
public class ValueHolder<T> {
|
||||
public T value;
|
||||
public ValueHolder(){}
|
||||
public ValueHolder(T v){value = v;}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
** 2023-10-16
|
||||
**
|
||||
** 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 wrapper1 interface for sqlite3.
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
|
||||
/**
|
||||
A SqlFunction implementation for window functions. The T type
|
||||
represents the type of data accumulated by this function while it
|
||||
works. e.g. a SUM()-like UDF might use Integer or Long and a
|
||||
CONCAT()-like UDF might use a StringBuilder or a List<String>.
|
||||
*/
|
||||
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 reported via sqlite3_result_error().
|
||||
*/
|
||||
public abstract void xInverse(SqlFunction.Arguments args);
|
||||
|
||||
/**
|
||||
As for the xValue() argument of the C API's
|
||||
sqlite3_create_window_function(). If this function throws, it is
|
||||
translated into sqlite3_result_error().
|
||||
|
||||
Note that the passed-in object will not actually contain any
|
||||
arguments for xValue() but will contain the context object needed
|
||||
for setting the call's result or error state.
|
||||
*/
|
||||
public abstract void xValue(SqlFunction.Arguments args);
|
||||
|
||||
}
|
||||
+4
-3
@@ -38,7 +38,7 @@ int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){
|
||||
int rc = SQLITE_OK;
|
||||
char *zSql = 0;
|
||||
int pgsz = 0;
|
||||
int nTotal = 0;
|
||||
unsigned int nTotal = 0;
|
||||
|
||||
if( 0==sqlite3_get_autocommit(db) ) return SQLITE_MISUSE;
|
||||
|
||||
@@ -86,8 +86,8 @@ int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){
|
||||
rc = p->xFetch(pFd, pgsz*iPg, pgsz, (void**)&pMap);
|
||||
if( rc!=SQLITE_OK || pMap==0 ) break;
|
||||
|
||||
nTotal += pMap[0];
|
||||
nTotal += pMap[pgsz-1];
|
||||
nTotal += (unsigned int)pMap[0];
|
||||
nTotal += (unsigned int)pMap[pgsz-1];
|
||||
|
||||
rc = p->xUnfetch(pFd, pgsz*iPg, (void*)pMap);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
@@ -103,5 +103,6 @@ int sqlite3_mmap_warm(sqlite3 *db, const char *zDb){
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
|
||||
(void)nTotal;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ SQLITE_EXTENSION_INIT1
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
@@ -1252,6 +1252,7 @@ static int geopolyInit(
|
||||
(void)pAux;
|
||||
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
|
||||
/* Allocate the sqlite3_vtab structure */
|
||||
nDb = strlen(argv[1]);
|
||||
|
||||
+14
-16
@@ -3332,7 +3332,7 @@ static int rtreeShadowName(const char *zName){
|
||||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static int rtreeIntegrity(sqlite3_vtab*, char**);
|
||||
static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**);
|
||||
|
||||
static sqlite3_module rtreeModule = {
|
||||
4, /* iVersion */
|
||||
@@ -3615,6 +3615,8 @@ static int rtreeInit(
|
||||
}
|
||||
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
|
||||
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
|
||||
|
||||
|
||||
/* Allocate the sqlite3_vtab structure */
|
||||
nDb = (int)strlen(argv[1]);
|
||||
@@ -4130,7 +4132,6 @@ static int rtreeCheckTable(
|
||||
){
|
||||
RtreeCheck check; /* Common context for various routines */
|
||||
sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
|
||||
int bEnd = 0; /* True if transaction should be closed */
|
||||
int nAux = 0; /* Number of extra columns. */
|
||||
|
||||
/* Initialize the context object */
|
||||
@@ -4139,14 +4140,6 @@ static int rtreeCheckTable(
|
||||
check.zDb = zDb;
|
||||
check.zTab = zTab;
|
||||
|
||||
/* If there is not already an open transaction, open one now. This is
|
||||
** to ensure that the queries run as part of this integrity-check operate
|
||||
** on a consistent snapshot. */
|
||||
if( sqlite3_get_autocommit(db) ){
|
||||
check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
bEnd = 1;
|
||||
}
|
||||
|
||||
/* Find the number of auxiliary columns */
|
||||
if( check.rc==SQLITE_OK ){
|
||||
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
|
||||
@@ -4187,11 +4180,6 @@ static int rtreeCheckTable(
|
||||
sqlite3_finalize(check.aCheckMapping[0]);
|
||||
sqlite3_finalize(check.aCheckMapping[1]);
|
||||
|
||||
/* If one was opened, close the transaction */
|
||||
if( bEnd ){
|
||||
int rc = sqlite3_exec(db, "END", 0, 0, 0);
|
||||
if( check.rc==SQLITE_OK ) check.rc = rc;
|
||||
}
|
||||
*pzReport = check.zReport;
|
||||
return check.rc;
|
||||
}
|
||||
@@ -4199,9 +4187,19 @@ static int rtreeCheckTable(
|
||||
/*
|
||||
** Implementation of the xIntegrity method for Rtree.
|
||||
*/
|
||||
static int rtreeIntegrity(sqlite3_vtab *pVtab, char **pzErr){
|
||||
static int rtreeIntegrity(
|
||||
sqlite3_vtab *pVtab, /* The virtual table to check */
|
||||
const char *zSchema, /* Schema in which the virtual table lives */
|
||||
const char *zName, /* Name of the virtual table */
|
||||
int isQuick, /* True for a quick_check */
|
||||
char **pzErr /* Write results here */
|
||||
){
|
||||
Rtree *pRtree = (Rtree*)pVtab;
|
||||
int rc;
|
||||
assert( pzErr!=0 && *pzErr==0 );
|
||||
UNUSED_PARAMETER(zSchema);
|
||||
UNUSED_PARAMETER(zName);
|
||||
UNUSED_PARAMETER(isQuick);
|
||||
rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
|
||||
if( rc==SQLITE_OK && *pzErr ){
|
||||
*pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
|
||||
|
||||
+21
-8
@@ -774,14 +774,27 @@ do_execsql_test 21.1 {
|
||||
# Round-off error associated with using large integer constraints on
|
||||
# a rtree search.
|
||||
#
|
||||
if {$tcl_platform(machine)!="i686" || $tcl_platform(os)!="Linux"} {
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree ( id, x0, x1 );
|
||||
INSERT INTO t1 VALUES (123, 9223372036854775799, 9223372036854775800);
|
||||
SELECT id FROM t1 WHERE x0 > 9223372036854775807;
|
||||
} {123}
|
||||
do_execsql_test 22.1 {
|
||||
SELECT id, x0 > 9223372036854775807 AS 'a0' FROM t1;
|
||||
} {123 1}
|
||||
}
|
||||
|
||||
# 2023-10-14 dbsqlfuzz --sql-fuzz find. rtreecheck() should not call
|
||||
# BEGIN/COMMIT because that causes problems with statement transactions,
|
||||
# and it is unnecessary.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 22.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree ( id, x0, x1 );
|
||||
INSERT INTO t1 VALUES (123, 9223372036854775799, 9223372036854775800);
|
||||
SELECT id FROM t1 WHERE x0 > 9223372036854775807;
|
||||
} {123}
|
||||
do_execsql_test 22.1 {
|
||||
SELECT id, x0 > 9223372036854775807 AS 'a0' FROM t1;
|
||||
} {123 1}
|
||||
do_test 23.0 {
|
||||
db eval {CREATE TABLE t1(a,b,c);}
|
||||
catch {db eval {CREATE TABLE t2 AS SELECT rtreecheck('t1') AS y;}}
|
||||
db eval {PRAGMA integrity_check;}
|
||||
} {ok}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -66,6 +66,7 @@ do_execsql_test -db db2 1.4 {
|
||||
2 four 1234
|
||||
}
|
||||
|
||||
db2 close
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -189,44 +190,49 @@ foreach {tn sql1 at sql2} {
|
||||
INSERT INTO t4(x, y, c) VALUES(9, 10, 11);
|
||||
}
|
||||
} {
|
||||
db_restore_and_reopen
|
||||
|
||||
set C1 [changeset_from_sql $sql1]
|
||||
execsql $at
|
||||
set C2 [changeset_from_sql $sql2]
|
||||
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp add $C1
|
||||
grp add $C2
|
||||
set T1 [grp output]
|
||||
grp delete
|
||||
|
||||
db_restore_and_reopen
|
||||
execsql $at
|
||||
set T2 [changeset_from_sql "$sql1 ; $sql2"]
|
||||
|
||||
if {[string range $tn end end]!="*"} {
|
||||
do_test 3.1.$tn.1 { changeset_to_list $T1 } [changeset_to_list $T2]
|
||||
} else {
|
||||
set tn [string range $tn 0 end-1]
|
||||
foreach {tn2 cmd} {
|
||||
1 changeset_from_sql
|
||||
2 patchset_from_sql
|
||||
} {
|
||||
db_restore_and_reopen
|
||||
|
||||
set C1 [$cmd $sql1]
|
||||
execsql $at
|
||||
set C2 [$cmd $sql2]
|
||||
|
||||
sqlite3changegroup grp
|
||||
grp schema db main
|
||||
grp add $C1
|
||||
grp add $C2
|
||||
set T1 [grp output]
|
||||
grp delete
|
||||
|
||||
db_restore_and_reopen
|
||||
execsql $at
|
||||
set T2 [$cmd "$sql1 ; $sql2"]
|
||||
|
||||
if {[string range $tn end end]!="*"} {
|
||||
do_test 3.1.$tn.$tn2.1 { changeset_to_list $T1 } [changeset_to_list $T2]
|
||||
set testname "$tn.$tn2"
|
||||
} else {
|
||||
set testname "[string range $tn 0 end-1].$tn2"
|
||||
}
|
||||
|
||||
db_restore_and_reopen
|
||||
proc xConflict {args} { return "REPLACE" }
|
||||
sqlite3changeset_apply_v2 db $T1 xConflict
|
||||
set S1 [scksum db main]
|
||||
|
||||
db_restore_and_reopen
|
||||
sqlite3changeset_apply_v2 db $T2 xConflict
|
||||
set S2 [scksum db main]
|
||||
|
||||
# if { $tn==7 } { puts [changeset_to_list $T1] }
|
||||
|
||||
do_test 3.1.$tn.2 { set S1 } $S2
|
||||
}
|
||||
|
||||
db_restore_and_reopen
|
||||
proc xConflict {args} { return "REPLACE" }
|
||||
sqlite3changeset_apply_v2 db $T1 xConflict
|
||||
set S1 [scksum db main]
|
||||
|
||||
db_restore_and_reopen
|
||||
sqlite3changeset_apply_v2 db $T2 xConflict
|
||||
set S2 [scksum db main]
|
||||
|
||||
# if { $tn==7 } { puts [changeset_to_list $T1] }
|
||||
|
||||
do_test 3.1.$tn.2 { set S1 } $S2
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# 2023 October 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 implements regression tests for SQLite library.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionnoact
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE p1(a INTEGER PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO p1 VALUES(1, 1, 'one');
|
||||
INSERT INTO p1 VALUES(2, 2, 'two');
|
||||
INSERT INTO p1 VALUES(3, 3, 'three');
|
||||
INSERT INTO p1 VALUES(4, 4, 'four');
|
||||
}
|
||||
|
||||
db_save
|
||||
|
||||
set C [changeset_from_sql {
|
||||
DELETE FROM p1 WHERE a=2;
|
||||
UPDATE p1 SET c='six' WHERE a=3;
|
||||
INSERT INTO p1 VALUES(5, 5, 'two');
|
||||
INSERT INTO p1 VALUES(6, 6, 'three');
|
||||
}]
|
||||
|
||||
db_restore_and_reopen
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE c1(x INTEGER PRIMARY KEY, y,
|
||||
FOREIGN KEY(y) REFERENCES p1(c) ON DELETE CASCADE ON UPDATE SET NULL
|
||||
);
|
||||
|
||||
INSERT INTO c1 VALUES(10, 'one');
|
||||
INSERT INTO c1 VALUES(20, 'two');
|
||||
INSERT INTO c1 VALUES(30, 'three');
|
||||
INSERT INTO c1 VALUES(40, 'four');
|
||||
}
|
||||
|
||||
db_save
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
PRAGMA foreign_keys = 1;
|
||||
}
|
||||
|
||||
set ::nConflict 0
|
||||
proc conflict {args} {
|
||||
incr ::nConflict
|
||||
return "OMIT"
|
||||
}
|
||||
|
||||
sqlite3changeset_apply_v2 db $C conflict
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM c1
|
||||
} {
|
||||
10 one
|
||||
30 {}
|
||||
40 four
|
||||
}
|
||||
|
||||
db_restore_and_reopen
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
PRAGMA foreign_keys = 1;
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
UPDATE p1 SET c=12345 WHERE a = 45;
|
||||
}
|
||||
|
||||
sqlite3changeset_apply_v2 -noaction db $C conflict
|
||||
do_execsql_test 1.6 {
|
||||
SELECT * FROM c1
|
||||
} {
|
||||
10 one
|
||||
20 two
|
||||
30 three
|
||||
40 four
|
||||
}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
PRAGMA foreign_keys = 1;
|
||||
UPDATE p1 SET c = 'ten' WHERE c='two';
|
||||
SELECT * FROM c1;
|
||||
} {
|
||||
10 one
|
||||
20 {}
|
||||
30 three
|
||||
40 four
|
||||
}
|
||||
|
||||
do_execsql_test 1.8 {
|
||||
PRAGMA foreign_key_check
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -603,8 +603,10 @@ static int sessionPreupdateHash(
|
||||
** the type byte).
|
||||
*/
|
||||
static int sessionSerialLen(const u8 *a){
|
||||
int e = *a;
|
||||
int e;
|
||||
int n;
|
||||
assert( a!=0 );
|
||||
e = *a;
|
||||
if( e==0 || e==0xFF ) return 1;
|
||||
if( e==SQLITE_NULL ) return 1;
|
||||
if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
|
||||
@@ -1266,7 +1268,7 @@ static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_free(azCol);
|
||||
sqlite3_free((char*)azCol);
|
||||
return pSession->rc;
|
||||
}
|
||||
|
||||
@@ -5322,11 +5324,24 @@ int sqlite3changeset_apply_v2(
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
|
||||
u64 savedFlag = db->flags & SQLITE_FkNoAction;
|
||||
|
||||
if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
|
||||
db->flags |= ((u64)SQLITE_FkNoAction);
|
||||
db->aDb[0].pSchema->schema_cookie -= 32;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
|
||||
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
|
||||
assert( db->flags & SQLITE_FkNoAction );
|
||||
db->flags &= ~((u64)SQLITE_FkNoAction);
|
||||
db->aDb[0].pSchema->schema_cookie -= 32;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -5437,6 +5452,7 @@ static int sessionChangeMerge(
|
||||
){
|
||||
SessionChange *pNew = 0;
|
||||
int rc = SQLITE_OK;
|
||||
assert( aRec!=0 );
|
||||
|
||||
if( !pExist ){
|
||||
pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec);
|
||||
@@ -5684,11 +5700,11 @@ static int sessionChangesetExtendRecord(
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
}else if( op==SQLITE_UPDATE ){
|
||||
/* Append missing "undefined" entries to the old.* record. And, if this
|
||||
** is an UPDATE, to the new.* record as well. */
|
||||
int iOff = 0;
|
||||
if( op==SQLITE_UPDATE ){
|
||||
if( pGrp->bPatch==0 ){
|
||||
for(ii=0; ii<nCol; ii++){
|
||||
iOff += sessionSerialLen(&aRec[iOff]);
|
||||
}
|
||||
@@ -5702,6 +5718,9 @@ static int sessionChangesetExtendRecord(
|
||||
for(ii=0; ii<(pTab->nCol-nCol); ii++){
|
||||
sessionAppendByte(pOut, 0x00, &rc);
|
||||
}
|
||||
}else{
|
||||
assert( op==SQLITE_DELETE && pGrp->bPatch );
|
||||
sessionAppendBlob(pOut, aRec, nRec, &rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -1318,10 +1318,17 @@ int sqlite3changeset_apply_v2(
|
||||
** <li>an insert change if all fields of the conflicting row match
|
||||
** the row being inserted.
|
||||
** </ul>
|
||||
**
|
||||
** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
|
||||
** If this flag it set, then all foreign key constraints in the target
|
||||
** database behave as if they were declared with "ON UPDATE NO ACTION ON
|
||||
** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
|
||||
** or SET DEFAULT.
|
||||
*/
|
||||
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
||||
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
||||
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
|
||||
#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constants Passed To The Conflict Handler
|
||||
|
||||
@@ -392,7 +392,6 @@ static int SQLITE_TCLAPI test_session_cmd(
|
||||
};
|
||||
size_t sz = sizeof(aOpt[0]);
|
||||
|
||||
int rc;
|
||||
int iArg;
|
||||
int iOpt;
|
||||
if( Tcl_GetIndexFromObjStruct(interp,objv[2],aOpt,sz,"option",0,&iOpt) ){
|
||||
@@ -812,9 +811,12 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
while( objc>1 ){
|
||||
const char *z1 = Tcl_GetString(objv[1]);
|
||||
int n = strlen(z1);
|
||||
if( n>1 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
|
||||
if( n>3 && n<=12 && 0==sqlite3_strnicmp("-nosavepoint", z1, n) ){
|
||||
flags |= SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
|
||||
}
|
||||
else if( n>3 && n<=9 && 0==sqlite3_strnicmp("-noaction", z1, n) ){
|
||||
flags |= SQLITE_CHANGESETAPPLY_FKNOACTION;
|
||||
}
|
||||
else if( n>2 && n<=7 && 0==sqlite3_strnicmp("-invert", z1, n) ){
|
||||
flags |= SQLITE_CHANGESETAPPLY_INVERT;
|
||||
}
|
||||
@@ -1529,7 +1531,7 @@ static int SQLITE_TCLAPI test_changegroup_cmd(
|
||||
|
||||
case 2: { /* output */
|
||||
int nByte = 0;
|
||||
const u8 *aByte = 0;
|
||||
u8 *aByte = 0;
|
||||
rc = sqlite3changegroup_output(p->pGrp, &nByte, (void**)&aByte);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = test_session_error(interp, rc, 0);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user