Compare commits
397 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dbfdbcdeb7 | |||
| 96be4a368b | |||
| fe263ce3a6 | |||
| 1f4c281b0b | |||
| 1cf61ce636 | |||
| cd552cf99f | |||
| 8c30f16a2e | |||
| f681a9a7d8 | |||
| 380ef6006b | |||
| a0e569d5ba | |||
| 46c7568db5 | |||
| 210b182b79 | |||
| 1e687a4187 | |||
| 67695a30ea | |||
| 3e616d3445 | |||
| d38777134b | |||
| df23b518da | |||
| 7957c70947 | |||
| b79848edd4 | |||
| 1560d52f82 | |||
| f402509b62 | |||
| bac6793a8a | |||
| 7423ecb0a9 | |||
| 7bcb650f57 | |||
| 6d7be8293e | |||
| 5a6f651397 | |||
| d649d7b90c | |||
| 095d399e7c | |||
| fdd37e9637 | |||
| 2445ee0e37 | |||
| ea8f9708c5 | |||
| bcd14a0a3e | |||
| c585e03a4b | |||
| 1f436ad563 | |||
| daa4cdf84a | |||
| bae748a76e | |||
| 74cc109780 | |||
| 01a2953350 | |||
| cf05295a85 | |||
| 91db1c413b | |||
| 10687e283a | |||
| 074b1aa42d | |||
| 522ce56734 | |||
| 68b74e2c42 | |||
| e5a7b815af | |||
| f98148be9c | |||
| 27e13f4f3a | |||
| 3a97a1ba8c | |||
| 81e9bbf8da | |||
| 7d20c2e8a3 | |||
| 192d0ff8cc | |||
| 97e6368abc | |||
| 1c11aa3da2 | |||
| bf5f1f5172 | |||
| 194f0bada7 | |||
| 08f8111bd8 | |||
| f8addcf937 | |||
| bcfe38b96a | |||
| 1b8f6558f8 | |||
| 1f68085849 | |||
| c493e4516a | |||
| 69b5674b40 | |||
| 0eca9db776 | |||
| c066f2af9c | |||
| 625d0b70fe | |||
| 45bff0ea64 | |||
| 07b483c48e | |||
| 6fd9469a32 | |||
| 33c2e37f98 | |||
| 2ba5a93e8f | |||
| 6276637075 | |||
| 8663a78ec9 | |||
| aa12f0027b | |||
| 9a13a21223 | |||
| 9b91aac83b | |||
| acd1d93497 | |||
| 449b34571e | |||
| 9615cf93c8 | |||
| 925b79039c | |||
| 7fc6e6a272 | |||
| 8c8443a38a | |||
| 44a03671c4 | |||
| 0a5508aeb4 | |||
| 4fe1ac8fe1 | |||
| 24d290e7b0 | |||
| 8504d37b99 | |||
| 4aacd1ef8e | |||
| 0840460689 | |||
| 240319decd | |||
| 787f4c3f09 | |||
| 4d453cc2ed | |||
| c701d17366 | |||
| 8bc112e3e4 | |||
| 27408ab9c9 | |||
| d9eae6b75a | |||
| 45bc9b9f97 | |||
| e33ea17d24 | |||
| a12e92d2f7 | |||
| 478dfc7e74 | |||
| 1b62720fa8 | |||
| 1e3a862b79 | |||
| bd05edd98f | |||
| a24a397b8b | |||
| 0e6e05d4d5 | |||
| c525e6e817 | |||
| 1ff6f19d8b | |||
| bfb4993364 | |||
| debc8f7bb7 | |||
| aa54d7a0ca | |||
| eb27359e5e | |||
| 33f3273705 | |||
| 216676664d | |||
| 6245e5a46b | |||
| caf0473652 | |||
| 2427ce16d9 | |||
| 46bfcc1a21 | |||
| 5e71497404 | |||
| 526399b0fd | |||
| e1910ed1d2 | |||
| d82c6a2cf7 | |||
| 3d21dcc924 | |||
| ba56f7020d | |||
| 99f1aa03fb | |||
| 1bd6b415e9 | |||
| 2752d1331d | |||
| 280559b446 | |||
| bbcf035954 | |||
| e2e81e6983 | |||
| b8c81790b2 | |||
| e24f20a4f5 | |||
| f0991c416b | |||
| 311d73efc2 | |||
| c52e9d97d4 | |||
| fbf1c0526e | |||
| e77e589a35 | |||
| 981022b7ff | |||
| a67c71224f | |||
| 66cd200ede | |||
| 513fff88c4 | |||
| fe182f74b2 | |||
| b1560be826 | |||
| 28789029ee | |||
| f453e8d84e | |||
| 4c1c400d64 | |||
| 87c807c6dd | |||
| b5aa9593ac | |||
| 5806a922ba | |||
| 427d2cd57d | |||
| db37e6097c | |||
| 6fcb7f22a1 | |||
| 2f7f948fa7 | |||
| 0cdde5b44f | |||
| a0c6de56ba | |||
| b68d63158a | |||
| 59de42763f | |||
| 37794b4ad0 | |||
| 397b82cf0f | |||
| 92d1bec584 | |||
| a5c484b432 | |||
| 64159096b8 | |||
| ab9c91ae82 | |||
| bcf25e7129 | |||
| 2be0a700ff | |||
| 3a26f72e20 | |||
| d961d49971 | |||
| 9a9140ba59 | |||
| 12bd8ea09a | |||
| 1e9baaf743 | |||
| df8aa3745a | |||
| 110055cd6c | |||
| 79a746b1f2 | |||
| 9ebf9d358b | |||
| e68aa0e44b | |||
| 6e1c2f0483 | |||
| 1ea6a53762 | |||
| 86d9c2d9f3 | |||
| d3a13f7d25 | |||
| 6ed5aa4b9b | |||
| 67f708298f | |||
| 792d1d1b6d | |||
| 6a5e74cbf0 | |||
| 23e59b34e9 | |||
| a09a4fbac9 | |||
| 6a23ff5a07 | |||
| 5706b316c3 | |||
| 90ba0d4995 | |||
| f091f423ad | |||
| 4c323ba4be | |||
| 368c549044 | |||
| b1929b7117 | |||
| 6facd9566d | |||
| 26370358b6 | |||
| a29b138692 | |||
| 1d151e6d25 | |||
| df724c893a | |||
| 140748f949 | |||
| 2bd9f69d40 | |||
| 333b07b18d | |||
| 9269b212bf | |||
| 69ce758efa | |||
| 342ef63e63 | |||
| cfee69fbe4 | |||
| eb9a145711 | |||
| d84bbac8be | |||
| a3fcd7c79e | |||
| 8658a8df59 | |||
| 7590bfd7fc | |||
| d4c224b833 | |||
| f18bf8997b | |||
| c0190101d1 | |||
| 977b0f8813 | |||
| 0d3e5ca28e | |||
| 51e3f855b8 | |||
| 5248693924 | |||
| 420233e375 | |||
| 1ff4233f90 | |||
| 10206572b6 | |||
| b504aab848 | |||
| d930d7ef14 | |||
| ded1959120 | |||
| dd16539e71 | |||
| 88ed38ca6d | |||
| f42ceb9075 | |||
| 7ac10ec8b5 | |||
| 8ae57fab64 | |||
| b65326f51a | |||
| 0148820fd7 | |||
| d2b230a488 | |||
| f429845a60 | |||
| b163f66de0 | |||
| 3f52361352 | |||
| b3c1884b65 | |||
| b3bcf489a4 | |||
| f08f71a21d | |||
| f96d44e5de | |||
| 209894c2f5 | |||
| 93394ebc27 | |||
| 8f06aed1df | |||
| 3f01048954 | |||
| 45fa2347fe | |||
| a01b7adb13 | |||
| c5031b578b | |||
| 81a2f7ba8f | |||
| 85f37eba16 | |||
| c1ea1a6d4d | |||
| 96e16194b8 | |||
| 4a0b7a332f | |||
| 79c792d5d9 | |||
| 890de76cf5 | |||
| a58208a214 | |||
| acf95a3217 | |||
| 953e450e8e | |||
| c953cb6b3b | |||
| 9bb2548e3e | |||
| 1e24694b59 | |||
| 972a0f5e8d | |||
| cd64392301 | |||
| cecf833325 | |||
| c638017280 | |||
| 4165fd8a86 | |||
| 691b44a1b3 | |||
| c02ac7b9d7 | |||
| 186fd30436 | |||
| 494830ca8e | |||
| 0df6c5b9a7 | |||
| 0a0777d845 | |||
| a0b34dcfc0 | |||
| eb55814c62 | |||
| 94e22bc077 | |||
| 844b457950 | |||
| 733aff3be8 | |||
| cc28137e08 | |||
| f087b6ace3 | |||
| f67058367b | |||
| 288f356854 | |||
| ad39fa81af | |||
| 3b51a1ac01 | |||
| d7324103b1 | |||
| 37b76bac18 | |||
| e618674714 | |||
| d7b83224bb | |||
| f4d435dc0d | |||
| e2826db73a | |||
| 5f4d013732 | |||
| bef9565485 | |||
| f124ddf36a | |||
| fbafaa5619 | |||
| 5db695197b | |||
| 2bd983a974 | |||
| 391f70c527 | |||
| f23a61258b | |||
| 88ed1806a4 | |||
| a20c09c477 | |||
| 5a9a2d8526 | |||
| 2ba946b341 | |||
| 873fc5dff2 | |||
| c171d7624e | |||
| ece7709f18 | |||
| 8a68ab963e | |||
| 246dc92354 | |||
| d4cc3a42e2 | |||
| 1365bcffbf | |||
| 7fec7c96c4 | |||
| 738443c523 | |||
| 9112717180 | |||
| 886f674198 | |||
| c26cde3bfd | |||
| c49c7dad89 | |||
| 0f0450eda3 | |||
| ef90c4209c | |||
| d3b39b18d0 | |||
| 37f22ff75e | |||
| 024818be2b | |||
| 204d787644 | |||
| d2fd099570 | |||
| 81cde80f7b | |||
| cbe4a26e02 | |||
| c4e7cf68ce | |||
| f857482ed1 | |||
| 31e08190ab | |||
| 10744c61ad | |||
| 56747d1840 | |||
| 680a9584c6 | |||
| c264a0fd95 | |||
| 259532c0ad | |||
| 6fe2a9a8f1 | |||
| cd83a85e05 | |||
| b70d450426 | |||
| b1cc4aeb61 | |||
| 0243ca8245 | |||
| 8488789d74 | |||
| 850289bf1e | |||
| 136afcfcc1 | |||
| 56da8772bd | |||
| dc2d79f80f | |||
| 5950eb8aa7 | |||
| 483e1181c5 | |||
| c801f8954e | |||
| d01239f379 | |||
| 6cabff0c22 | |||
| 6964b2f737 | |||
| a572bbfc7c | |||
| a5198b2e13 | |||
| 6b4dcad514 | |||
| 337f703c54 | |||
| 837dc09bce | |||
| 6b0429676e | |||
| 807186f1d3 | |||
| a69b2b2973 | |||
| 22a85a418e | |||
| 71b7e44869 | |||
| 19b0701087 | |||
| 0aa95099f5 | |||
| a863bafba7 | |||
| f95e964b6b | |||
| e4856e86cd | |||
| 7b1afecf53 | |||
| da00cc101c | |||
| 08122e96fe | |||
| 2d1c71abeb | |||
| 8a6f89c845 | |||
| 509d899a2d | |||
| fda6e50ac8 | |||
| de93449908 | |||
| 6864f78fdf | |||
| f72c2fd19e | |||
| d2b9cc099d | |||
| 7d83ed04b1 | |||
| 29c7c8b913 | |||
| 8aaf23a15e | |||
| bc969569a2 | |||
| 4d411659c4 | |||
| 7cf1b29065 | |||
| 86527e84e6 | |||
| d09fc81939 | |||
| 205d9960cb | |||
| 31076c5f26 | |||
| 24fe85b99a | |||
| 91db54f07a | |||
| 82d05b7b54 | |||
| 283d0ee4c6 | |||
| 29b496a2d5 | |||
| 16aa90bba4 | |||
| 99ef94e3ac | |||
| 2a5694910a | |||
| 2636632cae | |||
| a3ee3860a2 | |||
| c96e47c80e | |||
| 620a00ee32 | |||
| 62b954d545 | |||
| 0d92e66dfc | |||
| ddc62664bf | |||
| b4f41b801a | |||
| df77e56cde | |||
| dc9a262319 | |||
| b111f1cb2a | |||
| 5525ac1049 |
+15
-2
@@ -122,10 +122,13 @@ LDFLAGS.rt = @LDFLAGS_RT@
|
||||
CFLAGS.icu = @CFLAGS_ICU@
|
||||
LDFLAGS.libsqlite3.soname = @LDFLAGS_LIBSQLITE3_SONAME@
|
||||
# soname: see https://sqlite.org/src/forumpost/5a3b44f510df8ded
|
||||
LDFLAGS.libsqlite3.os-specific = @LDFLAGS_MAC_CVERSION@ @LDFLAGS_OUT_IMPLIB@
|
||||
LDFLAGS.libsqlite3.os-specific = \
|
||||
@LDFLAGS_MAC_CVERSION@ @LDFLAGS_MAC_INSTALL_NAME@ @LDFLAGS_OUT_IMPLIB@
|
||||
# os-specific: see
|
||||
# - https://sqlite.org/forum/forumpost/9dfd5b8fd525a5d7
|
||||
# - https://sqlite.org/forum/forumpost/0c7fc097b2
|
||||
# - https://sqlite.org/forum/forumpost/5651662b8875ec0a
|
||||
|
||||
libsqlite3.DLL.basename = @SQLITE_DLL_BASENAME@
|
||||
# DLL.basename: see https://sqlite.org/forum/forumpost/828fdfe904
|
||||
libsqlite3.out.implib = @SQLITE_OUT_IMPLIB@
|
||||
@@ -210,6 +213,13 @@ TCL_CONFIG_SH = @TCL_CONFIG_SH@
|
||||
#TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
|
||||
#TCL_EXEC_PREFIX = @TCL_EXEC_PREFIX@
|
||||
#TCL_VERSION = @TCL_VERSION@
|
||||
TCL_MAJOR_VERSION = @TCL_MAJOR_VERSION@
|
||||
# ^^^ main.mk optionally uses this for determining the Tcl extension's
|
||||
# DLL name.
|
||||
TCL_EXT_DLL_BASENAME = @TCL_EXT_DLL_BASENAME@
|
||||
# ^^^ base name of the Tcl extension DLL. It varies by platform and
|
||||
# Tcl version.
|
||||
|
||||
#
|
||||
# $(TCLLIBDIR) = where to install the tcl plugin. If this is empty, it
|
||||
# is calculated at make-time by the targets which need it but we
|
||||
@@ -254,12 +264,15 @@ AS_AUTO_DEF = $(TOP)/auto.def
|
||||
# invoked with to produce this makefile.
|
||||
#
|
||||
AS_AUTORECONFIG = @SQLITE_AUTORECONFIG@
|
||||
|
||||
.PHONY: reconfigure
|
||||
reconfigure:
|
||||
$(AS_AUTORECONFIG)
|
||||
USE_AMALGAMATION ?= @USE_AMALGAMATION@
|
||||
LINK_TOOLS_DYNAMICALLY ?= @LINK_TOOLS_DYNAMICALLY@
|
||||
AMALGAMATION_GEN_FLAGS ?= --linemacros=@AMALGAMATION_LINE_MACROS@
|
||||
EXTRA_SRC ?= @AMALGAMATION_EXTRA_SRC@
|
||||
STATIC_TCLSQLITE3 = @STATIC_TCLSQLITE3@
|
||||
STATIC_CLI_SHELL = @STATIC_CLI_SHELL@
|
||||
|
||||
#
|
||||
# CFLAGS for sqlite3$(T.exe)
|
||||
|
||||
+16
-12
@@ -884,7 +884,7 @@ RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>2
|
||||
TCC = $(TCC) -DSQLITE_DEBUG=1
|
||||
TCC = $(TCC) -DSQLITE_DEBUG=1 -DSQLITE_USE_W32_FOR_CONSOLE_IO
|
||||
RCC = $(RCC) -DSQLITE_DEBUG=1
|
||||
!IF $(DYNAMIC_SHELL)==0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
|
||||
@@ -1623,7 +1623,6 @@ TESTSRC = \
|
||||
$(TOP)\src\test_thread.c \
|
||||
$(TOP)\src\test_vdbecov.c \
|
||||
$(TOP)\src\test_vfs.c \
|
||||
$(TOP)\src\test_windirent.c \
|
||||
$(TOP)\src\test_window.c \
|
||||
$(TOP)\src\test_wsd.c \
|
||||
$(TOP)\ext\fts3\fts3_term.c \
|
||||
@@ -1972,10 +1971,6 @@ fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
fuzzcheck-asan.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) /fsanitize=address $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
run-fuzzcheck: fuzzcheck.exe fuzzcheck-asan.exe
|
||||
fuzzcheck --spinner $(FUZZDB)
|
||||
fuzzcheck-asan --spinner $(FUZZDB)
|
||||
|
||||
ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H)
|
||||
$(LTLINK) $(NO_WARN) $(FUZZCHECK_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
@@ -2399,12 +2394,11 @@ SHELL_DEP = \
|
||||
$(TOP)\ext\misc\sqlite3_stdio.h \
|
||||
$(TOP)\ext\misc\uint.c \
|
||||
$(TOP)\ext\misc\vfstrace.c \
|
||||
$(TOP)\ext\misc\windirent.h \
|
||||
$(TOP)\ext\misc\zipfile.c \
|
||||
$(TOP)\ext\recover\dbdata.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.c \
|
||||
$(TOP)\ext\recover\sqlite3recover.h \
|
||||
$(TOP)\src\test_windirent.c \
|
||||
$(TOP)\src\test_windirent.h
|
||||
$(TOP)\ext\recover\sqlite3recover.h
|
||||
|
||||
# If use of zlib is enabled, add the "zipfile.c" source file.
|
||||
#
|
||||
@@ -2589,7 +2583,7 @@ sqlite_tcl.h:
|
||||
|
||||
testfixture.exe: $(TESTFIXTURE_SRC) $(TESTFIXTURE_DEP) $(SQLITE3H) $(LIBRESOBJS) $(HDR) $(SQLITE_TCL_DEP)
|
||||
$(LTLINK) -DSQLITE_NO_SYNC=1 $(TESTFIXTURE_FLAGS) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) \
|
||||
-DBUILD_sqlite -I$(TCLINCDIR) -I$(TOP)\ext\misc \
|
||||
$(TESTFIXTURE_SRC) \
|
||||
/link $(LDFLAGS) $(LTLINKOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) $(LIBRESOBJS) $(TCLLIBS) $(LTLIBS) $(TLIBS)
|
||||
|
||||
@@ -2602,9 +2596,12 @@ 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 sqlite3_rsync.exe $(TOP)\tool\mktoolzip.tcl
|
||||
tool-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe sqlite3_rsync.exe sqlite3.dll $(TOP)\tool\mktoolzip.tcl
|
||||
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl
|
||||
|
||||
snapshot-zip: testfixture.exe sqlite3.exe sqldiff.exe sqlite3_analyzer.exe sqlite3_rsync.exe sqlite3.dll $(TOP)\tool\mktoolzip.tcl
|
||||
.\testfixture.exe $(TOP)\tool\mktoolzip.tcl --snapshot
|
||||
|
||||
coretestprogs: testfixture.exe sqlite3.exe
|
||||
|
||||
testprogs: $(TESTPROGS) srcck1.exe fuzzcheck.exe sessionfuzz.exe
|
||||
@@ -2678,7 +2675,14 @@ srctree-check: $(TOP)\tool\srctree-check.tcl
|
||||
|
||||
# Testing for a release
|
||||
#
|
||||
releasetest:
|
||||
releasetest: verify-source
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
|
||||
|
||||
# xdevtest is like releasetest, except that it skips the
|
||||
# dependency on verify-source so that xdevtest can be run from
|
||||
# a modified source tree.
|
||||
#
|
||||
xdevtest:
|
||||
$(TCLSH_CMD) $(TOP)\test\testrunner.tcl release
|
||||
|
||||
|
||||
|
||||
@@ -57,18 +57,18 @@ If you do not want to use Fossil, you can download tarballs or ZIP
|
||||
archives or [SQLite archives](https://sqlite.org/cli.html#sqlar) as follows:
|
||||
|
||||
* Latest trunk check-in as
|
||||
[Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz),
|
||||
[ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip), or
|
||||
[SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar).
|
||||
[Tarball](https://sqlite.org/src/tarball/sqlite.tar.gz),
|
||||
[ZIP-archive](https://sqlite.org/src/zip/sqlite.zip), or
|
||||
[SQLite-archive](https://sqlite.org/src/sqlar/sqlite.sqlar).
|
||||
|
||||
* Latest release as
|
||||
[Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=release),
|
||||
[ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip?r=release), or
|
||||
[SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar?r=release).
|
||||
[Tarball](https://sqlite.org/src/tarball/sqlite.tar.gz?r=release),
|
||||
[ZIP-archive](https://sqlite.org/src/zip/sqlite.zip?r=release), or
|
||||
[SQLite-archive](https://sqlite.org/src/sqlar/sqlite.sqlar?r=release).
|
||||
|
||||
* For other check-ins, substitute an appropriate branch name or
|
||||
tag or hash prefix in place of "release" in the URLs of the previous
|
||||
bullet. Or browse the [timeline](https://www.sqlite.org/src/timeline)
|
||||
bullet. Or browse the [timeline](https://sqlite.org/src/timeline)
|
||||
to locate the check-in desired, click on its information page link,
|
||||
then click on the "Tarball" or "ZIP Archive" links on the information
|
||||
page.
|
||||
@@ -308,14 +308,14 @@ individual source file exceeds 32K lines in length.
|
||||
## How It All Fits Together
|
||||
|
||||
SQLite is modular in design.
|
||||
See the [architectural description](https://www.sqlite.org/arch.html)
|
||||
See the [architectural description](https://sqlite.org/arch.html)
|
||||
for details. Other documents that are useful in
|
||||
helping to understand how SQLite works include the
|
||||
[file format](https://www.sqlite.org/fileformat2.html) description,
|
||||
the [virtual machine](https://www.sqlite.org/opcode.html) that runs
|
||||
[file format](https://sqlite.org/fileformat2.html) description,
|
||||
the [virtual machine](https://sqlite.org/opcode.html) that runs
|
||||
prepared statements, the description of
|
||||
[how transactions work](https://www.sqlite.org/atomiccommit.html), and
|
||||
the [overview of the query planner](https://www.sqlite.org/optoverview.html).
|
||||
[how transactions work](https://sqlite.org/atomiccommit.html), and
|
||||
the [overview of the query planner](https://sqlite.org/optoverview.html).
|
||||
|
||||
Decades of effort have gone into optimizing SQLite, both
|
||||
for small size and high performance. And optimizations tend to result in
|
||||
|
||||
@@ -46,8 +46,6 @@ sqlite-configure canonical {
|
||||
|
||||
define LINK_TOOLS_DYNAMICALLY [proj-opt-was-provided dynlink-tools]
|
||||
|
||||
proj-define-for-opt static-tclsqlite3 STATIC_TCLSQLITE3 "Statically link tclsqlite3?"
|
||||
|
||||
if {[set fsan [join [opt-val asan-fsanitize] ","]] in {auto ""}} {
|
||||
set fsan address,bounds-strict
|
||||
}
|
||||
@@ -56,4 +54,11 @@ sqlite-configure canonical {
|
||||
sqlite-handle-tcl
|
||||
sqlite-handle-emsdk
|
||||
|
||||
}
|
||||
proj-if-opt-truthy static-shells {
|
||||
proj-opt-set static-tclsqlite3 1
|
||||
proj-opt-set static-cli-shell 1
|
||||
}
|
||||
proj-define-for-opt static-tclsqlite3 STATIC_TCLSQLITE3 "Statically link tclsqlite3?"
|
||||
proj-define-for-opt static-cli-shell STATIC_CLI_SHELL "Statically link CLI shell?"
|
||||
|
||||
}; # sqlite-configure
|
||||
|
||||
+16
-6
@@ -125,10 +125,8 @@ OPT_FEATURE_FLAGS = @OPT_FEATURE_FLAGS@
|
||||
|
||||
LDFLAGS.libsqlite3.soname = @LDFLAGS_LIBSQLITE3_SONAME@
|
||||
# soname: see https://sqlite.org/src/forumpost/5a3b44f510df8ded
|
||||
LDFLAGS.libsqlite3.os-specific = @LDFLAGS_MAC_CVERSION@ @LDFLAGS_OUT_IMPLIB@
|
||||
# os-specific: see
|
||||
# - https://sqlite.org/forum/forumpost/9dfd5b8fd525a5d7
|
||||
# - https://sqlite.org/forum/forumpost/0c7fc097b2
|
||||
LDFLAGS.libsqlite3.os-specific = \
|
||||
@LDFLAGS_MAC_CVERSION@ @LDFLAGS_MAC_INSTALL_NAME@ @LDFLAGS_OUT_IMPLIB@
|
||||
|
||||
LDFLAGS.libsqlite3 = \
|
||||
$(LDFLAGS.rpath) $(LDFLAGS.pthread) \
|
||||
@@ -226,12 +224,25 @@ install: install-lib
|
||||
#
|
||||
ENABLE_STATIC_SHELL = @ENABLE_STATIC_SHELL@
|
||||
sqlite3-shell-link-flags.1 = $(TOP)/sqlite3.c $(LDFLAGS.libsqlite3)
|
||||
sqlite3-shell-link-flags.0 = -L. -lsqlite3 $(LDFLAGS.zlib)
|
||||
sqlite3-shell-link-flags.0 = -L. -lsqlite3 $(LDFLAGS.zlib) $(LDFLAGS.math)
|
||||
sqlite3-shell-deps.1 = $(TOP)/sqlite3.c
|
||||
sqlite3-shell-deps.0 = $(libsqlite3.DLL)
|
||||
#
|
||||
# STATIC_CLI_SHELL = 1 to statically link sqlite3$(T.exe), else
|
||||
# 0. Requires static versions of all requisite libraries. Primarily
|
||||
# intended for use with static-friendly environments like Alpine
|
||||
# Linux.
|
||||
#
|
||||
STATIC_CLI_SHELL = @STATIC_CLI_SHELL@
|
||||
#
|
||||
# sqlite3-shell-static.flags.N = N is $(STATIC_CLI_SHELL)
|
||||
#
|
||||
sqlite3-shell-static.flags.1 = -static
|
||||
sqlite3-shell-static.flags.0 =
|
||||
sqlite3$(T.exe): $(TOP)/shell.c $(sqlite3-shell-deps.$(ENABLE_STATIC_SHELL))
|
||||
$(CC) -o $@ \
|
||||
$(TOP)/shell.c $(sqlite3-shell-link-flags.$(ENABLE_STATIC_SHELL)) \
|
||||
$(sqlite3-shell-static.flags.$(STATIC_CLI_SHELL)) \
|
||||
-I. $(OPT_FEATURE_FLAGS) $(SHELL_OPT) \
|
||||
$(CFLAGS) $(CFLAGS.readline) $(CFLAGS.icu) \
|
||||
$(LDFLAGS) $(LDFLAGS.readline)
|
||||
@@ -282,7 +293,6 @@ dist:
|
||||
rm -fr $(dist_name)
|
||||
mkdir -p $(dist_name)
|
||||
cp -rp $(DIST_FILES) $(dist_name)/.
|
||||
rm -f $(dist_name)/tea/configure.ac.in
|
||||
tar czf $(dist_tarball) $(dist_name)
|
||||
rm -fr $(dist_name)
|
||||
ls -l $(dist_tarball)
|
||||
|
||||
@@ -724,7 +724,7 @@ RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
|
||||
!ENDIF
|
||||
|
||||
!IF $(DEBUG)>2
|
||||
TCC = $(TCC) -DSQLITE_DEBUG=1
|
||||
TCC = $(TCC) -DSQLITE_DEBUG=1 -DSQLITE_USE_W32_FOR_CONSOLE_IO
|
||||
RCC = $(RCC) -DSQLITE_DEBUG=1
|
||||
!IF $(DYNAMIC_SHELL)==0
|
||||
TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ Other preprocessor defines
|
||||
Additionally, preprocessor defines may be specified by using the OPTS macro
|
||||
on the NMAKE command line. However, not all possible preprocessor defines
|
||||
may be specified in this manner as some require the amalgamation to be built
|
||||
with them enabled (see http://www.sqlite.org/compile.html). For example, the
|
||||
with them enabled (see http://sqlite.org/compile.html). For example, the
|
||||
following will work:
|
||||
|
||||
"OPTS=-DSQLITE_ENABLE_STAT4=1 -DSQLITE_OMIT_JSON=1"
|
||||
|
||||
@@ -11,6 +11,10 @@ sqlite-configure autoconf {
|
||||
sqlite-check-common-system-deps
|
||||
proj-define-for-opt static-shell ENABLE_STATIC_SHELL \
|
||||
"Link library statically into the CLI shell?"
|
||||
proj-define-for-opt static-cli-shell STATIC_CLI_SHELL "Statically link CLI shell?"
|
||||
if {![opt-bool static-shell] && [opt-bool static-cli-shell]} {
|
||||
proj-fatal "--disable-static-shell and --static-cli-shell are mutualy exclusive"
|
||||
}
|
||||
if {![opt-bool shared] && ![opt-bool static-shell]} {
|
||||
proj-opt-set shared 1
|
||||
proj-indented-notice {
|
||||
|
||||
+540
-445
File diff suppressed because it is too large
Load Diff
+53
-37
@@ -1,9 +1,30 @@
|
||||
This is the SQLite extension for Tcl using the Tcl Extension
|
||||
Architecture (TEA).
|
||||
This is the SQLite extension for Tcl using something akin to
|
||||
the Tcl Extension Architecture (TEA). To build it:
|
||||
|
||||
----------------------- A BETTER WAY ---------------------------
|
||||
./configure ...flags...
|
||||
|
||||
A better way to build the TCL extension for SQLite is to use the
|
||||
e.g.:
|
||||
|
||||
./configure --with-tcl=/path/to/tcl/install/root
|
||||
|
||||
or:
|
||||
|
||||
./configure --with-tclsh=/path/to/tcl/install/root
|
||||
|
||||
Run ./configure --help for the full list of flags.
|
||||
|
||||
The configuration process will fail if tclConfig.sh cannot be found.
|
||||
|
||||
The makefile will only honor CFLAGS and CPPFLAGS passed to the
|
||||
configure script, not those directly passed to the makefile.
|
||||
|
||||
Then:
|
||||
|
||||
make test install
|
||||
|
||||
----------------------- THE PREFERRED WAY ---------------------------
|
||||
|
||||
The preferred way to build the TCL extension for SQLite is to use the
|
||||
canonical source code tarball. For Unix:
|
||||
|
||||
./configure --with-tclsh=$(TCLSH)
|
||||
@@ -20,24 +41,32 @@ step-by-step instructions at the links below for more information:
|
||||
https://sqlite.org/src/doc/trunk/doc/compile-for-unix.md
|
||||
https://sqlite.org/src/doc/trunk/doc/compile-for-windows.md
|
||||
|
||||
The whole point of the amalgamation-autoconf tarball (in which this
|
||||
README.txt file is embedded) is to provide a means of compiling
|
||||
SQLite that does not require first installing TCL and/or "tclsh".
|
||||
The canonical Makefile in the SQLite source tree provides more
|
||||
capabilities (such as the the ability to run test cases to ensure
|
||||
that the build worked) and is better maintained. The only
|
||||
downside of the canonical Makefile is that it requires a TCL
|
||||
installation. But if you are wanting to build the TCL extension for
|
||||
SQLite, then presumably you already have a TCL installation. So why
|
||||
not just use the more-capable and better-maintained canoncal Makefile?
|
||||
And info about the extension's Tcl interface can be found at:
|
||||
|
||||
This TEA builder is derived from code found at
|
||||
https://sqlite.org/tclsqlite.html
|
||||
|
||||
The whole point of the amalgamation-autoconf tarball (in which this
|
||||
README.txt file is embedded) is to provide a means of compiling SQLite
|
||||
that does not require first installing TCL and/or "tclsh". The
|
||||
canonical Makefile in the SQLite source tree provides more
|
||||
capabilities (such as the the ability to run test cases to ensure that
|
||||
the build worked) and is better maintained. The only downside of the
|
||||
canonical Makefile is that it requires a TCL installation. But if you
|
||||
are wanting to build the TCL extension for SQLite, then presumably you
|
||||
already have a TCL installation. So why not just use the more-capable
|
||||
and better-maintained canoncal Makefile?
|
||||
|
||||
As of version 3.50.0, this build process uses "teaish":
|
||||
|
||||
https://fossil.wanderinghorse.net/r/teaish
|
||||
|
||||
which is conceptually derived from the pre-3.50 toolchain, TEA:
|
||||
|
||||
http://core.tcl-lang.org/tclconfig
|
||||
http://core.tcl-lang.org/sampleextension
|
||||
|
||||
The SQLite developers do not understand how it works. It seems to
|
||||
work for us. It might also work for you. But we cannot promise that.
|
||||
It to works for us. It might also work for you. But we cannot
|
||||
promise that.
|
||||
|
||||
If you want to use this TEA builder and it works for you, that's fine.
|
||||
But if you have trouble, the first thing you should do is go back
|
||||
@@ -49,30 +78,17 @@ to using the canonical Makefile in the SQLite source tree.
|
||||
UNIX BUILD
|
||||
==========
|
||||
|
||||
Building under most UNIX systems is easy, just run the configure script
|
||||
and then run make. For more information about the build process, see
|
||||
the tcl/unix/README file in the Tcl src dist. The following minimal
|
||||
example will install the extension in the /opt/tcl directory.
|
||||
Building under most UNIX systems is easy, just run the configure
|
||||
script and then run make. For example:
|
||||
|
||||
$ cd sqlite-*-tea
|
||||
$ ./configure --prefix=/opt/tcl
|
||||
$ make
|
||||
$ ./configure --with-tcl=/path/to/tcl/install/root
|
||||
$ make test
|
||||
$ make install
|
||||
|
||||
WINDOWS BUILD
|
||||
=============
|
||||
|
||||
The recommended method to build extensions under windows is to use the
|
||||
Msys + Mingw build process. This provides a Unix-style build while
|
||||
generating native Windows binaries. Using the Msys + Mingw build tools
|
||||
means that you can use the same configure script as per the Unix build
|
||||
to create a Makefile. See the tcl/win/README file for the URL of
|
||||
the Msys + Mingw download.
|
||||
|
||||
If you have VC++ then you may wish to use the files in the win
|
||||
subdirectory and build the extension using just VC++. These files have
|
||||
been designed to be as generic as possible but will require some
|
||||
additional maintenance by the project developer to synchronise with
|
||||
the TEA configure.in and Makefile.in files. Instructions for using the
|
||||
VC++ makefile are written in the first part of the Makefile.vc
|
||||
file.
|
||||
On Windows this build is known to work on Cygwin and some Msys2
|
||||
environments. We do not currently support Microsoft makefiles for
|
||||
native Windows builds.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# -*- tcl -*-
|
||||
#
|
||||
# Unless this file is named _teaish.tester.tcl.in, you are probably
|
||||
# looking at an automatically generated/filtered copy and should
|
||||
# probably not edit it.
|
||||
#
|
||||
# This is the wrapper script invoked by teaish's "make test" recipe.
|
||||
# It gets passed 3 args:
|
||||
#
|
||||
# $1 = the DLL name, or "" if the extension has no DLL
|
||||
#
|
||||
# $2 = the "load prefix" for Tcl's [load] or empty if $1 is empty
|
||||
#
|
||||
# $3 = the /path/to/teaish/tester.tcl (test utility code)
|
||||
#
|
||||
@if TEAISH_VSATISFIES_CODE
|
||||
@TEAISH_VSATISFIES_CODE@
|
||||
@endif
|
||||
if {[llength [lindex $::argv 0]] > 0} {
|
||||
load [file normalize [lindex $::argv 0]] [lindex $::argv 1];
|
||||
# ----^^^^^^^ needed on Haiku when argv 0 is just a filename, else
|
||||
# load cannot find the file.
|
||||
}
|
||||
set ::argv [lassign $argv - -]
|
||||
source -encoding utf-8 [lindex $::argv 0]; # teaish/tester.tcl
|
||||
@if TEAISH_PKGINIT_TCL
|
||||
apply {{file} {
|
||||
set dir [file dirname $::argv0]
|
||||
source -encoding utf-8 $file
|
||||
}} [join {@TEAISH_PKGINIT_TCL@}]
|
||||
@endif
|
||||
@if TEAISH_TM_TCL
|
||||
apply {{file} {
|
||||
set dir [file dirname $::argv0]
|
||||
source -encoding utf-8 $file
|
||||
}} [join {@TEAISH_TM_TCL@}]
|
||||
@endif
|
||||
@if TEAISH_TEST_TCL
|
||||
apply {{file} {
|
||||
# Populate state for [tester.tcl::teaish-build-flag*]
|
||||
array set ::teaish__BuildFlags @TEAISH__DEFINES_MAP@
|
||||
set dir [file normalize [file dirname $file]]
|
||||
#test-fail "Just testing"
|
||||
source -encoding utf-8 $file
|
||||
}} [join {@TEAISH_TEST_TCL@}]
|
||||
@else # TEAISH_TEST_TCL
|
||||
# No $TEAISH_TEST_TCL provided, so here's a default test which simply
|
||||
# loads the extension.
|
||||
puts {Extension @TEAISH_NAME@ @TEAISH_VERSION@ successfully loaded from @TEAISH_TESTER_TCL@}
|
||||
@endif
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
#
|
||||
# Include the TEA standard macro set
|
||||
#
|
||||
|
||||
builtin(include,tclconfig/tcl.m4)
|
||||
|
||||
#
|
||||
# Add here whatever m4 macros you want to define for your package
|
||||
#
|
||||
@@ -0,0 +1,8 @@
|
||||
#/do/not/tclsh
|
||||
# ^^^ help out editors which guess this file's content type.
|
||||
#
|
||||
# Main configure script entry point for the TEA(ish) framework. All
|
||||
# extension-specific customization goes in teaish.tcl.in or
|
||||
# teaish.tcl.
|
||||
use teaish/core
|
||||
teaish-configure-core
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Look for and run autosetup...
|
||||
dir0="`dirname "$0"`"
|
||||
dirA="$dir0"
|
||||
if [ -d $dirA/autosetup ]; then
|
||||
# A local copy of autosetup
|
||||
dirA=$dirA/autosetup
|
||||
elif [ -d $dirA/../autosetup ]; then
|
||||
# SQLite "autoconf" bundle
|
||||
dirA=$dirA/../autosetup
|
||||
elif [ -d $dirA/../../autosetup ]; then
|
||||
# SQLite canonical source tree
|
||||
dirA=$dirA/../../autosetup
|
||||
else
|
||||
echo "$0: Cannot find autosetup" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
WRAPPER="$0"; export WRAPPER; exec "`"$dirA/autosetup-find-tclsh"`" \
|
||||
"$dirA/autosetup" --teaish-extension-dir="$dir0" \
|
||||
"$@"
|
||||
@@ -1,227 +0,0 @@
|
||||
#!/bin/bash -norc
|
||||
dnl This file is an input file used by the GNU "autoconf" program to
|
||||
dnl generate the file "configure", which is run during Tcl installation
|
||||
dnl to configure the system for the local environment.
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Sample configure.ac for Tcl Extensions. The only places you should
|
||||
# need to modify this file are marked by the string __CHANGE__
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# __CHANGE__
|
||||
# Set your package name and version numbers here.
|
||||
#
|
||||
# This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION
|
||||
# set as provided. These will also be added as -D defs in your Makefile
|
||||
# so you can encode the package version directly into the source files.
|
||||
# This will also define a special symbol for Windows (BUILD_<PACKAGE_NAME>
|
||||
# so that we create the export library with the dll.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
AC_INIT([sqlite],[@VERSION@])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Call TEA_INIT as the first TEA_ macro to set up initial vars.
|
||||
# This will define a ${TEA_PLATFORM} variable == "unix" or "windows"
|
||||
# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_INIT()
|
||||
|
||||
AC_CONFIG_AUX_DIR(tclconfig)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Load the tclConfig.sh file
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_PATH_TCLCONFIG
|
||||
TEA_LOAD_TCLCONFIG
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Load the tkConfig.sh file if necessary (Tk extension)
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#TEA_PATH_TKCONFIG
|
||||
#TEA_LOAD_TKCONFIG
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Handle the --prefix=... option by defaulting to what Tcl gave.
|
||||
# Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
TEA_PREFIX
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# Standard compiler checks.
|
||||
# This sets up CC by using the CC env var, or looks for gcc otherwise.
|
||||
# This also calls AC_PROG_CC and a few others to create the basic setup
|
||||
# necessary to compile executables.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
TEA_SETUP_COMPILER
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# __CHANGE__
|
||||
# Specify the C source files to compile in TEA_ADD_SOURCES,
|
||||
# public headers that need to be installed in TEA_ADD_HEADERS,
|
||||
# stub library C source files to compile in TEA_ADD_STUB_SOURCES,
|
||||
# and runtime Tcl library files in TEA_ADD_TCL_SOURCES.
|
||||
# This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS
|
||||
# and PKG_TCL_SOURCES.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
TEA_ADD_SOURCES([tclsqlite3.c])
|
||||
TEA_ADD_HEADERS([])
|
||||
TEA_ADD_INCLUDES([])
|
||||
TEA_ADD_LIBS([])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS4=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS5=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_GEOPOLY=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_MATH_FUNCTIONS=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DESERIALIZE=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DBPAGE_VTAB=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_BYTECODE_VTAB=1])
|
||||
TEA_ADD_CFLAGS([-DSQLITE_ENABLE_DBSTAT_VTAB=1])
|
||||
TEA_ADD_STUB_SOURCES([])
|
||||
TEA_ADD_TCL_SOURCES([])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# The --with-system-sqlite causes the TCL bindings to SQLite to use
|
||||
# the system shared library for SQLite rather than statically linking
|
||||
# against its own private copy. This is dangerous and leads to
|
||||
# undesirable dependences and is not recommended.
|
||||
# Patchs from rmax.
|
||||
#--------------------------------------------------------------------
|
||||
AC_ARG_WITH([system-sqlite],
|
||||
[AS_HELP_STRING([--with-system-sqlite],
|
||||
[use a system-supplied libsqlite3 instead of the bundled one])],
|
||||
[], [with_system_sqlite=no])
|
||||
if test x$with_system_sqlite != xno; then
|
||||
AC_CHECK_HEADER([sqlite3.h],
|
||||
[AC_CHECK_LIB([sqlite3],[sqlite3_initialize],
|
||||
[AC_DEFINE(USE_SYSTEM_SQLITE)
|
||||
LIBS="$LIBS -lsqlite3"])])
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# __CHANGE__
|
||||
#
|
||||
# You can add more files to clean if your extension creates any extra
|
||||
# files by extending CLEANFILES.
|
||||
# Add pkgIndex.tcl if it is generated in the Makefile instead of ./configure
|
||||
# and change Makefile.in to move it from CONFIG_CLEAN_FILES to BINARIES var.
|
||||
#
|
||||
# A few miscellaneous platform-specific items:
|
||||
# TEA_ADD_* any platform specific compiler/build info here.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#CLEANFILES="$CLEANFILES pkgIndex.tcl"
|
||||
if test "${TEA_PLATFORM}" = "windows" ; then
|
||||
# Ensure no empty if clauses
|
||||
:
|
||||
#TEA_ADD_SOURCES([win/winFile.c])
|
||||
#TEA_ADD_INCLUDES([-I\"$(${CYGPATH} ${srcdir}/win)\"])
|
||||
else
|
||||
# Ensure no empty else clauses
|
||||
:
|
||||
#TEA_ADD_SOURCES([unix/unixFile.c])
|
||||
#TEA_ADD_LIBS([-lsuperfly])
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# __CHANGE__
|
||||
# Choose which headers you need. Extension authors should try very
|
||||
# hard to only rely on the Tcl public header files. Internal headers
|
||||
# contain private data structures and are subject to change without
|
||||
# notice.
|
||||
# This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_PUBLIC_TCL_HEADERS
|
||||
#TEA_PRIVATE_TCL_HEADERS
|
||||
|
||||
#TEA_PUBLIC_TK_HEADERS
|
||||
#TEA_PRIVATE_TK_HEADERS
|
||||
#TEA_PATH_X
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Check whether --enable-threads or --disable-threads was given.
|
||||
# This auto-enables if Tcl was compiled threaded.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_ENABLE_THREADS
|
||||
if test "${TCL_THREADS}" = "1" ; then
|
||||
AC_DEFINE(SQLITE_THREADSAFE, 1, [Trigger sqlite threadsafe build])
|
||||
# Not automatically added by Tcl because its assumed Tcl links to them,
|
||||
# but it may not if it isn't really a threaded build.
|
||||
TEA_ADD_LIBS([$THREADS_LIBS])
|
||||
else
|
||||
AC_DEFINE(SQLITE_THREADSAFE, 0, [Trigger sqlite non-threadsafe build])
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# The statement below defines a collection of symbols related to
|
||||
# building as a shared library instead of a static library.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_ENABLE_SHARED
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# This macro figures out what flags to use with the compiler/linker
|
||||
# when building shared/static debug/optimized objects. This information
|
||||
# can be taken from the tclConfig.sh file, but this figures it all out.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_CONFIG_CFLAGS
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Set the default compiler switches based on the --enable-symbols option.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_ENABLE_SYMBOLS
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# This macro generates a line to use when building a library. It
|
||||
# depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS,
|
||||
# and TEA_LOAD_TCLCONFIG macros above.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_MAKE_LIB
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Determine the name of the tclsh and/or wish executables in the
|
||||
# Tcl and Tk build directories or the location they were installed
|
||||
# into. These paths are used to support running test cases only,
|
||||
# the Makefile should not be making use of these paths to generate
|
||||
# a pkgIndex.tcl file or anything else at extension build time.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
TEA_PROG_TCLSH
|
||||
#TEA_PROG_WISH
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Setup a *Config.sh.in configuration file.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
#TEA_EXPORT_CONFIG([sample])
|
||||
#AC_SUBST(SAMPLE_VAR)
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Specify files to substitute AC variables in. You may alternatively
|
||||
# have a special pkgIndex.tcl.in or other files which require
|
||||
# substituting the AC variables in. Include these here.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_CONFIG_FILES([Makefile pkgIndex.tcl])
|
||||
#AC_CONFIG_FILES([sampleConfig.sh])
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Finally, substitute all of the various values into the files
|
||||
# specified with AC_CONFIG_FILES.
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
AC_OUTPUT
|
||||
@@ -1,15 +0,0 @@
|
||||
.TH sqlite3 n 4.1 "Tcl-Extensions"
|
||||
.HS sqlite3 tcl
|
||||
.BS
|
||||
.SH NAME
|
||||
sqlite3 \- an interface to the SQLite3 database engine
|
||||
.SH SYNOPSIS
|
||||
\fBsqlite3\fI command_name ?filename?\fR
|
||||
.br
|
||||
.SH DESCRIPTION
|
||||
SQLite3 is a self-contains, zero-configuration, transactional SQL database
|
||||
engine. This extension provides an easy to use interface for accessing
|
||||
SQLite database files from Tcl.
|
||||
.PP
|
||||
For full documentation see \fIhttp://www.sqlite.org/\fR and
|
||||
in particular \fIhttp://www.sqlite.org/tclsqlite.html\fR.
|
||||
@@ -1,10 +1,40 @@
|
||||
# -*- tcl -*-
|
||||
# Tcl package index file, version 1.1
|
||||
# Tcl package index file
|
||||
#
|
||||
# Unless this file is named pkgIndex.tcl.in, you are probably looking
|
||||
# at an automatically generated/filtered copy and should probably not
|
||||
# edit it.
|
||||
#
|
||||
# Adapted from https://core.tcl-lang.org/tcltls
|
||||
@if TEAISH_VSATISFIES_CODE
|
||||
@TEAISH_VSATISFIES_CODE@
|
||||
@endif
|
||||
if {[package vsatisfies [package provide Tcl] 9.0-]} {
|
||||
package ifneeded sqlite3 @PACKAGE_VERSION@ \
|
||||
[list load [file join $dir @PKG_LIB_FILE9@] Sqlite3]
|
||||
package ifneeded {@TEAISH_PKGNAME@} {@TEAISH_VERSION@} [list apply {{dir} {
|
||||
@if TEAISH_ENABLE_DLL
|
||||
load [file join $dir {@TEAISH_DLL9@}] @TEAISH_LOAD_PREFIX@
|
||||
@endif
|
||||
@if TEAISH_PKGINIT_TCL_TAIL
|
||||
set initScript [file join $dir {@TEAISH_PKGINIT_TCL_TAIL@}]
|
||||
if {[file exists $initScript]} {
|
||||
source -encoding utf-8 $initScript
|
||||
}
|
||||
@endif
|
||||
}} $dir]
|
||||
} else {
|
||||
package ifneeded sqlite3 @PACKAGE_VERSION@ \
|
||||
[list load [file join $dir @PKG_LIB_FILE8@] Sqlite3]
|
||||
package ifneeded {@TEAISH_PKGNAME@} {@TEAISH_VERSION@} [list apply {{dir} {
|
||||
@if TEAISH_ENABLE_DLL
|
||||
if {[string tolower [file extension {@TEAISH_DLL8@}]] in [list .dll .dylib .so]} {
|
||||
load [file join $dir {@TEAISH_DLL8@}] @TEAISH_LOAD_PREFIX@
|
||||
} else {
|
||||
load {} @TEAISH_LOAD_PREFIX@
|
||||
}
|
||||
@endif
|
||||
@if TEAISH_PKGINIT_TCL_TAIL
|
||||
set initScript [file join $dir {@TEAISH_PKGINIT_TCL_TAIL@}]
|
||||
if {[file exists $initScript]} {
|
||||
source -encoding utf-8 $initScript
|
||||
}
|
||||
@endif
|
||||
}} $dir]
|
||||
}
|
||||
|
||||
@@ -1,541 +0,0 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2020-11-14.01; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
# Create dirs (including intermediate dirs) using mode 755.
|
||||
# This is like GNU 'install' as of coreutils 8.32 (2020).
|
||||
mkdir_umask=22
|
||||
|
||||
backupsuffix=
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-p pass -p to $cpprog.
|
||||
-s $stripprog installed files.
|
||||
-S SUFFIX attempt to back up existing files, with suffix SUFFIX.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
|
||||
By default, rm is invoked with -f; when overridden with RMPROG,
|
||||
it's up to you to specify -f if you want it.
|
||||
|
||||
If -S is not specified, no backups are attempted.
|
||||
|
||||
Email bug reports to bug-automake@gnu.org.
|
||||
Automake home page: https://www.gnu.org/software/automake/
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-p) cpprog="$cpprog -p";;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-S) backupsuffix="$2"
|
||||
shift;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
# Don't chown directories that already exist.
|
||||
if test $dstdir_status = 0; then
|
||||
chowncmd=""
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dstbase=`basename "$src"`
|
||||
case $dst in
|
||||
*/) dst=$dst$dstbase;;
|
||||
*) dst=$dst/$dstbase;;
|
||||
esac
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
case $dstdir in
|
||||
*/) dstdirslash=$dstdir;;
|
||||
*) dstdirslash=$dstdir/;;
|
||||
esac
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
# The $RANDOM variable is not portable (e.g., dash). Use it
|
||||
# here however when possible just to lower collision chance.
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
|
||||
trap '
|
||||
ret=$?
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
|
||||
exit $ret
|
||||
' 0
|
||||
|
||||
# Because "mkdir -p" follows existing symlinks and we likely work
|
||||
# directly in world-writeable /tmp, make sure that the '$tmpdir'
|
||||
# directory is successfully created first before we actually test
|
||||
# 'mkdir -p'.
|
||||
if (umask $mkdir_umask &&
|
||||
$mkdirprog $mkdir_mode "$tmpdir" &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
test_tmpdir="$tmpdir/a"
|
||||
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=${dstdirslash}_inst.$$_
|
||||
rmtmp=${dstdirslash}_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask &&
|
||||
{ test -z "$stripcmd" || {
|
||||
# Create $dsttmp read-write so that cp doesn't create it read-only,
|
||||
# which would cause strip to fail.
|
||||
if test -z "$doit"; then
|
||||
: >"$dsttmp" # No need to fork-exec 'touch'.
|
||||
else
|
||||
$doit touch "$dsttmp"
|
||||
fi
|
||||
}
|
||||
} &&
|
||||
$doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# If $backupsuffix is set, and the file being installed
|
||||
# already exists, attempt a backup. Don't worry if it fails,
|
||||
# e.g., if mv doesn't support -f.
|
||||
if test -n "$backupsuffix" && test -f "$dst"; then
|
||||
$doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
|
||||
fi
|
||||
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,569 @@
|
||||
# Teaish configure script for the SQLite Tcl extension
|
||||
|
||||
#
|
||||
# State for disparate config-time pieces.
|
||||
#
|
||||
array set sqlite__Config [proj-strip-hash-comments {
|
||||
#
|
||||
# The list of feature --flags which the --all flag implies. This
|
||||
# requires special handling in a few places.
|
||||
#
|
||||
all-flag-enables {fts3 fts4 fts5 rtree geopoly}
|
||||
|
||||
# >0 if building in the canonical tree. -1=undetermined
|
||||
is-canonical -1
|
||||
}]
|
||||
|
||||
#
|
||||
# Set up the package info for teaish...
|
||||
#
|
||||
apply {{dir} {
|
||||
# Figure out the version number...
|
||||
set version ""
|
||||
if {[file exists $dir/../VERSION]} {
|
||||
# The canonical SQLite TEA(ish) build
|
||||
set version [proj-file-content -trim $dir/../VERSION]
|
||||
set ::sqlite__Config(is-canonical) 1
|
||||
set distname sqlite-tcl
|
||||
} elseif {[file exists $dir/generic/tclsqlite3.c]} {
|
||||
# The copy from the teaish tree, used as a dev/test bed before
|
||||
# updating SQLite's tree.
|
||||
set ::sqlite__Config(is-canonical) 0
|
||||
set fd [open $dir/generic/tclsqlite3.c rb]
|
||||
while {[gets $fd line] >=0} {
|
||||
if {[regexp {^#define[ ]+SQLITE_VERSION[ ]+"(3.+)"} \
|
||||
$line - version]} {
|
||||
set distname sqlite-teaish
|
||||
break
|
||||
}
|
||||
}
|
||||
close $fd
|
||||
}
|
||||
|
||||
if {"" eq $version} {
|
||||
proj-fatal "Cannot determine the SQLite version number"
|
||||
}
|
||||
|
||||
proj-assert {$::sqlite__Config(is-canonical) > -1}
|
||||
proj-assert {[string match 3.*.* $version]} \
|
||||
"Unexpected SQLite version: $version"
|
||||
|
||||
set pragmas {}
|
||||
if {$::sqlite__Config(is-canonical)} {
|
||||
# Disable "make dist" in the canonical tree. That tree is
|
||||
# generated from several pieces and creating/testing working
|
||||
# "dist" rules for that sub-build currently feels unnecessary. The
|
||||
# copy in the teaish tree, though, should be able to "make dist".
|
||||
lappend pragmas no-dist
|
||||
} else {
|
||||
lappend pragmas full-dist
|
||||
}
|
||||
|
||||
teaish-pkginfo-set -vars {
|
||||
-name sqlite
|
||||
-name.pkg sqlite3
|
||||
-version $version
|
||||
-name.dist $distname
|
||||
-libDir sqlite$version
|
||||
-pragmas $pragmas
|
||||
-src generic/tclsqlite3.c
|
||||
}
|
||||
# We should also have:
|
||||
# -vsatisfies 8.6-
|
||||
# But at least one platform is failing this vsatisfies check
|
||||
# for no apparent reason:
|
||||
# https://sqlite.org/forum/forumpost/fde857fb8101a4be
|
||||
}} [teaish-get -dir]
|
||||
|
||||
|
||||
#
|
||||
# Must return either an empty string or a list in the form accepted by
|
||||
# autosetup's [options] function.
|
||||
#
|
||||
proc teaish-options {} {
|
||||
# These flags and defaults mostly derive from the historical TEA
|
||||
# build. Some, like ICU, are taken from the canonical SQLite tree.
|
||||
return [subst -nocommands -nobackslashes {
|
||||
with-system-sqlite=0
|
||||
=> {Use the system-level SQLite instead of the copy in this tree.
|
||||
Also requires use of --override-sqlite-version so that the build
|
||||
knows what version number to associate with the system-level SQLite.}
|
||||
override-sqlite-version:VERSION
|
||||
=> {For use with --with-system-sqlite to set the version number.}
|
||||
threadsafe=1 => {Disable mutexing}
|
||||
with-tempstore:=no => {Use an in-RAM database for temporary tables: never,no,yes,always}
|
||||
load-extension=0 => {Enable loading of external extensions}
|
||||
math=1 => {Disable math functions}
|
||||
json=1 => {Disable JSON functions}
|
||||
fts3 => {Enable the FTS3 extension}
|
||||
fts4 => {Enable the FTS4 extension}
|
||||
fts5 => {Enable the FTS5 extension}
|
||||
update-limit => {Enable the UPDATE/DELETE LIMIT clause}
|
||||
geopoly => {Enable the GEOPOLY extension}
|
||||
rtree => {Enable the RTREE extension}
|
||||
session => {Enable the SESSION extension}
|
||||
all=1 => {Disable $::sqlite__Config(all-flag-enables)}
|
||||
with-icu-ldflags:LDFLAGS
|
||||
=> {Enable SQLITE_ENABLE_ICU and add the given linker flags for the
|
||||
ICU libraries. e.g. on Ubuntu systems, try '-licui18n -licuuc -licudata'.}
|
||||
with-icu-cflags:CFLAGS
|
||||
=> {Apply extra CFLAGS/CPPFLAGS necessary for building with ICU.
|
||||
e.g. -I/usr/local/include}
|
||||
with-icu-config:=auto
|
||||
=> {Enable SQLITE_ENABLE_ICU. Value must be one of: auto, pkg-config,
|
||||
/path/to/icu-config}
|
||||
icu-collations=0
|
||||
=> {Enable SQLITE_ENABLE_ICU_COLLATIONS. Requires --with-icu-ldflags=...
|
||||
or --with-icu-config}
|
||||
}]
|
||||
}
|
||||
|
||||
#
|
||||
# Gets called by tea-configure-core. Must perform any configuration
|
||||
# work needed for this extension.
|
||||
#
|
||||
proc teaish-configure {} {
|
||||
use teaish/feature
|
||||
|
||||
if {[proj-opt-was-provided override-sqlite-version]} {
|
||||
teaish-pkginfo-set -version [opt-val override-sqlite-version]
|
||||
proj-warn "overriding sqlite version number:" [teaish-pkginfo-get -version]
|
||||
} elseif {[proj-opt-was-provided with-system-sqlite]
|
||||
&& [opt-val with-system-sqlite] ne "0"} {
|
||||
proj-fatal "when using --with-system-sqlite also use" \
|
||||
"--override-sqlite-version to specify a library version number."
|
||||
}
|
||||
|
||||
define CFLAGS [proj-get-env CFLAGS {-O2}]
|
||||
sqlite-munge-cflags
|
||||
|
||||
#
|
||||
# Add feature flags from legacy configure.ac which are not covered by
|
||||
# --flags.
|
||||
#
|
||||
sqlite-add-feature-flag {
|
||||
-DSQLITE_3_SUFFIX_ONLY=1
|
||||
-DSQLITE_ENABLE_DESERIALIZE=1
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB=1
|
||||
-DSQLITE_ENABLE_BYTECODE_VTAB=1
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB=1
|
||||
}
|
||||
|
||||
if {[opt-bool with-system-sqlite]} {
|
||||
msg-result "Using system-level sqlite3."
|
||||
teaish-cflags-add -DUSE_SYSTEM_SQLITE
|
||||
teaish-ldflags-add -lsqlite3
|
||||
} elseif {$::sqlite__Config(is-canonical)} {
|
||||
teaish-cflags-add -I[teaish-get -dir]/..
|
||||
}
|
||||
|
||||
teaish-check-librt
|
||||
teaish-check-libz
|
||||
sqlite-handle-threadsafe
|
||||
sqlite-handle-tempstore
|
||||
sqlite-handle-load-extension
|
||||
sqlite-handle-math
|
||||
sqlite-handle-icu
|
||||
|
||||
sqlite-handle-common-feature-flags; # must be late in the process
|
||||
}; # teaish-configure
|
||||
|
||||
define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags.
|
||||
#
|
||||
# Adds $args, if not empty, to OPT_FEATURE_FLAGS. This is intended only for holding
|
||||
# -DSQLITE_ENABLE/OMIT/... flags, but that is not enforced here.
|
||||
proc sqlite-add-feature-flag {args} {
|
||||
if {"" ne $args} {
|
||||
define-append OPT_FEATURE_FLAGS {*}$args
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Check for log(3) in libm and die with an error if it is not
|
||||
# found. $featureName should be the feature name which requires that
|
||||
# function (it's used only in error messages). defines LDFLAGS_MATH to
|
||||
# the required linker flags (which may be empty even if the math APIs
|
||||
# are found, depending on the OS).
|
||||
proc sqlite-affirm-have-math {featureName} {
|
||||
if {"" eq [get-define LDFLAGS_MATH ""]} {
|
||||
if {![msg-quiet proj-check-function-in-lib log m]} {
|
||||
user-error "Missing math APIs for $featureName"
|
||||
}
|
||||
set lfl [get-define lib_log ""]
|
||||
undefine lib_log
|
||||
if {"" ne $lfl} {
|
||||
user-notice "Forcing requirement of $lfl for $featureName"
|
||||
}
|
||||
define LDFLAGS_MATH $lfl
|
||||
teaish-ldflags-prepend $lfl
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Handle various SQLITE_ENABLE/OMIT_... feature flags.
|
||||
proc sqlite-handle-common-feature-flags {} {
|
||||
msg-result "Feature flags..."
|
||||
if {![opt-bool all]} {
|
||||
# Special handling for --disable-all
|
||||
foreach flag $::sqlite__Config(all-flag-enables) {
|
||||
if {![proj-opt-was-provided $flag]} {
|
||||
proj-opt-set $flag 0
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach {boolFlag featureFlag ifSetEvalThis} [proj-strip-hash-comments {
|
||||
all {} {
|
||||
# The 'all' option must be first in this list. This impl makes
|
||||
# an effort to only apply flags which the user did not already
|
||||
# apply, so that combinations like (--all --disable-geopoly)
|
||||
# will indeed disable geopoly. There are corner cases where
|
||||
# flags which depend on each other will behave in non-intuitive
|
||||
# ways:
|
||||
#
|
||||
# --all --disable-rtree
|
||||
#
|
||||
# Will NOT disable geopoly, though geopoly depends on rtree.
|
||||
# The --geopoly flag, though, will automatically re-enable
|
||||
# --rtree, so --disable-rtree won't actually disable anything in
|
||||
# that case.
|
||||
foreach k $::sqlite__Config(all-flag-enables) {
|
||||
if {![proj-opt-was-provided $k]} {
|
||||
proj-opt-set $k 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fts3 -DSQLITE_ENABLE_FTS3 {sqlite-affirm-have-math fts3}
|
||||
fts4 -DSQLITE_ENABLE_FTS4 {sqlite-affirm-have-math fts4}
|
||||
fts5 -DSQLITE_ENABLE_FTS5 {sqlite-affirm-have-math fts5}
|
||||
geopoly -DSQLITE_ENABLE_GEOPOLY {proj-opt-set rtree}
|
||||
rtree -DSQLITE_ENABLE_RTREE {}
|
||||
session {-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK} {}
|
||||
update-limit -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT {}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
}] {
|
||||
if {$boolFlag ni $::autosetup(options)} {
|
||||
# Skip flags which are in the canonical build but not
|
||||
# the autoconf bundle.
|
||||
continue
|
||||
}
|
||||
proj-if-opt-truthy $boolFlag {
|
||||
sqlite-add-feature-flag $featureFlag
|
||||
if {0 != [eval $ifSetEvalThis] && "all" ne $boolFlag} {
|
||||
msg-result " + $boolFlag"
|
||||
}
|
||||
} {
|
||||
if {"all" ne $boolFlag} {
|
||||
msg-result " - $boolFlag"
|
||||
}
|
||||
}
|
||||
}
|
||||
#
|
||||
# Invert the above loop's logic for some SQLITE_OMIT_... cases. If
|
||||
# config option $boolFlag is false, [sqlite-add-feature-flag
|
||||
# $featureFlag], where $featureFlag is intended to be
|
||||
# -DSQLITE_OMIT_...
|
||||
foreach {boolFlag featureFlag} {
|
||||
json -DSQLITE_OMIT_JSON
|
||||
} {
|
||||
if {[proj-opt-truthy $boolFlag]} {
|
||||
msg-result " + $boolFlag"
|
||||
} else {
|
||||
sqlite-add-feature-flag $featureFlag
|
||||
msg-result " - $boolFlag"
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
# Remove duplicates from the final feature flag sets and show them
|
||||
# to the user.
|
||||
set oFF [get-define OPT_FEATURE_FLAGS]
|
||||
if {"" ne $oFF} {
|
||||
define OPT_FEATURE_FLAGS [lsort -unique $oFF]
|
||||
msg-result "Library feature flags: [get-define OPT_FEATURE_FLAGS]"
|
||||
}
|
||||
if {[lsearch [get-define TARGET_DEBUG ""] -DSQLITE_DEBUG=1] > -1} {
|
||||
msg-result "Note: this is a debug build, so performance will suffer."
|
||||
}
|
||||
teaish-cflags-add -define OPT_FEATURE_FLAGS
|
||||
}; # sqlite-handle-common-feature-flags
|
||||
|
||||
#
|
||||
# If --enable-threadsafe is set, this adds -DSQLITE_THREADSAFE=1 to
|
||||
# OPT_FEATURE_FLAGS and sets LDFLAGS_PTHREAD to the linker flags
|
||||
# needed for linking pthread (possibly an empty string). If
|
||||
# --enable-threadsafe is not set, adds -DSQLITE_THREADSAFE=0 to
|
||||
# OPT_FEATURE_FLAGS and sets LDFLAGS_PTHREAD to an empty string.
|
||||
#
|
||||
# It prepends the flags to the global LDFLAGS.
|
||||
proc sqlite-handle-threadsafe {} {
|
||||
msg-checking "Support threadsafe operation? "
|
||||
define LDFLAGS_PTHREAD ""
|
||||
set enable 0
|
||||
if {[proj-opt-was-provided threadsafe]} {
|
||||
proj-if-opt-truthy threadsafe {
|
||||
if {[proj-check-function-in-lib pthread_create pthread]
|
||||
&& [proj-check-function-in-lib pthread_mutexattr_init pthread]} {
|
||||
incr enable
|
||||
set ldf [get-define lib_pthread_create]
|
||||
define LDFLAGS_PTHREAD $ldf
|
||||
teaish-ldflags-prepend $ldf
|
||||
undefine lib_pthread_create
|
||||
undefine lib_pthread_mutexattr_init
|
||||
} else {
|
||||
user-error "Missing required pthread libraries. Use --disable-threadsafe to disable this check."
|
||||
}
|
||||
# Recall that LDFLAGS_PTHREAD might be empty even if pthreads if
|
||||
# found because it's in -lc on some platforms.
|
||||
} {
|
||||
msg-result "Disabled using --disable-threadsafe"
|
||||
}
|
||||
} else {
|
||||
#
|
||||
# If user does not specify --[disable-]threadsafe then select a
|
||||
# default based on whether it looks like Tcl has threading
|
||||
# support.
|
||||
#
|
||||
catch {
|
||||
scan [exec echo {puts [tcl::pkgconfig get threaded]} | [get-define TCLSH_CMD]] \
|
||||
%d enable
|
||||
}
|
||||
if {$enable} {
|
||||
set flagName "--threadsafe"
|
||||
set lblAbled "enabled"
|
||||
msg-result yes
|
||||
} else {
|
||||
set flagName "--disable-threadsafe"
|
||||
set lblAbled "disabled"
|
||||
msg-result no
|
||||
}
|
||||
msg-result "Defaulting to ${flagName} because Tcl has threading ${lblAbled}."
|
||||
# ^^^ We (probably) don't need to link against -lpthread in the
|
||||
# is-enabled case. We might in the case of static linking. Unsure.
|
||||
}
|
||||
sqlite-add-feature-flag -DSQLITE_THREADSAFE=${enable}
|
||||
return $enable
|
||||
}
|
||||
|
||||
#
|
||||
# Handles the --enable-load-extension flag. Returns 1 if the support
|
||||
# is enabled, else 0. If support for that feature is not found, a
|
||||
# fatal error is triggered if --enable-load-extension is explicitly
|
||||
# provided, else a loud warning is instead emitted. If
|
||||
# --disable-load-extension is used, no check is performed.
|
||||
#
|
||||
# Makes the following environment changes:
|
||||
#
|
||||
# - defines LDFLAGS_DLOPEN to any linker flags needed for this
|
||||
# feature. It may legally be empty on some systems where dlopen()
|
||||
# is in libc.
|
||||
#
|
||||
# - If the feature is not available, adds
|
||||
# -DSQLITE_OMIT_LOAD_EXTENSION=1 to the feature flags list.
|
||||
proc sqlite-handle-load-extension {} {
|
||||
define LDFLAGS_DLOPEN ""
|
||||
set found 0
|
||||
proj-if-opt-truthy load-extension {
|
||||
set found [proj-check-function-in-lib dlopen dl]
|
||||
if {$found} {
|
||||
set ldf [get-define lib_dlopen]
|
||||
define LDFLAGS_DLOPEN $ldf
|
||||
teaish-ldflags-prepend $ldf
|
||||
undefine lib_dlopen
|
||||
} else {
|
||||
if {[proj-opt-was-provided load-extension]} {
|
||||
# Explicit --enable-load-extension: fail if not found
|
||||
proj-indented-notice -error {
|
||||
--enable-load-extension was provided but dlopen()
|
||||
not found. Use --disable-load-extension to bypass this
|
||||
check.
|
||||
}
|
||||
} else {
|
||||
# It was implicitly enabled: warn if not found
|
||||
proj-indented-notice {
|
||||
WARNING: dlopen() not found, so loadable module support will
|
||||
be disabled. Use --disable-load-extension to bypass this
|
||||
check.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if {$found} {
|
||||
msg-result "Loadable extension support enabled."
|
||||
} else {
|
||||
msg-result "Disabling loadable extension support. Use --enable-load-extension to enable them."
|
||||
sqlite-add-feature-flag -DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
}
|
||||
return $found
|
||||
}
|
||||
|
||||
#
|
||||
# ICU - International Components for Unicode
|
||||
#
|
||||
# Handles these flags:
|
||||
#
|
||||
# --with-icu-ldflags=LDFLAGS
|
||||
# --with-icu-cflags=CFLAGS
|
||||
# --with-icu-config[=auto | pkg-config | /path/to/icu-config]
|
||||
# --enable-icu-collations
|
||||
#
|
||||
# --with-icu-config values:
|
||||
#
|
||||
# - auto: use the first one of (pkg-config, icu-config) found on the
|
||||
# system.
|
||||
# - pkg-config: use only pkg-config to determine flags
|
||||
# - /path/to/icu-config: use that to determine flags
|
||||
#
|
||||
# If --with-icu-config is used as neither pkg-config nor icu-config
|
||||
# are found, fail fatally.
|
||||
#
|
||||
# If both --with-icu-ldflags and --with-icu-config are provided, they
|
||||
# are cumulative. If neither are provided, icu-collations is not
|
||||
# honored and a warning is emitted if it is provided.
|
||||
#
|
||||
# Design note: though we could automatically enable ICU if the
|
||||
# icu-config binary or (pkg-config icu-io) are found, we specifically
|
||||
# do not. ICU is always an opt-in feature.
|
||||
proc sqlite-handle-icu {} {
|
||||
define LDFLAGS_LIBICU [join [opt-val with-icu-ldflags ""]]
|
||||
define CFLAGS_LIBICU [join [opt-val with-icu-cflags ""]]
|
||||
if {[proj-opt-was-provided with-icu-config]} {
|
||||
msg-result "Checking for ICU support..."
|
||||
set icuConfigBin [opt-val with-icu-config]
|
||||
set tryIcuConfigBin 1; # set to 0 if we end up using pkg-config
|
||||
if {$icuConfigBin in {auto pkg-config}} {
|
||||
uplevel 3 { use pkg-config }
|
||||
if {[pkg-config-init 0] && [pkg-config icu-io]} {
|
||||
# Maintenance reminder: historical docs say to use both of
|
||||
# (icu-io, icu-uc). icu-uc lacks a required lib and icu-io has
|
||||
# all of them on tested OSes.
|
||||
set tryIcuConfigBin 0
|
||||
define LDFLAGS_LIBICU [get-define PKG_ICU_IO_LDFLAGS]
|
||||
define-append LDFLAGS_LIBICU [get-define PKG_ICU_IO_LIBS]
|
||||
define CFLAGS_LIBICU [get-define PKG_ICU_IO_CFLAGS]
|
||||
} elseif {"pkg-config" eq $icuConfigBin} {
|
||||
proj-fatal "pkg-config cannot find package icu-io"
|
||||
} else {
|
||||
proj-assert {"auto" eq $icuConfigBin}
|
||||
}
|
||||
}
|
||||
if {$tryIcuConfigBin} {
|
||||
if {"auto" eq $icuConfigBin} {
|
||||
set icuConfigBin [proj-first-bin-of \
|
||||
/usr/local/bin/icu-config \
|
||||
/usr/bin/icu-config]
|
||||
if {"" eq $icuConfigBin} {
|
||||
proj-indented-notice -error {
|
||||
--with-icu-config=auto cannot find (pkg-config icu-io) or icu-config binary.
|
||||
On Ubuntu-like systems try:
|
||||
--with-icu-ldflags='-licui18n -licuuc -licudata'
|
||||
}
|
||||
}
|
||||
}
|
||||
if {[file-isexec $icuConfigBin]} {
|
||||
set x [exec $icuConfigBin --ldflags]
|
||||
if {"" eq $x} {
|
||||
proj-indented-notice -error \
|
||||
[subst {
|
||||
$icuConfigBin --ldflags returned no data.
|
||||
On Ubuntu-like systems try:
|
||||
--with-icu-ldflags='-licui18n -licuuc -licudata'
|
||||
}]
|
||||
}
|
||||
define-append LDFLAGS_LIBICU $x
|
||||
set x [exec $icuConfigBin --cppflags]
|
||||
define-append CFLAGS_LIBICU $x
|
||||
} else {
|
||||
proj-fatal "--with-icu-config=$icuConfigBin does not refer to an executable"
|
||||
}
|
||||
}
|
||||
}
|
||||
set ldflags [define LDFLAGS_LIBICU [string trim [get-define LDFLAGS_LIBICU]]]
|
||||
set cflags [define CFLAGS_LIBICU [string trim [get-define CFLAGS_LIBICU]]]
|
||||
if {"" ne $ldflags} {
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_ICU
|
||||
msg-result "Enabling ICU support with flags: $ldflags $cflags"
|
||||
if {[opt-bool icu-collations]} {
|
||||
msg-result "Enabling ICU collations."
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_ICU_COLLATIONS
|
||||
}
|
||||
teaish-ldflags-prepend $ldflags
|
||||
teaish-cflags-add $cflags
|
||||
} elseif {[opt-bool icu-collations]} {
|
||||
proj-warn "ignoring --enable-icu-collations because neither --with-icu-ldflags nor --with-icu-config provided any linker flags"
|
||||
} else {
|
||||
msg-result "ICU support is disabled."
|
||||
}
|
||||
}; # sqlite-handle-icu
|
||||
|
||||
|
||||
#
|
||||
# Handles the --with-tempstore flag.
|
||||
#
|
||||
# The test fixture likes to set SQLITE_TEMP_STORE on its own, so do
|
||||
# not set that feature flag unless it was explicitly provided to the
|
||||
# configure script.
|
||||
proc sqlite-handle-tempstore {} {
|
||||
if {[proj-opt-was-provided with-tempstore]} {
|
||||
set ts [opt-val with-tempstore no]
|
||||
set tsn 1
|
||||
msg-checking "Use an in-RAM database for temporary tables? "
|
||||
switch -exact -- $ts {
|
||||
never { set tsn 0 }
|
||||
no { set tsn 1 }
|
||||
yes { set tsn 2 }
|
||||
always { set tsn 3 }
|
||||
default {
|
||||
user-error "Invalid --with-tempstore value '$ts'. Use one of: never, no, yes, always"
|
||||
}
|
||||
}
|
||||
msg-result $ts
|
||||
sqlite-add-feature-flag -DSQLITE_TEMP_STORE=$tsn
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Handles the --enable-math flag.
|
||||
proc sqlite-handle-math {} {
|
||||
proj-if-opt-truthy math {
|
||||
if {![proj-check-function-in-lib ceil m]} {
|
||||
user-error "Cannot find libm functions. Use --disable-math to bypass this."
|
||||
}
|
||||
set lfl [get-define lib_ceil]
|
||||
undefine lib_ceil
|
||||
define LDFLAGS_MATH $lfl
|
||||
teaish-ldflags-prepend $lfl
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_MATH_FUNCTIONS
|
||||
msg-result "Enabling math SQL functions"
|
||||
} {
|
||||
define LDFLAGS_MATH ""
|
||||
msg-result "Disabling math SQL functions"
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Move -DSQLITE_OMIT... and -DSQLITE_ENABLE... flags from CFLAGS and
|
||||
# CPPFLAGS to OPT_FEATURE_FLAGS and remove them from BUILD_CFLAGS.
|
||||
proc sqlite-munge-cflags {} {
|
||||
# Move CFLAGS and CPPFLAGS entries matching -DSQLITE_OMIT* and
|
||||
# -DSQLITE_ENABLE* to OPT_FEATURE_FLAGS. This behavior is derived
|
||||
# from the pre-3.48 build.
|
||||
#
|
||||
# If any configure flags for features are in conflict with
|
||||
# CFLAGS/CPPFLAGS-specified feature flags, all bets are off. There
|
||||
# are no guarantees about which one will take precedence.
|
||||
foreach flagDef {CFLAGS CPPFLAGS} {
|
||||
set tmp ""
|
||||
foreach cf [get-define $flagDef ""] {
|
||||
switch -glob -- $cf {
|
||||
-DSQLITE_OMIT* -
|
||||
-DSQLITE_ENABLE* {
|
||||
sqlite-add-feature-flag $cf
|
||||
}
|
||||
default {
|
||||
lappend tmp $cf
|
||||
}
|
||||
}
|
||||
}
|
||||
define $flagDef $tmp
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
test-expect 1.0-open {
|
||||
sqlite3 db :memory:
|
||||
} {}
|
||||
|
||||
test-assert 1.1-version-3.x {
|
||||
[string match 3.* [db eval {select sqlite_version()}]]
|
||||
}
|
||||
|
||||
test-expect 1.2-select {
|
||||
db eval {select 'hi, world',1,2,3}
|
||||
} {{hi, world} 1 2 3}
|
||||
|
||||
|
||||
test-expect 99.0-db-close {db close} {}
|
||||
@@ -1,61 +0,0 @@
|
||||
#------------------------------------------------------------- -*- makefile -*-
|
||||
#
|
||||
# Sample makefile for building Tcl extensions.
|
||||
#
|
||||
# Basic build, test and install
|
||||
# nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl
|
||||
# nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl test
|
||||
# nmake /s /nologo /f makefile.vc INSTALLDIR=c:\path\to\tcl install
|
||||
#
|
||||
# For other build options (debug, static etc.)
|
||||
# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
|
||||
# detailed documentation.
|
||||
#
|
||||
# See the file "license.terms" for information on usage and redistribution
|
||||
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# PROJECT is sqlite, not sqlite3 to match TEA AC_INIT definition.
|
||||
# This makes the generated DLL name also consistent between the two
|
||||
# except for the "t" suffix which is the convention for nmake builds.
|
||||
PROJECT = sqlite
|
||||
PRJ_PACKAGE_TCLNAME = sqlite3
|
||||
|
||||
!include "rules-ext.vc"
|
||||
|
||||
PRJ_OBJS = $(TMP_DIR)\tclsqlite3.obj
|
||||
|
||||
# Preprocessor macros specific to sqlite3.
|
||||
PRJ_DEFINES = -I"$(ROOT)\.." -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE \
|
||||
-DSQLITE_ENABLE_DBPAGE_VTAB=1 -DSQLITE_ENABLE_DBSTAT_VTAB=1 \
|
||||
-DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS4=1 \
|
||||
-DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 \
|
||||
-DSQLITE_ENABLE_JSON1=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 \
|
||||
-DSQLITE_3_SUFFIX_ONLY=1 -DSQLITE_ENABLE_RTREE=1 \
|
||||
-DSQLITE_UNTESTABLE=1 -DSQLITE_OMIT_LOOKASIDE=1 \
|
||||
-DSQLITE_SECURE_DELETE=1 -DSQLITE_SOUNDEX=1 -DSQLITE_ENABLE_GEOPOLY=1 \
|
||||
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 \
|
||||
-DSQLITE_ENABLE_MATH_FUNCTIONS=1 -DDSQLITE_USE_ALLOCA=1 \
|
||||
-DSQLITE_ENABLE_STAT4=1 -DSQLITE_OMIT_DEPRECATED=1 \
|
||||
-DSQLITE_WIN32_GETVERSIONEX=0 -DSQLITE_WIN32_NO_ANSI=1
|
||||
PRJ_DEFINES = $(PRJ_DEFINES) -I$(TMP_DIR)
|
||||
|
||||
# Standard targets to build, install, test etc.
|
||||
!include "$(_RULESDIR)\targets.vc"
|
||||
|
||||
# The built-in pkgindex does no suffice for our extension as
|
||||
# the PROJECT name (sqlite) is not same as init function name (Sqlite3)
|
||||
pkgindex:
|
||||
@echo if {[package vsatisfies [package provide Tcl] 9.0-]} { > $(OUT_DIR)\pkgIndex.tcl
|
||||
@echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
|
||||
[list load [file join $$dir $(PRJLIBNAME9)] [string totitle $(PRJ_PACKAGE_TCLNAME)]] >> $(OUT_DIR)\pkgIndex.tcl
|
||||
@echo } else { >> $(OUT_DIR)\pkgIndex.tcl
|
||||
@echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
|
||||
[list load [file join $$dir $(PRJLIBNAME8)] [string totitle $(PRJ_PACKAGE_TCLNAME)]] >> $(OUT_DIR)\pkgIndex.tcl
|
||||
@echo } >> $(OUT_DIR)\pkgIndex.tcl
|
||||
|
||||
# Install the manpage though on Windows, doubt it does much good
|
||||
install: default-install-docs-n
|
||||
|
||||
# Explicit dependency rules
|
||||
@@ -1,815 +0,0 @@
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
* nmakehlp.c --
|
||||
*
|
||||
* This is used to fix limitations within nmake and the environment.
|
||||
*
|
||||
* Copyright (c) 2002 by David Gravereaux.
|
||||
* Copyright (c) 2006 by Pat Thoyts
|
||||
*
|
||||
* See the file "license.terms" for information on usage and redistribution of
|
||||
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#include <windows.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment (lib, "user32.lib")
|
||||
#pragma comment (lib, "kernel32.lib")
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* This library is required for x64 builds with _some_ versions of MSVC
|
||||
*/
|
||||
#if defined(_M_IA64) || defined(_M_AMD64)
|
||||
#if _MSC_VER >= 1400 && _MSC_VER < 1500
|
||||
#pragma comment(lib, "bufferoverflowU")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ISO hack for dumb VC++ */
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
/* protos */
|
||||
|
||||
static int CheckForCompilerFeature(const char *option);
|
||||
static int CheckForLinkerFeature(char **options, int count);
|
||||
static int IsIn(const char *string, const char *substring);
|
||||
static int SubstituteFile(const char *substs, const char *filename);
|
||||
static int QualifyPath(const char *path);
|
||||
static int LocateDependency(const char *keyfile);
|
||||
static const char *GetVersionFromFile(const char *filename, const char *match, int numdots);
|
||||
static DWORD WINAPI ReadFromPipe(LPVOID args);
|
||||
|
||||
/* globals */
|
||||
|
||||
#define CHUNK 25
|
||||
#define STATICBUFFERSIZE 1000
|
||||
typedef struct {
|
||||
HANDLE pipe;
|
||||
char buffer[STATICBUFFERSIZE];
|
||||
} pipeinfo;
|
||||
|
||||
pipeinfo Out = {INVALID_HANDLE_VALUE, ""};
|
||||
pipeinfo Err = {INVALID_HANDLE_VALUE, ""};
|
||||
|
||||
/*
|
||||
* exitcodes: 0 == no, 1 == yes, 2 == error
|
||||
*/
|
||||
|
||||
int
|
||||
main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
char msg[300];
|
||||
DWORD dwWritten;
|
||||
int chars;
|
||||
const char *s;
|
||||
|
||||
/*
|
||||
* Make sure children (cl.exe and link.exe) are kept quiet.
|
||||
*/
|
||||
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
||||
|
||||
/*
|
||||
* Make sure the compiler and linker aren't effected by the outside world.
|
||||
*/
|
||||
|
||||
SetEnvironmentVariable("CL", "");
|
||||
SetEnvironmentVariable("LINK", "");
|
||||
|
||||
if (argc > 1 && *argv[1] == '-') {
|
||||
switch (*(argv[1]+1)) {
|
||||
case 'c':
|
||||
if (argc != 3) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -c <compiler option>\n"
|
||||
"Tests for whether cl.exe supports an option\n"
|
||||
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 2;
|
||||
}
|
||||
return CheckForCompilerFeature(argv[2]);
|
||||
case 'l':
|
||||
if (argc < 3) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -l <linker option> ?<mandatory option> ...?\n"
|
||||
"Tests for whether link.exe supports an option\n"
|
||||
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 2;
|
||||
}
|
||||
return CheckForLinkerFeature(&argv[2], argc-2);
|
||||
case 'f':
|
||||
if (argc == 2) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -f <string> <substring>\n"
|
||||
"Find a substring within another\n"
|
||||
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 2;
|
||||
} else if (argc == 3) {
|
||||
/*
|
||||
* If the string is blank, there is no match.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return IsIn(argv[2], argv[3]);
|
||||
}
|
||||
case 's':
|
||||
if (argc == 2) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -s <substitutions file> <file>\n"
|
||||
"Perform a set of string map type substutitions on a file\n"
|
||||
"exitcodes: 0\n",
|
||||
argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 2;
|
||||
}
|
||||
return SubstituteFile(argv[2], argv[3]);
|
||||
case 'V':
|
||||
if (argc != 4) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -V filename matchstring\n"
|
||||
"Extract a version from a file:\n"
|
||||
"eg: pkgIndex.tcl \"package ifneeded http\"",
|
||||
argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 0;
|
||||
}
|
||||
s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0');
|
||||
if (s && *s) {
|
||||
printf("%s\n", s);
|
||||
return 0;
|
||||
} else
|
||||
return 1; /* Version not found. Return non-0 exit code */
|
||||
|
||||
case 'Q':
|
||||
if (argc != 3) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -Q path\n"
|
||||
"Emit the fully qualified path\n"
|
||||
"exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 2;
|
||||
}
|
||||
return QualifyPath(argv[2]);
|
||||
|
||||
case 'L':
|
||||
if (argc != 3) {
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -L keypath\n"
|
||||
"Emit the fully qualified path of directory containing keypath\n"
|
||||
"exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
|
||||
&dwWritten, NULL);
|
||||
return 2;
|
||||
}
|
||||
return LocateDependency(argv[2]);
|
||||
}
|
||||
}
|
||||
chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"usage: %s -c|-f|-l|-Q|-s|-V ...\n"
|
||||
"This is a little helper app to equalize shell differences between WinNT and\n"
|
||||
"Win9x and get nmake.exe to accomplish its job.\n",
|
||||
argv[0]);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int
|
||||
CheckForCompilerFeature(
|
||||
const char *option)
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
DWORD threadID;
|
||||
char msg[300];
|
||||
BOOL ok;
|
||||
HANDLE hProcess, h, pipeThreads[2];
|
||||
char cmdline[100];
|
||||
|
||||
hProcess = GetCurrentProcess();
|
||||
|
||||
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = INVALID_HANDLE_VALUE;
|
||||
|
||||
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = FALSE;
|
||||
|
||||
/*
|
||||
* Create a non-inheritible pipe.
|
||||
*/
|
||||
|
||||
CreatePipe(&Out.pipe, &h, &sa, 0);
|
||||
|
||||
/*
|
||||
* Dupe the write side, make it inheritible, and close the original.
|
||||
*/
|
||||
|
||||
DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
|
||||
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
||||
|
||||
/*
|
||||
* Same as above, but for the error side.
|
||||
*/
|
||||
|
||||
CreatePipe(&Err.pipe, &h, &sa, 0);
|
||||
DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
|
||||
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
||||
|
||||
/*
|
||||
* Base command line.
|
||||
*/
|
||||
|
||||
lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
|
||||
|
||||
/*
|
||||
* Append our option for testing
|
||||
*/
|
||||
|
||||
lstrcat(cmdline, option);
|
||||
|
||||
/*
|
||||
* Filename to compile, which exists, but is nothing and empty.
|
||||
*/
|
||||
|
||||
lstrcat(cmdline, " .\\nul");
|
||||
|
||||
ok = CreateProcess(
|
||||
NULL, /* Module name. */
|
||||
cmdline, /* Command line. */
|
||||
NULL, /* Process handle not inheritable. */
|
||||
NULL, /* Thread handle not inheritable. */
|
||||
TRUE, /* yes, inherit handles. */
|
||||
DETACHED_PROCESS, /* No console for you. */
|
||||
NULL, /* Use parent's environment block. */
|
||||
NULL, /* Use parent's starting directory. */
|
||||
&si, /* Pointer to STARTUPINFO structure. */
|
||||
&pi); /* Pointer to PROCESS_INFORMATION structure. */
|
||||
|
||||
if (!ok) {
|
||||
DWORD err = GetLastError();
|
||||
int chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPSTR)&msg[chars],
|
||||
(300-chars), 0);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close our references to the write handles that have now been inherited.
|
||||
*/
|
||||
|
||||
CloseHandle(si.hStdOutput);
|
||||
CloseHandle(si.hStdError);
|
||||
|
||||
WaitForInputIdle(pi.hProcess, 5000);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
/*
|
||||
* Start the pipe reader threads.
|
||||
*/
|
||||
|
||||
pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
|
||||
pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
|
||||
|
||||
/*
|
||||
* Block waiting for the process to end.
|
||||
*/
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
/*
|
||||
* Wait for our pipe to get done reading, should it be a little slow.
|
||||
*/
|
||||
|
||||
WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
|
||||
CloseHandle(pipeThreads[0]);
|
||||
CloseHandle(pipeThreads[1]);
|
||||
|
||||
/*
|
||||
* Look for the commandline warning code in both streams.
|
||||
* - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
|
||||
*/
|
||||
|
||||
return !(strstr(Out.buffer, "D4002") != NULL
|
||||
|| strstr(Err.buffer, "D4002") != NULL
|
||||
|| strstr(Out.buffer, "D9002") != NULL
|
||||
|| strstr(Err.buffer, "D9002") != NULL
|
||||
|| strstr(Out.buffer, "D2021") != NULL
|
||||
|| strstr(Err.buffer, "D2021") != NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
CheckForLinkerFeature(
|
||||
char **options,
|
||||
int count)
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
DWORD threadID;
|
||||
char msg[300];
|
||||
BOOL ok;
|
||||
HANDLE hProcess, h, pipeThreads[2];
|
||||
int i;
|
||||
char cmdline[255];
|
||||
|
||||
hProcess = GetCurrentProcess();
|
||||
|
||||
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
||||
ZeroMemory(&si, sizeof(STARTUPINFO));
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = INVALID_HANDLE_VALUE;
|
||||
|
||||
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
|
||||
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
sa.lpSecurityDescriptor = NULL;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
/*
|
||||
* Create a non-inheritible pipe.
|
||||
*/
|
||||
|
||||
CreatePipe(&Out.pipe, &h, &sa, 0);
|
||||
|
||||
/*
|
||||
* Dupe the write side, make it inheritible, and close the original.
|
||||
*/
|
||||
|
||||
DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
|
||||
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
||||
|
||||
/*
|
||||
* Same as above, but for the error side.
|
||||
*/
|
||||
|
||||
CreatePipe(&Err.pipe, &h, &sa, 0);
|
||||
DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
|
||||
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
||||
|
||||
/*
|
||||
* Base command line.
|
||||
*/
|
||||
|
||||
lstrcpy(cmdline, "link.exe -nologo ");
|
||||
|
||||
/*
|
||||
* Append our option for testing.
|
||||
*/
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
lstrcat(cmdline, " \"");
|
||||
lstrcat(cmdline, options[i]);
|
||||
lstrcat(cmdline, "\"");
|
||||
}
|
||||
|
||||
ok = CreateProcess(
|
||||
NULL, /* Module name. */
|
||||
cmdline, /* Command line. */
|
||||
NULL, /* Process handle not inheritable. */
|
||||
NULL, /* Thread handle not inheritable. */
|
||||
TRUE, /* yes, inherit handles. */
|
||||
DETACHED_PROCESS, /* No console for you. */
|
||||
NULL, /* Use parent's environment block. */
|
||||
NULL, /* Use parent's starting directory. */
|
||||
&si, /* Pointer to STARTUPINFO structure. */
|
||||
&pi); /* Pointer to PROCESS_INFORMATION structure. */
|
||||
|
||||
if (!ok) {
|
||||
DWORD err = GetLastError();
|
||||
int chars = snprintf(msg, sizeof(msg) - 1,
|
||||
"Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPSTR)&msg[chars],
|
||||
(300-chars), 0);
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close our references to the write handles that have now been inherited.
|
||||
*/
|
||||
|
||||
CloseHandle(si.hStdOutput);
|
||||
CloseHandle(si.hStdError);
|
||||
|
||||
WaitForInputIdle(pi.hProcess, 5000);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
/*
|
||||
* Start the pipe reader threads.
|
||||
*/
|
||||
|
||||
pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
|
||||
pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
|
||||
|
||||
/*
|
||||
* Block waiting for the process to end.
|
||||
*/
|
||||
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
/*
|
||||
* Wait for our pipe to get done reading, should it be a little slow.
|
||||
*/
|
||||
|
||||
WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
|
||||
CloseHandle(pipeThreads[0]);
|
||||
CloseHandle(pipeThreads[1]);
|
||||
|
||||
/*
|
||||
* Look for the commandline warning code in the stderr stream.
|
||||
*/
|
||||
|
||||
return !(strstr(Out.buffer, "LNK1117") != NULL ||
|
||||
strstr(Err.buffer, "LNK1117") != NULL ||
|
||||
strstr(Out.buffer, "LNK4044") != NULL ||
|
||||
strstr(Err.buffer, "LNK4044") != NULL ||
|
||||
strstr(Out.buffer, "LNK4224") != NULL ||
|
||||
strstr(Err.buffer, "LNK4224") != NULL);
|
||||
}
|
||||
|
||||
static DWORD WINAPI
|
||||
ReadFromPipe(
|
||||
LPVOID args)
|
||||
{
|
||||
pipeinfo *pi = (pipeinfo *) args;
|
||||
char *lastBuf = pi->buffer;
|
||||
DWORD dwRead;
|
||||
BOOL ok;
|
||||
|
||||
again:
|
||||
if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
|
||||
CloseHandle(pi->pipe);
|
||||
return (DWORD)-1;
|
||||
}
|
||||
ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
|
||||
if (!ok || dwRead == 0) {
|
||||
CloseHandle(pi->pipe);
|
||||
return 0;
|
||||
}
|
||||
lastBuf += dwRead;
|
||||
goto again;
|
||||
|
||||
return 0; /* makes the compiler happy */
|
||||
}
|
||||
|
||||
static int
|
||||
IsIn(
|
||||
const char *string,
|
||||
const char *substring)
|
||||
{
|
||||
return (strstr(string, substring) != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* GetVersionFromFile --
|
||||
* Looks for a match string in a file and then returns the version
|
||||
* following the match where a version is anything acceptable to
|
||||
* package provide or package ifneeded.
|
||||
*/
|
||||
|
||||
static const char *
|
||||
GetVersionFromFile(
|
||||
const char *filename,
|
||||
const char *match,
|
||||
int numdots)
|
||||
{
|
||||
static char szBuffer[100];
|
||||
char *szResult = NULL;
|
||||
FILE *fp = fopen(filename, "rt");
|
||||
|
||||
if (fp != NULL) {
|
||||
/*
|
||||
* Read data until we see our match string.
|
||||
*/
|
||||
|
||||
while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) {
|
||||
LPSTR p, q;
|
||||
|
||||
p = strstr(szBuffer, match);
|
||||
if (p != NULL) {
|
||||
/*
|
||||
* Skip to first digit after the match.
|
||||
*/
|
||||
|
||||
p += strlen(match);
|
||||
while (*p && !isdigit((unsigned char)*p)) {
|
||||
++p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find ending whitespace.
|
||||
*/
|
||||
|
||||
q = p;
|
||||
while (*q && (strchr("0123456789.ab", *q)) && (((!strchr(".ab", *q)
|
||||
&& !strchr("ab", q[-1])) || --numdots))) {
|
||||
++q;
|
||||
}
|
||||
|
||||
*q = 0;
|
||||
szResult = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
return szResult;
|
||||
}
|
||||
|
||||
/*
|
||||
* List helpers for the SubstituteFile function
|
||||
*/
|
||||
|
||||
typedef struct list_item_t {
|
||||
struct list_item_t *nextPtr;
|
||||
char * key;
|
||||
char * value;
|
||||
} list_item_t;
|
||||
|
||||
/* insert a list item into the list (list may be null) */
|
||||
static list_item_t *
|
||||
list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
|
||||
{
|
||||
list_item_t *itemPtr = (list_item_t *)malloc(sizeof(list_item_t));
|
||||
if (itemPtr) {
|
||||
itemPtr->key = strdup(key);
|
||||
itemPtr->value = strdup(value);
|
||||
itemPtr->nextPtr = NULL;
|
||||
|
||||
while(*listPtrPtr) {
|
||||
listPtrPtr = &(*listPtrPtr)->nextPtr;
|
||||
}
|
||||
*listPtrPtr = itemPtr;
|
||||
}
|
||||
return itemPtr;
|
||||
}
|
||||
|
||||
static void
|
||||
list_free(list_item_t **listPtrPtr)
|
||||
{
|
||||
list_item_t *tmpPtr, *listPtr = *listPtrPtr;
|
||||
while (listPtr) {
|
||||
tmpPtr = listPtr;
|
||||
listPtr = listPtr->nextPtr;
|
||||
free(tmpPtr->key);
|
||||
free(tmpPtr->value);
|
||||
free(tmpPtr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SubstituteFile --
|
||||
* As windows doesn't provide anything useful like sed and it's unreliable
|
||||
* to use the tclsh you are building against (consider x-platform builds -
|
||||
* eg compiling AMD64 target from IX86) we provide a simple substitution
|
||||
* option here to handle autoconf style substitutions.
|
||||
* The substitution file is whitespace and line delimited. The file should
|
||||
* consist of lines matching the regular expression:
|
||||
* \s*\S+\s+\S*$
|
||||
*
|
||||
* Usage is something like:
|
||||
* nmakehlp -S << $** > $@
|
||||
* @PACKAGE_NAME@ $(PACKAGE_NAME)
|
||||
* @PACKAGE_VERSION@ $(PACKAGE_VERSION)
|
||||
* <<
|
||||
*/
|
||||
|
||||
static int
|
||||
SubstituteFile(
|
||||
const char *substitutions,
|
||||
const char *filename)
|
||||
{
|
||||
static char szBuffer[1024], szCopy[1024];
|
||||
list_item_t *substPtr = NULL;
|
||||
FILE *fp, *sp;
|
||||
|
||||
fp = fopen(filename, "rt");
|
||||
if (fp != NULL) {
|
||||
|
||||
/*
|
||||
* Build a list of substutitions from the first filename
|
||||
*/
|
||||
|
||||
sp = fopen(substitutions, "rt");
|
||||
if (sp != NULL) {
|
||||
while (fgets(szBuffer, sizeof(szBuffer), sp) != NULL) {
|
||||
unsigned char *ks, *ke, *vs, *ve;
|
||||
ks = (unsigned char*)szBuffer;
|
||||
while (ks && *ks && isspace(*ks)) ++ks;
|
||||
ke = ks;
|
||||
while (ke && *ke && !isspace(*ke)) ++ke;
|
||||
vs = ke;
|
||||
while (vs && *vs && isspace(*vs)) ++vs;
|
||||
ve = vs;
|
||||
while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
|
||||
*ke = 0, *ve = 0;
|
||||
list_insert(&substPtr, (char*)ks, (char*)vs);
|
||||
}
|
||||
fclose(sp);
|
||||
}
|
||||
|
||||
/* debug: dump the list */
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
int n = 0;
|
||||
list_item_t *p = NULL;
|
||||
for (p = substPtr; p != NULL; p = p->nextPtr, ++n) {
|
||||
fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Run the substitutions over each line of the input
|
||||
*/
|
||||
|
||||
while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) {
|
||||
list_item_t *p = NULL;
|
||||
for (p = substPtr; p != NULL; p = p->nextPtr) {
|
||||
char *m = strstr(szBuffer, p->key);
|
||||
if (m) {
|
||||
char *cp, *op, *sp;
|
||||
cp = szCopy;
|
||||
op = szBuffer;
|
||||
while (op != m) *cp++ = *op++;
|
||||
sp = p->value;
|
||||
while (sp && *sp) *cp++ = *sp++;
|
||||
op += strlen(p->key);
|
||||
while (*op) *cp++ = *op++;
|
||||
*cp = 0;
|
||||
memcpy(szBuffer, szCopy, sizeof(szCopy));
|
||||
}
|
||||
}
|
||||
printf("%s", szBuffer);
|
||||
}
|
||||
|
||||
list_free(&substPtr);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL FileExists(LPCTSTR szPath)
|
||||
{
|
||||
#ifndef INVALID_FILE_ATTRIBUTES
|
||||
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
||||
#endif
|
||||
DWORD pathAttr = GetFileAttributes(szPath);
|
||||
return (pathAttr != INVALID_FILE_ATTRIBUTES &&
|
||||
!(pathAttr & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* QualifyPath --
|
||||
*
|
||||
* This composes the current working directory with a provided path
|
||||
* and returns the fully qualified and normalized path.
|
||||
* Mostly needed to setup paths for testing.
|
||||
*/
|
||||
|
||||
static int
|
||||
QualifyPath(
|
||||
const char *szPath)
|
||||
{
|
||||
char szCwd[MAX_PATH + 1];
|
||||
|
||||
GetFullPathName(szPath, sizeof(szCwd)-1, szCwd, NULL);
|
||||
printf("%s\n", szCwd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements LocateDependency for a single directory. See that command
|
||||
* for an explanation.
|
||||
* Returns 0 if found after printing the directory.
|
||||
* Returns 1 if not found but no errors.
|
||||
* Returns 2 on any kind of error
|
||||
* Basically, these are used as exit codes for the process.
|
||||
*/
|
||||
static int LocateDependencyHelper(const char *dir, const char *keypath)
|
||||
{
|
||||
HANDLE hSearch;
|
||||
char path[MAX_PATH+1];
|
||||
size_t dirlen;
|
||||
int keylen, ret;
|
||||
WIN32_FIND_DATA finfo;
|
||||
|
||||
if (dir == NULL || keypath == NULL)
|
||||
return 2; /* Have no real error reporting mechanism into nmake */
|
||||
dirlen = strlen(dir);
|
||||
if ((dirlen + 3) > sizeof(path))
|
||||
return 2;
|
||||
strncpy(path, dir, dirlen);
|
||||
strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */
|
||||
keylen = strlen(keypath);
|
||||
|
||||
#if 0 /* This function is not available in Visual C++ 6 */
|
||||
/*
|
||||
* Use numerics 0 -> FindExInfoStandard,
|
||||
* 1 -> FindExSearchLimitToDirectories,
|
||||
* as these are not defined in Visual C++ 6
|
||||
*/
|
||||
hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0);
|
||||
#else
|
||||
hSearch = FindFirstFile(path, &finfo);
|
||||
#endif
|
||||
if (hSearch == INVALID_HANDLE_VALUE)
|
||||
return 1; /* Not found */
|
||||
|
||||
/* Loop through all subdirs checking if the keypath is under there */
|
||||
ret = 1; /* Assume not found */
|
||||
do {
|
||||
int sublen;
|
||||
/*
|
||||
* We need to check it is a directory despite the
|
||||
* FindExSearchLimitToDirectories in the above call. See SDK docs
|
||||
*/
|
||||
if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
||||
continue;
|
||||
sublen = strlen(finfo.cFileName);
|
||||
if ((dirlen+1+sublen+1+keylen+1) > sizeof(path))
|
||||
continue; /* Path does not fit, assume not matched */
|
||||
strncpy(path+dirlen+1, finfo.cFileName, sublen);
|
||||
path[dirlen+1+sublen] = '\\';
|
||||
strncpy(path+dirlen+1+sublen+1, keypath, keylen+1);
|
||||
if (FileExists(path)) {
|
||||
/* Found a match, print to stdout */
|
||||
path[dirlen+1+sublen] = '\0';
|
||||
QualifyPath(path);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
} while (FindNextFile(hSearch, &finfo));
|
||||
FindClose(hSearch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* LocateDependency --
|
||||
*
|
||||
* Locates a dependency for a package.
|
||||
* keypath - a relative path within the package directory
|
||||
* that is used to confirm it is the correct directory.
|
||||
* The search path for the package directory is currently only
|
||||
* the parent and grandparent of the current working directory.
|
||||
* If found, the command prints
|
||||
* name_DIRPATH=<full path of located directory>
|
||||
* and returns 0. If not found, does not print anything and returns 1.
|
||||
*/
|
||||
static int LocateDependency(const char *keypath)
|
||||
{
|
||||
size_t i;
|
||||
int ret;
|
||||
static const char *paths[] = {"..", "..\\..", "..\\..\\.."};
|
||||
|
||||
for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) {
|
||||
ret = LocateDependencyHelper(paths[i], keypath);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: c
|
||||
* c-basic-offset: 4
|
||||
* fill-column: 78
|
||||
* indent-tabs-mode: t
|
||||
* tab-width: 8
|
||||
* End:
|
||||
*/
|
||||
@@ -1,123 +0,0 @@
|
||||
# This file should only be included in makefiles for Tcl extensions,
|
||||
# NOT in the makefile for Tcl itself.
|
||||
|
||||
!ifndef _RULES_EXT_VC
|
||||
|
||||
# We need to run from the directory the parent makefile is located in.
|
||||
# nmake does not tell us what makefile was used to invoke it so parent
|
||||
# makefile has to set the MAKEFILEVC macro or we just make a guess and
|
||||
# warn if we think that is not the case.
|
||||
!if "$(MAKEFILEVC)" == ""
|
||||
|
||||
!if exist("$(PROJECT).vc")
|
||||
MAKEFILEVC = $(PROJECT).vc
|
||||
!elseif exist("makefile.vc")
|
||||
MAKEFILEVC = makefile.vc
|
||||
!endif
|
||||
!endif # "$(MAKEFILEVC)" == ""
|
||||
|
||||
!if !exist("$(MAKEFILEVC)")
|
||||
MSG = ^
|
||||
You must run nmake from the directory containing the project makefile.^
|
||||
If you are doing that and getting this message, set the MAKEFILEVC^
|
||||
macro to the name of the project makefile.
|
||||
!message WARNING: $(MSG)
|
||||
!endif
|
||||
|
||||
!if "$(PROJECT)" == "tcl"
|
||||
!error The rules-ext.vc file is not intended for Tcl itself.
|
||||
!endif
|
||||
|
||||
# We extract version numbers using the nmakehlp program. For now use
|
||||
# the local copy of nmakehlp. Once we locate Tcl, we will use that
|
||||
# one if it is newer.
|
||||
!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
|
||||
!if [$(CC) -nologo -DNDEBUG "nmakehlp.c" -link -subsystem:console > nul]
|
||||
!endif
|
||||
!else
|
||||
!if [copy x86_64-w64-mingw32-nmakehlp.exe nmakehlp.exe >NUL]
|
||||
!endif
|
||||
!endif
|
||||
|
||||
# First locate the Tcl directory that we are working with.
|
||||
!if "$(TCLDIR)" != ""
|
||||
|
||||
_RULESDIR = $(TCLDIR:/=\)
|
||||
|
||||
!else
|
||||
|
||||
# If an installation path is specified, that is also the Tcl directory.
|
||||
# Also Tk never builds against an installed Tcl, it needs Tcl sources
|
||||
!if defined(INSTALLDIR) && "$(PROJECT)" != "tk"
|
||||
_RULESDIR=$(INSTALLDIR:/=\)
|
||||
!else
|
||||
# Locate Tcl sources
|
||||
!if [echo _RULESDIR = \> nmakehlp.out] \
|
||||
|| [nmakehlp -L generic\tcl.h >> nmakehlp.out]
|
||||
_RULESDIR = ..\..\tcl
|
||||
!else
|
||||
!include nmakehlp.out
|
||||
!endif
|
||||
|
||||
!endif # defined(INSTALLDIR)....
|
||||
|
||||
!endif # ifndef TCLDIR
|
||||
|
||||
# Now look for the targets.vc file under the Tcl root. Note we check this
|
||||
# file and not rules.vc because the latter also exists on older systems.
|
||||
!if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl
|
||||
_RULESDIR = $(_RULESDIR)\lib\nmake
|
||||
!elseif exist("$(_RULESDIR)\win\targets.vc") # Building against Tcl sources
|
||||
_RULESDIR = $(_RULESDIR)\win
|
||||
!else
|
||||
# If we have not located Tcl's targets file, most likely we are compiling
|
||||
# against an older version of Tcl and so must use our own support files.
|
||||
_RULESDIR = .
|
||||
!endif
|
||||
|
||||
!if "$(_RULESDIR)" != "."
|
||||
# Potentially using Tcl's support files. If this extension has its own
|
||||
# nmake support files, need to compare the versions and pick newer.
|
||||
|
||||
!if exist("rules.vc") # The extension has its own copy
|
||||
|
||||
!if [echo TCL_RULES_MAJOR = \> versions.vc] \
|
||||
&& [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc]
|
||||
!endif
|
||||
!if [echo TCL_RULES_MINOR = \>> versions.vc] \
|
||||
&& [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc]
|
||||
!endif
|
||||
|
||||
!if [echo OUR_RULES_MAJOR = \>> versions.vc] \
|
||||
&& [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc]
|
||||
!endif
|
||||
!if [echo OUR_RULES_MINOR = \>> versions.vc] \
|
||||
&& [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc]
|
||||
!endif
|
||||
!include versions.vc
|
||||
# We have a newer version of the support files, use them
|
||||
!if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR))
|
||||
_RULESDIR = .
|
||||
!endif
|
||||
|
||||
!endif # if exist("rules.vc")
|
||||
|
||||
!endif # if $(_RULESDIR) != "."
|
||||
|
||||
# Let rules.vc know what copy of nmakehlp.c to use.
|
||||
NMAKEHLPC = $(_RULESDIR)\nmakehlp.c
|
||||
|
||||
# Get rid of our internal defines before calling rules.vc
|
||||
!undef TCL_RULES_MAJOR
|
||||
!undef TCL_RULES_MINOR
|
||||
!undef OUR_RULES_MAJOR
|
||||
!undef OUR_RULES_MINOR
|
||||
|
||||
!if exist("$(_RULESDIR)\rules.vc")
|
||||
!message *** Using $(_RULESDIR)\rules.vc
|
||||
!include "$(_RULESDIR)\rules.vc"
|
||||
!else
|
||||
!error *** Could not locate rules.vc in $(_RULESDIR)
|
||||
!endif
|
||||
|
||||
!endif # _RULES_EXT_VC
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,98 +0,0 @@
|
||||
#------------------------------------------------------------- -*- makefile -*-
|
||||
# targets.vc --
|
||||
#
|
||||
# Part of the nmake based build system for Tcl and its extensions.
|
||||
# This file defines some standard targets for the convenience of extensions
|
||||
# and can be optionally included by the extension makefile.
|
||||
# See TIP 477 (https://core.tcl-lang.org/tips/doc/main/tip/477.md) for docs.
|
||||
|
||||
$(PROJECT): setup pkgindex $(PRJLIB)
|
||||
|
||||
!ifdef PRJ_STUBOBJS
|
||||
$(PROJECT): $(PRJSTUBLIB)
|
||||
$(PRJSTUBLIB): $(PRJ_STUBOBJS)
|
||||
$(LIBCMD) $**
|
||||
|
||||
$(PRJ_STUBOBJS):
|
||||
$(CCSTUBSCMD) %s
|
||||
!endif # PRJ_STUBOBJS
|
||||
|
||||
!ifdef PRJ_MANIFEST
|
||||
$(PROJECT): $(PRJLIB).manifest
|
||||
$(PRJLIB).manifest: $(PRJ_MANIFEST)
|
||||
@nmakehlp -s << $** >$@
|
||||
@MACHINE@ $(MACHINE:IX86=X86)
|
||||
<<
|
||||
!endif
|
||||
|
||||
!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk"
|
||||
$(PRJLIB): $(PRJ_OBJS) $(RESFILE)
|
||||
!if $(STATIC_BUILD)
|
||||
$(LIBCMD) $**
|
||||
!else
|
||||
$(DLLCMD) $**
|
||||
$(_VC_MANIFEST_EMBED_DLL)
|
||||
!endif
|
||||
-@del $*.exp
|
||||
!endif
|
||||
|
||||
!if "$(PRJ_HEADERS)" != "" && "$(PRJ_OBJS)" != ""
|
||||
$(PRJ_OBJS): $(PRJ_HEADERS)
|
||||
!endif
|
||||
|
||||
# If parent makefile has defined stub objects, add their installation
|
||||
# to the default install
|
||||
!if "$(PRJ_STUBOBJS)" != ""
|
||||
default-install: default-install-stubs
|
||||
!endif
|
||||
|
||||
# Unlike the other default targets, these cannot be in rules.vc because
|
||||
# the executed command depends on existence of macro PRJ_HEADERS_PUBLIC
|
||||
# that the parent makefile will not define until after including rules-ext.vc
|
||||
!if "$(PRJ_HEADERS_PUBLIC)" != ""
|
||||
default-install: default-install-headers
|
||||
default-install-headers:
|
||||
@echo Installing headers to '$(INCLUDE_INSTALL_DIR)'
|
||||
@for %f in ($(PRJ_HEADERS_PUBLIC)) do @$(COPY) %f "$(INCLUDE_INSTALL_DIR)"
|
||||
!endif
|
||||
|
||||
!if "$(DISABLE_STANDARD_TARGETS)" == ""
|
||||
DISABLE_STANDARD_TARGETS = 0
|
||||
!endif
|
||||
|
||||
!if "$(DISABLE_TARGET_setup)" == ""
|
||||
DISABLE_TARGET_setup = 0
|
||||
!endif
|
||||
!if "$(DISABLE_TARGET_install)" == ""
|
||||
DISABLE_TARGET_install = 0
|
||||
!endif
|
||||
!if "$(DISABLE_TARGET_clean)" == ""
|
||||
DISABLE_TARGET_clean = 0
|
||||
!endif
|
||||
!if "$(DISABLE_TARGET_test)" == ""
|
||||
DISABLE_TARGET_test = 0
|
||||
!endif
|
||||
!if "$(DISABLE_TARGET_shell)" == ""
|
||||
DISABLE_TARGET_shell = 0
|
||||
!endif
|
||||
|
||||
!if !$(DISABLE_STANDARD_TARGETS)
|
||||
!if !$(DISABLE_TARGET_setup)
|
||||
setup: default-setup
|
||||
!endif
|
||||
!if !$(DISABLE_TARGET_install)
|
||||
install: default-install
|
||||
!endif
|
||||
!if !$(DISABLE_TARGET_clean)
|
||||
clean: default-clean
|
||||
realclean: hose
|
||||
hose: default-hose
|
||||
distclean: realclean default-distclean
|
||||
!endif
|
||||
!if !$(DISABLE_TARGET_test)
|
||||
test: default-test
|
||||
!endif
|
||||
!if !$(DISABLE_TARGET_shell)
|
||||
shell: default-shell
|
||||
!endif
|
||||
!endif # DISABLE_STANDARD_TARGETS
|
||||
+25
-12
@@ -9132,7 +9132,7 @@ int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
|
||||
const char *sA = Jim_GetString(aObjPtr, &Alen);
|
||||
const char *sB = Jim_GetString(bObjPtr, &Blen);
|
||||
|
||||
return Alen == Blen && memcmp(sA, sB, Alen) == 0;
|
||||
return Alen == Blen && *sA == *sB && memcmp(sA, sB, Alen) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10242,7 +10242,7 @@ static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void
|
||||
int len1, len2;
|
||||
const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
|
||||
const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
|
||||
return len1 == len2 && memcmp(str1, str2, len1) == 0;
|
||||
return len1 == len2 && *str1 == *str2 && memcmp(str1, str2, len1) == 0;
|
||||
}
|
||||
|
||||
static void JimCommandsHT_ValDestructor(void *interp, void *val)
|
||||
@@ -13864,6 +13864,13 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
|
||||
case JIM_EXPROP_NOT:
|
||||
wC = !bA;
|
||||
break;
|
||||
case JIM_EXPROP_UNARYPLUS:
|
||||
case JIM_EXPROP_UNARYMINUS:
|
||||
rc = JIM_ERR;
|
||||
Jim_SetResultFormatted(interp,
|
||||
"can't use non-numeric string as operand of \"%s\"",
|
||||
node->type == JIM_EXPROP_UNARYPLUS ? "+" : "-");
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@@ -19868,16 +19875,22 @@ wrongargs:
|
||||
}
|
||||
else if (errorCodeObj) {
|
||||
int len = Jim_ListLength(interp, argv[idx + 1]);
|
||||
int i;
|
||||
|
||||
ret = JIM_OK;
|
||||
if (len > Jim_ListLength(interp, errorCodeObj)) {
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
|
||||
Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
|
||||
if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
ret = -1;
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
ret = JIM_OK;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
|
||||
Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
|
||||
if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20253,7 +20266,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
|
||||
}
|
||||
|
||||
case OPT_SET:
|
||||
return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
|
||||
return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG | JIM_UNSHARED);
|
||||
|
||||
case OPT_EXISTS:{
|
||||
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
|
||||
@@ -20265,7 +20278,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
|
||||
}
|
||||
|
||||
case OPT_UNSET:
|
||||
if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
|
||||
if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_UNSHARED) != JIM_OK) {
|
||||
return JIM_ERR;
|
||||
}
|
||||
return JIM_OK;
|
||||
|
||||
+1233
-173
File diff suppressed because it is too large
Load Diff
+172
-114
@@ -24,6 +24,7 @@ if {"--help" ni $::argv} {
|
||||
}
|
||||
use system ; # Will output "Host System" and "Build System" lines
|
||||
if {"--help" ni $::argv} {
|
||||
proj-tweak-default-env-dirs
|
||||
msg-result "Source dir = $::autosetup(srcdir)"
|
||||
msg-result "Build dir = $::autosetup(builddir)"
|
||||
use cc cc-db cc-shared cc-lib pkg-config
|
||||
@@ -59,6 +60,18 @@ array set sqliteConfig [subst [proj-strip-hash-comments {
|
||||
# (dump-defines-txt) but also a JSON file named after this option's
|
||||
# value.
|
||||
dump-defines-json ""
|
||||
|
||||
#
|
||||
# The list of feature --flags which the --all flag implies. This
|
||||
# requires special handling in a few places.
|
||||
#
|
||||
all-flag-enables {fts4 fts5 rtree geopoly session}
|
||||
|
||||
#
|
||||
# Default value for the --all flag. Can hypothetically be modified
|
||||
# by non-canonical builds.
|
||||
#
|
||||
all-flag-default 0
|
||||
}]]
|
||||
|
||||
########################################################################
|
||||
@@ -83,10 +96,16 @@ array set sqliteConfig [subst [proj-strip-hash-comments {
|
||||
proc sqlite-configure {buildMode configScript} {
|
||||
proj-assert {$::sqliteConfig(build-mode) eq "unknown"} \
|
||||
"sqlite-configure must not be called more than once"
|
||||
set allBuildModes {canonical autoconf tcl-extension}
|
||||
set allBuildModes {canonical autoconf}
|
||||
if {$buildMode ni $allBuildModes} {
|
||||
user-error "Invalid build mode: $buildMode. Expecting one of: $allBuildModes"
|
||||
}
|
||||
if {$::sqliteConfig(all-flag-default)} {
|
||||
set allFlagHelp "Disable these extensions: $::sqliteConfig(all-flag-enables)"
|
||||
} else {
|
||||
set allFlagHelp "Enable these extensions: $::sqliteConfig(all-flag-enables)"
|
||||
}
|
||||
|
||||
set ::sqliteConfig(build-mode) $buildMode
|
||||
########################################################################
|
||||
# A gentle introduction to flags handling in autosetup
|
||||
@@ -193,11 +212,16 @@ proc sqlite-configure {buildMode configScript} {
|
||||
geopoly => {Enable the GEOPOLY extension}
|
||||
rtree => {Enable the RTREE extension}
|
||||
session => {Enable the SESSION extension}
|
||||
all => {Enable FTS4, FTS5, Geopoly, RTree, Sessions}
|
||||
all=$::sqliteConfig(all-flag-default) => {$allFlagHelp}
|
||||
largefile=1
|
||||
=> {This legacy flag has no effect on the library but may influence
|
||||
the generated sqlite_cfg.h by adding #define HAVE_LFS}
|
||||
}
|
||||
{canonical} {
|
||||
column-metadata => {Enable the column metadata APIs}
|
||||
# ^^^ Affects how sqlite3.c is generated, so is not available in
|
||||
# the autoconf build.
|
||||
}
|
||||
}
|
||||
|
||||
# Options for TCL support
|
||||
@@ -208,8 +232,6 @@ proc sqlite-configure {buildMode configScript} {
|
||||
This tree requires TCL for code generation but can use the in-tree
|
||||
copy of autosetup/jimsh0.c for that. The SQLite TCL extension and the
|
||||
test code require a canonical tclsh.}
|
||||
}
|
||||
{canonical tcl-extension} {
|
||||
with-tcl:DIR
|
||||
=> {Directory containing tclConfig.sh or a directory one level up from
|
||||
that, from which we can derive a directory containing tclConfig.sh.
|
||||
@@ -217,15 +239,16 @@ proc sqlite-configure {buildMode configScript} {
|
||||
the --prefix flag.}
|
||||
with-tclsh:PATH
|
||||
=> {Full pathname of tclsh to use. It is used for (A) trying to find
|
||||
tclConfig.sh and (B) all TCL-based code generation. Warning: if
|
||||
its containing dir has multiple tclsh versions, it may select the
|
||||
tclConfig.sh and (B) all TCL-based code generation. Use --with-tcl
|
||||
unless you have a specific need for this flag. Warning: if its
|
||||
containing dir has multiple tclsh versions, it may select the
|
||||
wrong tclConfig.sh!}
|
||||
}
|
||||
{canonical} {
|
||||
static-tclsqlite3=0
|
||||
=> {Statically-link tclsqlite3. This only works if TCL support is
|
||||
enabled and the libs tclsqlite3 needs are all available in
|
||||
static form.}
|
||||
enabled and all requisite libraries are available in
|
||||
static form. Note that glibc is unable to fully statically
|
||||
link certain libraries required by tclsqlite3, so this won't
|
||||
work on most Linux environments.}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +301,21 @@ proc sqlite-configure {buildMode configScript} {
|
||||
=> {Top-most dir of the wasi-sdk for a WASI build}
|
||||
}
|
||||
|
||||
{*} {
|
||||
# Note that --static-cli-shell has a completely different
|
||||
# meaning from --static-shell in the autoconf build!
|
||||
# --[disable-]static-shell is a legacy flag which we can't
|
||||
# remove without breaking downstream builds.
|
||||
static-cli-shell=0
|
||||
=> {Statically-link the sqlite3 CLI shell.
|
||||
This only works if the requisite libraries are all available in
|
||||
static form.}
|
||||
}
|
||||
|
||||
{canonical} {
|
||||
static-shells=0
|
||||
=> {Shorthand for --static-cli-shell --static-tclsqlite3}
|
||||
|
||||
with-emsdk:=auto
|
||||
=> {Top-most dir of the Emscripten SDK installation.
|
||||
Needed only by ext/wasm. Default=EMSDK env var.}
|
||||
@@ -293,12 +330,14 @@ proc sqlite-configure {buildMode configScript} {
|
||||
packaging {
|
||||
{autoconf} {
|
||||
# --disable-static-shell: https://sqlite.org/forum/forumpost/cc219ee704
|
||||
# Note that this has a different meaning from --static-cli-shell in the
|
||||
# canonical build!
|
||||
static-shell=1
|
||||
=> {Link the sqlite3 shell app against the DLL instead of embedding sqlite3.c}
|
||||
}
|
||||
{canonical autoconf} {
|
||||
# A potential TODO without a current use case:
|
||||
#rpath=1 => {Disable use of the rpath linker flag}
|
||||
rpath=1 => {Disable use of the rpath linker flag}
|
||||
|
||||
# soname: https://sqlite.org/src/forumpost/5a3b44f510df8ded
|
||||
soname:=legacy
|
||||
=> {SONAME for libsqlite3.so. "none", or not using this flag, sets no
|
||||
@@ -388,19 +427,15 @@ proc sqlite-configure {buildMode configScript} {
|
||||
}
|
||||
|
||||
# Filter allFlags to create the set of [options] legal for this build
|
||||
set opts {}
|
||||
foreach {group XY} [subst -nobackslashes -nocommands \
|
||||
[proj-strip-hash-comments $allFlags]] {
|
||||
foreach {group XY} [subst -nobackslashes -nocommands $allFlags] {
|
||||
foreach {X Y} $XY {
|
||||
if { $buildMode in $X || "*" in $X } {
|
||||
foreach y $Y {
|
||||
lappend opts $y
|
||||
}
|
||||
options-add $Y
|
||||
}
|
||||
}
|
||||
}
|
||||
#lappend opts "soname:=duplicateEntry => {x}"; #just testing
|
||||
if {[catch {options $opts} msg xopts]} {
|
||||
if {[catch {options {}} msg xopts]} {
|
||||
# Workaround for <https://github.com/msteveb/autosetup/issues/73>
|
||||
# where [options] behaves oddly on _some_ TCL builds when it's
|
||||
# called from deeper than the global scope.
|
||||
@@ -431,7 +466,7 @@ proc sqlite-configure-phase1 {buildMode} {
|
||||
}
|
||||
set ::sqliteConfig(msg-debug-enabled) [proj-val-truthy [get-env msg-debug 0]]
|
||||
proc-debug "msg-debug is enabled"
|
||||
sqlite-autoreconfig
|
||||
proj-setup-autoreconfig SQLITE_AUTORECONFIG
|
||||
proj-file-extensions
|
||||
if {".exe" eq [get-define TARGET_EXEEXT]} {
|
||||
define SQLITE_OS_UNIX 0
|
||||
@@ -452,6 +487,11 @@ proc sqlite-configure-phase1 {buildMode} {
|
||||
# may not) define HAVE_LFS.
|
||||
cc-check-lfs
|
||||
}
|
||||
set srcdir $::autosetup(srcdir)
|
||||
proj-dot-ins-append $srcdir/Makefile.in
|
||||
if {[file exists $srcdir/sqlite3.pc.in]} {
|
||||
proj-dot-ins-append $srcdir/sqlite3.pc.in
|
||||
}
|
||||
}; # sqlite-configure-phase1
|
||||
|
||||
########################################################################
|
||||
@@ -484,7 +524,6 @@ proc sqlite-configure-finalize {} {
|
||||
sqlite-handle-common-feature-flags
|
||||
sqlite-finalize-feature-flags
|
||||
sqlite-process-dot-in-files; # do not [define] anything after this
|
||||
sqlite-post-config-validation
|
||||
sqlite-dump-defines
|
||||
}
|
||||
|
||||
@@ -501,28 +540,7 @@ proc msg-debug {msg} {
|
||||
# the debug message. It is not legal to call this from the global
|
||||
# scope.
|
||||
proc proc-debug {msg} {
|
||||
msg-debug "\[[proj-current-proc-name 1]\]: $msg"
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Sets up the SQLITE_AUTORECONFIG define.
|
||||
proc sqlite-autoreconfig {} {
|
||||
# SQLITE_AUTORECONFIG contains make target rules for re-running the
|
||||
# configure script with the same arguments it was initially invoked
|
||||
# with. This can be used to automatically reconfigure.
|
||||
set squote {{arg} {
|
||||
# Wrap $arg in single-quotes if it looks like it might need that
|
||||
# to avoid mis-handling as a shell argument. We assume that $arg
|
||||
# will never contain any single-quote characters.
|
||||
if {[string match {*[ &;$*"]*} $arg]} { return '$arg' }
|
||||
return $arg
|
||||
}}
|
||||
define-append SQLITE_AUTORECONFIG cd [apply $squote $::autosetup(builddir)] \
|
||||
&& [apply $squote $::autosetup(srcdir)/configure]
|
||||
#{*}$::autosetup(argv) breaks with --flag='val with spaces', so...
|
||||
foreach arg $::autosetup(argv) {
|
||||
define-append SQLITE_AUTORECONFIG [apply $squote $arg]
|
||||
}
|
||||
msg-debug "\[[proj-scope 1]\]: $msg"
|
||||
}
|
||||
|
||||
define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags.
|
||||
@@ -565,8 +583,12 @@ proc sqlite-affirm-have-math {featureName} {
|
||||
if {![msg-quiet proj-check-function-in-lib log m]} {
|
||||
user-error "Missing math APIs for $featureName"
|
||||
}
|
||||
define LDFLAGS_MATH [get-define lib_log ""]
|
||||
set lfl [get-define lib_log ""]
|
||||
undefine lib_log
|
||||
if {"" ne $lfl} {
|
||||
user-notice "Forcing requirement of $lfl for $featureName"
|
||||
}
|
||||
define LDFLAGS_MATH $lfl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,17 +618,23 @@ proc sqlite-check-common-system-deps {} {
|
||||
|
||||
# Check for needed/wanted functions
|
||||
cc-check-functions gmtime_r isnan localtime_r localtime_s \
|
||||
malloc_usable_size strchrnul usleep utime pread pread64 pwrite pwrite64
|
||||
strchrnul usleep utime pread pread64 pwrite pwrite64
|
||||
|
||||
set ldrt ""
|
||||
# Collapse funcs from librt into LDFLAGS_RT.
|
||||
# Some systems (ex: SunOS) require -lrt in order to use nanosleep
|
||||
foreach func {fdatasync nanosleep} {
|
||||
if {[proj-check-function-in-lib $func rt]} {
|
||||
lappend ldrt [get-define lib_${func}]
|
||||
apply {{} {
|
||||
set ldrt ""
|
||||
# Collapse funcs from librt into LDFLAGS_RT.
|
||||
# Some systems (ex: SunOS) require -lrt in order to use nanosleep
|
||||
foreach func {fdatasync nanosleep} {
|
||||
if {[proj-check-function-in-lib $func rt]} {
|
||||
set ldrt [get-define lib_${func} ""]
|
||||
undefine lib_${func}
|
||||
if {"" ne $ldrt} {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
define LDFLAGS_RT [join [lsort -unique $ldrt] ""]
|
||||
define LDFLAGS_RT $ldrt
|
||||
}}
|
||||
|
||||
# Check for needed/wanted headers
|
||||
cc-check-includes \
|
||||
@@ -701,15 +729,36 @@ proc sqlite-setup-default-cflags {} {
|
||||
# Handle various SQLITE_ENABLE/OMIT_... feature flags.
|
||||
proc sqlite-handle-common-feature-flags {} {
|
||||
msg-result "Feature flags..."
|
||||
foreach {boolFlag featureFlag ifSetEvalThis} {
|
||||
all {} {
|
||||
# The 'all' option must be first in this list.
|
||||
proj-opt-set fts4
|
||||
proj-opt-set fts5
|
||||
proj-opt-set geopoly
|
||||
proj-opt-set rtree
|
||||
proj-opt-set session
|
||||
if {![opt-bool all]} {
|
||||
# Special handling for --disable-all
|
||||
foreach flag $::sqliteConfig(all-flag-enables) {
|
||||
if {![proj-opt-was-provided $flag]} {
|
||||
proj-opt-set $flag 0
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach {boolFlag featureFlag ifSetEvalThis} [proj-strip-hash-comments {
|
||||
all {} {
|
||||
# The 'all' option must be first in this list. This impl makes
|
||||
# an effort to only apply flags which the user did not already
|
||||
# apply, so that combinations like (--all --disable-geopoly)
|
||||
# will indeed disable geopoly. There are corner cases where
|
||||
# flags which depend on each other will behave in non-intuitive
|
||||
# ways:
|
||||
#
|
||||
# --all --disable-rtree
|
||||
#
|
||||
# Will NOT disable geopoly, though geopoly depends on rtree.
|
||||
# The --geopoly flag, though, will automatically re-enable
|
||||
# --rtree, so --disable-rtree won't actually disable anything in
|
||||
# that case.
|
||||
foreach k $::sqliteConfig(all-flag-enables) {
|
||||
if {![proj-opt-was-provided $k]} {
|
||||
proj-opt-set $k 1
|
||||
}
|
||||
}
|
||||
}
|
||||
fts3 -DSQLITE_ENABLE_FTS3 {sqlite-affirm-have-math fts3}
|
||||
fts4 -DSQLITE_ENABLE_FTS4 {sqlite-affirm-have-math fts4}
|
||||
fts5 -DSQLITE_ENABLE_FTS5 {sqlite-affirm-have-math fts5}
|
||||
geopoly -DSQLITE_ENABLE_GEOPOLY {proj-opt-set rtree}
|
||||
@@ -725,8 +774,9 @@ proc sqlite-handle-common-feature-flags {} {
|
||||
sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3
|
||||
}
|
||||
}
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
} {
|
||||
scanstatus -DSQLITE_ENABLE_STMT_SCANSTATUS {}
|
||||
column-metadata -DSQLITE_ENABLE_COLUMN_METADATA {}
|
||||
}] {
|
||||
if {$boolFlag ni $::autosetup(options)} {
|
||||
# Skip flags which are in the canonical build but not
|
||||
# the autoconf bundle.
|
||||
@@ -946,8 +996,10 @@ proc sqlite-handle-emsdk {} {
|
||||
# Maybe there's a copy in the path?
|
||||
proj-bin-define wasm-opt BIN_WASM_OPT
|
||||
}
|
||||
proj-make-from-dot-in $emccSh $extWasmConfig
|
||||
catch {exec chmod u+x $emccSh}
|
||||
proj-dot-ins-append $emccSh.in $emccSh {
|
||||
catch {exec chmod u+x $dotInsOut}
|
||||
}
|
||||
proj-dot-ins-append $extWasmConfig.in $extWasmConfig
|
||||
} else {
|
||||
define EMCC_WRAPPER ""
|
||||
file delete -force -- $emccSh $extWasmConfig
|
||||
@@ -1449,6 +1501,28 @@ proc sqlite-handle-mac-cversion {} {
|
||||
return $rc
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# If this is a Mac platform, check for support for
|
||||
# -Wl,-install_name,... and, if it's available, define
|
||||
# LDFLAGS_MAC_INSTALL_NAME to a variant of that string which is
|
||||
# intended to expand at make-time, else set LDFLAGS_MAC_INSTALL_NAME
|
||||
# to an empty string.
|
||||
#
|
||||
# https://sqlite.org/forum/forumpost/5651662b8875ec0a
|
||||
proc sqlite-handle-mac-install-name {} {
|
||||
define LDFLAGS_MAC_INSTALL_NAME ""; # {-Wl,-install_name,"$(install-dir.lib)/$(libsqlite3.DLL)"}
|
||||
set rc 0
|
||||
if {[proj-looks-like-mac]} {
|
||||
cc-with {-link 1} {
|
||||
if {[cc-check-flags "-Wl,-install_name,/usr/local/lib/libsqlite3.dylib"]} {
|
||||
define LDFLAGS_MAC_INSTALL_NAME {-Wl,-install_name,"$(install-dir.lib)/$(libsqlite3.DLL)"}
|
||||
set rc 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return $rc
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Handles the --dll-basename configure flag. [define]'s
|
||||
# SQLITE_DLL_BASENAME to the DLL's preferred base name (minus
|
||||
@@ -1621,6 +1695,7 @@ proc sqlite-handle-env-quirks {} {
|
||||
sqlite-handle-dll-basename
|
||||
sqlite-handle-out-implib
|
||||
sqlite-handle-mac-cversion
|
||||
sqlite-handle-mac-install-name
|
||||
if {[llength [info proc sqlite-custom-handle-flags]] > 0} {
|
||||
# sqlite-custom-handle-flags is assumed to be imported via a
|
||||
# client-specific import: autosetup/sqlite-custom.tcl.
|
||||
@@ -1643,15 +1718,7 @@ proc sqlite-process-dot-in-files {} {
|
||||
# (e.g. [proj-check-rpath]) may do so before we "mangle" them here.
|
||||
proj-remap-autoconf-dir-vars
|
||||
|
||||
set srcdir $::autosetup(srcdir)/
|
||||
foreach f {Makefile sqlite3.pc} {
|
||||
if {[file exists $srcdir/$f.in]} {
|
||||
# ^^^ we do this only so that this block can be made to work for
|
||||
# multiple builds. e.g. the tea build (under construction) does
|
||||
# not hae sqlite3.pc.in.
|
||||
proj-make-from-dot-in -touch $f
|
||||
}
|
||||
}
|
||||
proj-dot-ins-process -validate
|
||||
make-config-header sqlite_cfg.h \
|
||||
-bare {SIZEOF_* HAVE_DECL_*} \
|
||||
-none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG
|
||||
@@ -1661,32 +1728,6 @@ proc sqlite-process-dot-in-files {} {
|
||||
proj-touch sqlite_cfg.h ; # help avoid frequent unnecessary @SQLITE_AUTORECONFIG@
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Perform some high-level validation on the generated files...
|
||||
#
|
||||
# 1) Ensure that no unresolved @VAR@ placeholders are in files which
|
||||
# use those.
|
||||
#
|
||||
# 2) TBD
|
||||
proc sqlite-post-config-validation {} {
|
||||
# Check #1: ensure that files which get filtered for @VAR@ do not
|
||||
# contain any unresolved @VAR@ refs. That may indicate an
|
||||
# unexported/unused var or a typo.
|
||||
set srcdir $::autosetup(srcdir)
|
||||
foreach f [list Makefile sqlite3.pc \
|
||||
$srcdir/tool/emcc.sh \
|
||||
$srcdir/ext/wasm/config.make] {
|
||||
if {![file exists $f]} continue
|
||||
set lnno 1
|
||||
foreach line [proj-file-content-list $f] {
|
||||
if {[regexp {(@[A-Za-z0-9_]+@)} $line match]} {
|
||||
error "Unresolved reference to $match at line $lnno of $f"
|
||||
}
|
||||
incr lnno
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Handle --with-wasi-sdk[=DIR]
|
||||
#
|
||||
@@ -1931,13 +1972,14 @@ proc sqlite-check-tcl {} {
|
||||
# TCLLIBDIR from here, which will cause the canonical makefile to
|
||||
# use this one rather than to re-calculate it at make-time.
|
||||
set tcllibdir [get-env TCLLIBDIR ""]
|
||||
set sq3Ver [get-define PACKAGE_VERSION]
|
||||
if {"" eq $tcllibdir} {
|
||||
# Attempt to extract TCLLIBDIR from TCL's $auto_path
|
||||
if {"" ne $with_tclsh &&
|
||||
[catch {exec echo "puts stdout \$auto_path" | "$with_tclsh"} result] == 0} {
|
||||
foreach i $result {
|
||||
if {[file isdir $i]} {
|
||||
set tcllibdir $i/sqlite3
|
||||
set tcllibdir $i/sqlite${sq3Ver}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2069,20 +2111,35 @@ proc sqlite-determine-codegen-tcl {} {
|
||||
}; # sqlite-determine-codegen-tcl
|
||||
|
||||
########################################################################
|
||||
# Runs sqlite-check-tcl and, if $alsoCheckCodeGen is true,
|
||||
# sqlite-determine-codegen-tcl (intended only for the canonical
|
||||
# build).
|
||||
proc sqlite-handle-tcl {{alsoCheckCodeGen 1}} {
|
||||
# Runs sqlite-check-tcl and, if this is the canonical build,
|
||||
# sqlite-determine-codegen-tcl.
|
||||
proc sqlite-handle-tcl {} {
|
||||
sqlite-check-tcl
|
||||
if {$alsoCheckCodeGen} {
|
||||
msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]"
|
||||
if {"canonical" ne $::sqliteConfig(build-mode)} return
|
||||
msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]"
|
||||
|
||||
# Determine the base name of the Tcl extension's DLL
|
||||
#
|
||||
if {[get-define HAVE_TCL]} {
|
||||
if {[string match *-cygwin [get-define host]]} {
|
||||
set libname cyg
|
||||
} else {
|
||||
set libname lib
|
||||
}
|
||||
if {[get-define TCL_MAJOR_VERSION] > 8} {
|
||||
append libname tcl9
|
||||
}
|
||||
append libname sqlite
|
||||
} else {
|
||||
set libname ""
|
||||
}
|
||||
define TCL_EXT_DLL_BASENAME $libname
|
||||
# The extension is added in the makefile
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# Handle the --enable/disable-rpath flag.
|
||||
proc sqlite-handle-rpath {} {
|
||||
proj-check-rpath
|
||||
# autosetup/cc-shared.tcl sets the rpath flag definition in
|
||||
# [get-define SH_LINKRPATH], but it does so on a per-platform basis
|
||||
# rather than as a compiler check. Though we should do a proper
|
||||
@@ -2091,12 +2148,13 @@ proc sqlite-handle-rpath {} {
|
||||
# for which sqlite-env-is-unix-on-windows returns a non-empty
|
||||
# string.
|
||||
|
||||
# if {[proj-opt-truthy rpath]} {
|
||||
# proj-check-rpath
|
||||
# } else {
|
||||
# msg-result "Disabling use of rpath."
|
||||
# define LDFLAGS_RPATH ""
|
||||
# }
|
||||
# https://sqlite.org/forum/forumpost/13cac3b56516f849
|
||||
if {[proj-opt-truthy rpath]} {
|
||||
proj-check-rpath
|
||||
} else {
|
||||
msg-result "Disabling use of rpath."
|
||||
define LDFLAGS_RPATH ""
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
The *.tcl files in this directory are part of the SQLite's "autoconf"
|
||||
bundle which are specific to the TEA(-ish) build. During the tarball
|
||||
generation process, they are copied into <TOP>/autoconf/autosetup/teaish
|
||||
(which itself is created as part of that process).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,214 @@
|
||||
########################################################################
|
||||
# 2025 April 7
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# * May you do good and not evil.
|
||||
# * May you find forgiveness for yourself and forgive others.
|
||||
# * May you share freely, never taking more than you give.
|
||||
#
|
||||
########################################################################
|
||||
# ----- @module feature-tests.tcl -----
|
||||
# @section TEA-ish collection of feature tests.
|
||||
#
|
||||
# Functions in this file with a prefix of teaish__ are
|
||||
# private/internal APIs. Those with a prefix of teaish- are
|
||||
# public APIs.
|
||||
|
||||
|
||||
# @teaish-check-libz
|
||||
#
|
||||
# Checks for zlib.h and the function deflate in libz. If found,
|
||||
# prepends -lz to the extension's ldflags and returns 1, else returns
|
||||
# 0. It also defines LDFLAGS_LIBZ to the libs flag.
|
||||
#
|
||||
proc teaish-check-libz {} {
|
||||
teaish-check-cached "Checking for libz" {
|
||||
set rc 0
|
||||
if {[msg-quiet cc-check-includes zlib.h] && [msg-quiet proj-check-function-in-lib deflate z]} {
|
||||
teaish-ldflags-prepend [define LDFLAGS_LIBZ [get-define lib_deflate]]
|
||||
undefine lib_deflate
|
||||
incr rc
|
||||
}
|
||||
expr $rc
|
||||
}
|
||||
}
|
||||
|
||||
# @teaish-check-librt ?funclist?
|
||||
#
|
||||
# Checks whether -lrt is needed for any of the given functions. If
|
||||
# so, appends -lrt via [teaish-ldflags-prepend] and returns 1, else
|
||||
# returns 0. It also defines LDFLAGS_LIBRT to the libs flag or an
|
||||
# empty string.
|
||||
#
|
||||
# Some systems (ex: SunOS) require -lrt in order to use nanosleep.
|
||||
#
|
||||
proc teaish-check-librt {{funclist {fdatasync nanosleep}}} {
|
||||
teaish-check-cached -nostatus "Checking whether ($funclist) need librt" {
|
||||
define LDFLAGS_LIBRT ""
|
||||
foreach func $funclist {
|
||||
if {[msg-quiet proj-check-function-in-lib $func rt]} {
|
||||
set ldrt [get-define lib_${func}]
|
||||
undefine lib_${func}
|
||||
if {"" ne $ldrt} {
|
||||
teaish-ldflags-prepend -r [define LDFLAGS_LIBRT $ldrt]
|
||||
msg-result $ldrt
|
||||
return 1
|
||||
} else {
|
||||
msg-result "no lib needed"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
msg-result "not found"
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
# @teaish-check-stdint
|
||||
#
|
||||
# A thin proxy for [cc-with] which checks for <stdint.h> and the
|
||||
# various fixed-size int types it declares. It defines HAVE_STDINT_T
|
||||
# to 0 or 1 and (if it's 1) defines HAVE_XYZ_T for each XYZ int type
|
||||
# to 0 or 1, depending on whether its available.
|
||||
proc teaish-check-stdint {} {
|
||||
teaish-check-cached "Checking for stdint.h" {
|
||||
msg-quiet cc-with {-includes stdint.h} \
|
||||
{cc-check-types int8_t int16_t int32_t int64_t intptr_t \
|
||||
uint8_t uint16_t uint32_t uint64_t uintptr_t}
|
||||
}
|
||||
}
|
||||
|
||||
# @teaish-is-mingw
|
||||
#
|
||||
# Returns 1 if building for mingw, else 0.
|
||||
proc teaish-is-mingw {} {
|
||||
return [expr {
|
||||
[string match *mingw* [get-define host]] &&
|
||||
![file exists /dev/null]
|
||||
}]
|
||||
}
|
||||
|
||||
# @teaish-check-libdl
|
||||
#
|
||||
# Checks for whether dlopen() can be found and whether it requires
|
||||
# -ldl for linking. If found, returns 1, defines LDFLAGS_DLOPEN to the
|
||||
# linker flags (if any), and passes those flags to
|
||||
# teaish-ldflags-prepend. It unconditionally defines HAVE_DLOPEN to 0
|
||||
# or 1 (the its return result value).
|
||||
proc teaish-check-dlopen {} {
|
||||
teaish-check-cached -nostatus "Checking for dlopen()" {
|
||||
set rc 0
|
||||
set lfl ""
|
||||
if {[cc-with {-includes dlfcn.h} {
|
||||
cctest -link 1 -declare "extern char* dlerror(void);" -code "dlerror();"}]} {
|
||||
msg-result "-ldl not needed"
|
||||
incr rc
|
||||
} elseif {[cc-check-includes dlfcn.h]} {
|
||||
incr rc
|
||||
if {[cc-check-function-in-lib dlopen dl]} {
|
||||
set lfl [get-define lib_dlopen]
|
||||
undefine lib_dlopen
|
||||
msg-result " dlopen() needs $lfl"
|
||||
} else {
|
||||
msg-result " - dlopen() not found in libdl. Assuming dlopen() is built-in."
|
||||
}
|
||||
} else {
|
||||
msg-result "not found"
|
||||
}
|
||||
teaish-ldflags-prepend [define LDFLAGS_DLOPEN $lfl]
|
||||
define HAVE_DLOPEN $rc
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# @teaish-check-libmath
|
||||
#
|
||||
# Handles the --enable-math flag. Returns 1 if found, else 0.
|
||||
# If found, it prepends -lm (if needed) to the linker flags.
|
||||
proc teaish-check-libmath {} {
|
||||
teaish-check-cached "Checking for libc math library" {
|
||||
set lfl ""
|
||||
set rc 0
|
||||
if {[msg-quiet proj-check-function-in-lib ceil m]} {
|
||||
incr rc
|
||||
set lfl [get-define lib_ceil]
|
||||
undefine lib_ceil
|
||||
teaish-ldflags-prepend $lfl
|
||||
msg-checking "$lfl "
|
||||
}
|
||||
define LDFLAGS_LIBMATH $lfl
|
||||
expr $rc
|
||||
}
|
||||
}
|
||||
|
||||
# @teaish-import-features ?-flags? feature-names...
|
||||
#
|
||||
# For each $name in feature-names... it invokes:
|
||||
#
|
||||
# use teaish/feature/$name
|
||||
#
|
||||
# to load TEAISH_AUTOSETUP_DIR/feature/$name.tcl
|
||||
#
|
||||
# By default, if a proc named teaish-check-${name}-options is defined
|
||||
# after sourcing a file, it is called and its result is passed to
|
||||
# proj-append-options. This can be suppressed with the -no-options
|
||||
# flag.
|
||||
#
|
||||
# Flags:
|
||||
#
|
||||
# -no-options: disables the automatic running of
|
||||
# teaish-check-NAME-options,
|
||||
#
|
||||
# -run: if the function teaish-check-NAME exists after importing
|
||||
# then it is called. This flag must not be used when calling this
|
||||
# function from teaish-options. This trumps both -pre and -post.
|
||||
#
|
||||
# -pre: if the function teaish-check-NAME exists after importing
|
||||
# then it is passed to [teaish-checks-queue -pre].
|
||||
#
|
||||
# -post: works like -pre but instead uses[teaish-checks-queue -post].
|
||||
proc teaish-import-features {args} {
|
||||
set pk ""
|
||||
set doOpt 1
|
||||
proj-parse-simple-flags args flags {
|
||||
-no-options 0 {set doOpt 0}
|
||||
-run 0 {expr 1}
|
||||
-pre 0 {set pk -pre}
|
||||
-post 0 {set pk -post}
|
||||
}
|
||||
#
|
||||
# TODO: never import the same module more than once. The "use"
|
||||
# command is smart enough to not do that but we would need to
|
||||
# remember whether or not any teaish-check-${arg}* procs have been
|
||||
# called before, and skip them.
|
||||
#
|
||||
if {$flags(-run) && "" ne $pk} {
|
||||
proj-error "Cannot use both -run and $pk" \
|
||||
" (called from [proj-scope 1])"
|
||||
}
|
||||
|
||||
foreach arg $args {
|
||||
uplevel "use teaish/feature/$arg"
|
||||
if {$doOpt} {
|
||||
set n "teaish-check-${arg}-options"
|
||||
if {[llength [info proc $n]] > 0} {
|
||||
if {"" ne [set x [$n]]} {
|
||||
options-add $x
|
||||
}
|
||||
}
|
||||
}
|
||||
if {$flags(-run)} {
|
||||
set n "teaish-check-${arg}"
|
||||
if {[llength [info proc $n]] > 0} {
|
||||
uplevel 1 $n
|
||||
}
|
||||
} elseif {"" ne $pk} {
|
||||
set n "teaish-check-${arg}"
|
||||
if {[llength [info proc $n]] > 0} {
|
||||
teaish-checks-queue {*}$pk $n
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
########################################################################
|
||||
# 2025 April 5
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
########################################################################
|
||||
#
|
||||
# Helper routines for running tests on teaish extensions
|
||||
#
|
||||
########################################################################
|
||||
# ----- @module teaish/tester.tcl -----
|
||||
#
|
||||
# @section TEA-ish Testing APIs.
|
||||
#
|
||||
# Though these are part of the autosup dir hierarchy, they are not
|
||||
# intended to be run from autosetup code. Rather, they're for use
|
||||
# with/via teaish.tester.tcl and target canonical Tcl only, not JimTcl
|
||||
# (which the autosetup pieces do target).
|
||||
|
||||
#
|
||||
# @test-current-scope ?lvl?
|
||||
#
|
||||
# Returns the name of the _calling_ proc from ($lvl + 1) levels up the
|
||||
# call stack (where the caller's level will be 1 up from _this_
|
||||
# call). If $lvl would resolve to global scope "global scope" is
|
||||
# returned and if it would be negative then a string indicating such
|
||||
# is returned (as opposed to throwing an error).
|
||||
#
|
||||
proc test-current-scope {{lvl 0}} {
|
||||
#uplevel [expr {$lvl + 1}] {lindex [info level 0] 0}
|
||||
set ilvl [info level]
|
||||
set offset [expr {$ilvl - $lvl - 1}]
|
||||
if { $offset < 0} {
|
||||
return "invalid scope ($offset)"
|
||||
} elseif { $offset == 0} {
|
||||
return "global scope"
|
||||
} else {
|
||||
return [lindex [info level $offset] 0]
|
||||
}
|
||||
}
|
||||
|
||||
# @test-msg
|
||||
#
|
||||
# Emits all arugments to stdout.
|
||||
#
|
||||
proc test-msg {args} {
|
||||
puts "$args"
|
||||
}
|
||||
|
||||
# @test-warn
|
||||
#
|
||||
# Emits all arugments to stderr.
|
||||
#
|
||||
proc test-warn {args} {
|
||||
puts stderr "WARNING: $args"
|
||||
}
|
||||
|
||||
#
|
||||
# @test-error msg
|
||||
#
|
||||
# Triggers a test-failed error with a string describing the calling
|
||||
# scope and the provided message.
|
||||
#
|
||||
proc test-fail {args} {
|
||||
#puts stderr "ERROR: \[[test-current-scope 1]]: $msg"
|
||||
#exit 1
|
||||
error "FAIL: \[[test-current-scope 1]]: $args"
|
||||
}
|
||||
|
||||
array set ::test__Counters {}
|
||||
array set ::test__Config {
|
||||
verbose-assert 0 verbose-affirm 0
|
||||
}
|
||||
|
||||
# Internal impl for affirm and assert.
|
||||
#
|
||||
# $args = ?-v? script {msg-on-fail ""}
|
||||
proc test__affert {failMode args} {
|
||||
if {$failMode} {
|
||||
set what assert
|
||||
} else {
|
||||
set what affirm
|
||||
}
|
||||
set verbose $::test__Config(verbose-$what)
|
||||
if {"-v" eq [lindex $args 0]} {
|
||||
lassign $args - script msg
|
||||
if {1 == [llength $args]} {
|
||||
# If -v is the only arg, toggle default verbose mode
|
||||
set ::test__Config(verbose-$what) [expr {!$::test__Config(verbose-$what)}]
|
||||
return
|
||||
}
|
||||
incr verbose
|
||||
} else {
|
||||
lassign $args script msg
|
||||
}
|
||||
incr ::test__Counters($what)
|
||||
if {![uplevel 1 expr [list $script]]} {
|
||||
if {"" eq $msg} {
|
||||
set msg $script
|
||||
}
|
||||
set txt [join [list $what # $::test__Counters($what) "failed:" $msg]]
|
||||
if {$failMode} {
|
||||
puts stderr $txt
|
||||
exit 1
|
||||
} else {
|
||||
error $txt
|
||||
}
|
||||
} elseif {$verbose} {
|
||||
puts stderr [join [list $what # $::test__Counters($what) "passed:" $script]]
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# @affirm ?-v? script ?msg?
|
||||
#
|
||||
# Works like a conventional assert method does, but reports failures
|
||||
# using [error] instead of [exit]. If -v is used, it reports passing
|
||||
# assertions to stderr. $script is evaluated in the caller's scope as
|
||||
# an argument to [expr].
|
||||
#
|
||||
proc affirm {args} {
|
||||
tailcall test__affert 0 {*}$args
|
||||
}
|
||||
|
||||
#
|
||||
# @assert ?-v? script ?msg?
|
||||
#
|
||||
# Works like [affirm] but exits on error.
|
||||
#
|
||||
proc assert {args} {
|
||||
tailcall test__affert 1 {*}$args
|
||||
}
|
||||
|
||||
#
|
||||
# @assert-matches ?-e? pattern ?-e? rhs ?msg?
|
||||
#
|
||||
# Equivalent to assert {[string match $pattern $rhs]} except that
|
||||
# if either of those are prefixed with an -e flag, they are eval'd
|
||||
# and their results are used.
|
||||
#
|
||||
proc assert-matches {args} {
|
||||
set evalLhs 0
|
||||
set evalRhs 0
|
||||
if {"-e" eq [lindex $args 0]} {
|
||||
incr evalLhs
|
||||
set args [lassign $args -]
|
||||
}
|
||||
set args [lassign $args pattern]
|
||||
if {"-e" eq [lindex $args 0]} {
|
||||
incr evalRhs
|
||||
set args [lassign $args -]
|
||||
}
|
||||
set args [lassign $args rhs msg]
|
||||
|
||||
if {$evalLhs} {
|
||||
set pattern [uplevel 1 $pattern]
|
||||
}
|
||||
if {$evalRhs} {
|
||||
set rhs [uplevel 1 $rhs]
|
||||
}
|
||||
#puts "***pattern=$pattern\n***rhs=$rhs"
|
||||
tailcall test__affert 1 \
|
||||
[join [list \[ string match [list $pattern] [list $rhs] \]]] $msg
|
||||
# why does this not work? [list \[ string match [list $pattern] [list $rhs] \]] $msg
|
||||
# "\[string match [list $pattern] [list $rhs]\]"
|
||||
}
|
||||
|
||||
#
|
||||
# @test-assert testId script ?msg?
|
||||
#
|
||||
# Works like [assert] but emits $testId to stdout first.
|
||||
#
|
||||
proc test-assert {testId script {msg ""}} {
|
||||
puts "test $testId"
|
||||
tailcall test__affert 1 $script $msg
|
||||
}
|
||||
|
||||
#
|
||||
# @test-expect testId script result
|
||||
#
|
||||
# Runs $script in the calling scope and compares its result to
|
||||
# $result, minus any leading or trailing whitespace. If they differ,
|
||||
# it triggers an [assert].
|
||||
#
|
||||
proc test-expect {testId script result} {
|
||||
puts "test $testId"
|
||||
set x [string trim [uplevel 1 $script]]
|
||||
set result [string trim $result]
|
||||
tailcall test__affert 0 [list "{$x}" eq "{$result}"] \
|
||||
"\nEXPECTED: <<$result>>\nGOT: <<$x>>"
|
||||
}
|
||||
|
||||
#
|
||||
# @test-catch cmd ?...args?
|
||||
#
|
||||
# Runs [cmd ...args], repressing any exception except to possibly log
|
||||
# the failure. Returns 1 if it caught anything, 0 if it didn't.
|
||||
#
|
||||
proc test-catch {cmd args} {
|
||||
if {[catch {
|
||||
uplevel 1 $cmd {*}$args
|
||||
} rc xopts]} {
|
||||
puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc"
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# @test-catch-matching pattern (script|cmd args...)
|
||||
#
|
||||
# Works like test-catch, but it expects its argument(s) to to throw an
|
||||
# error matching the given string (checked with [string match]). If
|
||||
# they do not throw, or the error does not match $pattern, this
|
||||
# function throws, else it returns 1.
|
||||
#
|
||||
# If there is no second argument, the $cmd is assumed to be a script,
|
||||
# and will be eval'd in the caller's scope.
|
||||
#
|
||||
# TODO: add -glob and -regex flags to control matching flavor.
|
||||
#
|
||||
proc test-catch-matching {pattern cmd args} {
|
||||
if {[catch {
|
||||
#puts "**** catch-matching cmd=$cmd args=$args"
|
||||
if {0 == [llength $args]} {
|
||||
uplevel 1 $cmd {*}$args
|
||||
} else {
|
||||
$cmd {*}$args
|
||||
}
|
||||
} rc xopts]} {
|
||||
if {[string match $pattern $rc]} {
|
||||
return 1
|
||||
} else {
|
||||
error "[test-current-scope] exception does not match {$pattern}: {$rc}"
|
||||
}
|
||||
}
|
||||
error "[test-current-scope] expecting to see an error matching {$pattern}"
|
||||
}
|
||||
|
||||
if {![array exists ::teaish__BuildFlags]} {
|
||||
array set ::teaish__BuildFlags {}
|
||||
}
|
||||
|
||||
#
|
||||
# @teaish-build-flag3 flag tgtVar ?dflt?
|
||||
#
|
||||
# If the current build has the configure-time flag named $flag set
|
||||
# then tgtVar is assigned its value and 1 is returned, else tgtVal is
|
||||
# assigned $dflt and 0 is returned.
|
||||
#
|
||||
# Caveat #1: only valid when called in the context of teaish's default
|
||||
# "make test" recipe, e.g. from teaish.test.tcl. It is not valid from
|
||||
# a teaish.tcl configure script because (A) the state it relies on
|
||||
# doesn't fully exist at that point and (B) that level of the API has
|
||||
# more direct access to the build state. This function requires that
|
||||
# an external script have populated its internal state, which is
|
||||
# normally handled via teaish.tester.tcl.in.
|
||||
#
|
||||
# Caveat #2: defines in the style of HAVE_FEATURENAME with a value of
|
||||
# 0 are, by long-standing configure script conventions, treated as
|
||||
# _undefined_ here.
|
||||
#
|
||||
proc teaish-build-flag3 {flag tgtVar {dflt ""}} {
|
||||
upvar $tgtVar tgt
|
||||
if {[info exists ::teaish__BuildFlags($flag)]} {
|
||||
set tgt $::teaish__BuildFlags($flag)
|
||||
return 1;
|
||||
} elseif {0==[array size ::teaish__BuildFlags]} {
|
||||
test-warn \
|
||||
"\[[test-current-scope]] was called from " \
|
||||
"[test-current-scope 1] without the build flags imported."
|
||||
}
|
||||
set tgt $dflt
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# @teaish-build-flag flag ?dflt?
|
||||
#
|
||||
# Convenience form of teaish-build-flag3 which returns the
|
||||
# configure-time-defined value of $flag or "" if it's not defined (or
|
||||
# if it's an empty string).
|
||||
#
|
||||
proc teaish-build-flag {flag {dflt ""}} {
|
||||
set tgt ""
|
||||
teaish-build-flag3 $flag tgt $dflt
|
||||
return $tgt
|
||||
}
|
||||
+1
-1
@@ -846,7 +846,7 @@ or more tokens. The alternative meaning is tried if the original token
|
||||
would have generated a syntax error.</p>
|
||||
|
||||
<p>The <tt>%fallback</tt> directive was added to support robust parsing of SQL
|
||||
syntax in <a href='https://www.sqlite.org/'>SQLite</a>.
|
||||
syntax in <a href='https://sqlite.org/'>SQLite</a>.
|
||||
The SQL language contains a large assortment of keywords, each of which
|
||||
appears as a different token to the language parser. SQL contains so
|
||||
many keywords that it can be difficult for programmers to keep up with
|
||||
|
||||
+107
-51
@@ -35,84 +35,69 @@ perspective on how to compile SQLite on unix-like systems.
|
||||
### 2.1 Setup
|
||||
|
||||
<ol type="1">
|
||||
<li value="1">
|
||||
[Fossil](https://fossil-scm.org/) installed.
|
||||
<li value="1">
|
||||
[Fossil][] installed.
|
||||
<li> Check out source code and set environment variables:
|
||||
<ol type="a">
|
||||
<li> **TCLSOURCE** →
|
||||
The top-level directory of a Fossil check-out of the TCL source tree.
|
||||
The top-level directory of a [Fossil][] check-out of the
|
||||
[TCL source tree][tcl-fossil].
|
||||
<li> **SQLITESOURCE** →
|
||||
A Fossil check-out of the SQLite source tree.
|
||||
<li> **TCLBUILD** →
|
||||
<li> **TCLHOME** →
|
||||
A directory that does not exist at the start of the test and which
|
||||
will be deleted at the end of the test, and that will contain the
|
||||
test builds of the TCL libraries and the SQLite TCL Extensions.
|
||||
It is the top-most installation directory, i.e. the one provided
|
||||
to Tcl's `./configure --prefix=/path/to/tcl`.
|
||||
<li> **TCLVERSION** →
|
||||
The `X.Y`-form version of Tcl being used: 8.6, 9.0, 9.1...
|
||||
</ol>
|
||||
</ol>
|
||||
|
||||
### 2.2 Testing TCL 8.6 on unix
|
||||
### 2.2 Testing TCL 8.x and 9.x on unix
|
||||
|
||||
From a checked-out copy of [the core Tcl tree][tcl-fossil]
|
||||
|
||||
<ol type="1">
|
||||
<li value="3"> `mkdir -p $TCLBUILD/tcl86`
|
||||
<li> `cd $TCLSOURCE/unix`
|
||||
<li> `fossil up core-8-6-16` <br>
|
||||
↑ Or some other version of Tcl8.6.
|
||||
<li value="3">`TCLVERSION=8.6` <br>
|
||||
↑ A version of your choice. This process has been tested with
|
||||
values of 8.6, 9.0, and 9.1 (as of 2025-04-16). The out-of-life
|
||||
version 8.5 fails some of `make devtest` for undetermined reasons.
|
||||
<li>`TCLHOME=$HOME/tcl/$TCLVERSION`
|
||||
<li>`TCLSOURCE=/path/to/tcl/checkout`
|
||||
<li>`SQLITESOURCE=/path/to/sqlite/checkout`
|
||||
<li>`rm -fr $TCLHOME` <br>
|
||||
↑ Ensure that no stale Tcl installation is laying around.
|
||||
<li> `cd $TCLSOURCE`
|
||||
<li> `fossil up core-8-6-branch` <br>
|
||||
↑ The branch corresponding to `$TCLVERSION`, e.g.
|
||||
`core-9-0-branch` or `trunk`.
|
||||
<li> `fossil clean -x`
|
||||
<li> `./configure --prefix=$TCLBUILD/tcl86 --disable-shared` <br>
|
||||
↑ The --disable-shared is to avoid the need to set LD_LIBRARY_PATH
|
||||
<li> `cd unix`
|
||||
<li> `./configure --prefix=$TCLHOME --disable-shared` <br>
|
||||
↑ The `--disable-shared` is to avoid the need to set `LD_LIBRARY_PATH`
|
||||
when using this Tcl build.
|
||||
<li> `make install`
|
||||
<li> `cd $SQLITESOURCE`
|
||||
<li> `fossil clean -x`
|
||||
<li> `./configure --with-tclsh=$TCLBUILD/tcl86/bin/tclsh8.6 --all`
|
||||
<li> `./configure --with-tcl=$TCLHOME --all`
|
||||
<li> `make tclextension-install` <br>
|
||||
↑ Verify extension installed at $TCLBUILD/tcl86/lib/tcl8.6/sqlite3.*
|
||||
↑ Verify extension installed at
|
||||
`$TCLHOME/lib/tcl${TCLVERSION}/sqlite<SQLITE_VERSION>`.
|
||||
<li> `make tclextension-list` <br>
|
||||
↑ Verify TCL extension correctly installed.
|
||||
<li> `make tclextension-verify` <br>
|
||||
↑ Verify that the correct version is installed.
|
||||
<li> `$TCLBUILD/tcl86/bin/tclsh8.6 test/testrunner.tcl release --explain` <br>
|
||||
<li> `$TCLHOME/bin/tclsh[89].[0-9] test/testrunner.tcl release --explain` <br>
|
||||
↑ Verify thousands of lines of output with no errors. Or
|
||||
consider running "devtest" without --explain instead of "release".
|
||||
</ol>
|
||||
|
||||
### 2.3 Testing TCL 9.0 on unix
|
||||
|
||||
<ol>
|
||||
<li value="16"> `mkdir -p $TCLBUILD/tcl90`
|
||||
<li> `fossil up core-9-0-0` <br>
|
||||
↑ Or some other version of Tcl9
|
||||
<li> `fossil clean -x`
|
||||
<li> `./configure --prefix=$TCLBUILD/tcl90 --disable-shared` <br>
|
||||
↑ The --disable-shared is to avoid the need to set LD_LIBRARY_PATH
|
||||
when using this Tcl build.
|
||||
<li> `make install`
|
||||
<li> `cp -r ../library $TCLBUILD/tcl90/lib/tcl9.0` <br>
|
||||
↑ The Tcl library is not installed by "make install" for Tcl9.0 unless
|
||||
you also include the --disable-zipfs to ./configure. But if you do that
|
||||
then the generated tclsh9.0 is no longer stand-alone. On the other hand,
|
||||
if you don't install the Tcl library, other programs like testfixture
|
||||
won't be able to find the Tcl library and hence won't work. This
|
||||
extra installation step resolves the dilemma.
|
||||
This step is not required when building Tcl8.6, which lacks support for
|
||||
zipfs and hence always installs its Tcl library.
|
||||
<li> `cd $SQLITESOURCE`
|
||||
<li> `fossil clean -x`
|
||||
<li> `./configure --with-tclsh=$TCLBUILD/tcl90/bin/tclsh9.0 --all`
|
||||
<li> `make tclextension-install` <br>
|
||||
↑ Verify extension installed at $TCLBUILD/tcl90/lib/sqlite3.*
|
||||
<li> `make tclextension-list` <br>
|
||||
↑ Verify TCL extension correctly installed.
|
||||
<li> `make tclextension-verify`
|
||||
<li> `$TCLBUILD/tcl90/bin/tclsh9.0 test/testrunner.tcl release --explain` <br>
|
||||
↑ Verify thousands of lines of output with no errors. Or
|
||||
consider running "devtest" without --explain instead of "release".
|
||||
</ol>
|
||||
|
||||
### 2.4 Cleanup
|
||||
### 2.3 Cleanup
|
||||
|
||||
<ol type="1">
|
||||
<li value="29"> `rm -rf $TCLBUILD`
|
||||
<li value="29"> `rm -rf $TCLHOME`
|
||||
</ol>
|
||||
|
||||
<a id="windows"></a>
|
||||
@@ -123,9 +108,11 @@ perspective on how to compile SQLite on Windows.
|
||||
|
||||
### 3.1 Setup for Windows
|
||||
|
||||
(These docs are not as up-to-date as the Unix docs, above.)
|
||||
|
||||
<ol type="1">
|
||||
<li value="1">
|
||||
[Fossil](https://fossil-scm.org/) installed.
|
||||
<li value="1">
|
||||
[Fossil][] installed.
|
||||
<li>
|
||||
Unix-like command-line tools installed. Example:
|
||||
[unxutils](https://unxutils.sourceforge.net/)
|
||||
@@ -206,3 +193,72 @@ perspective on how to compile SQLite on Windows.
|
||||
<ol type="1">
|
||||
<li value="35"> `rm -rf %TCLBUILD%`
|
||||
</ol>
|
||||
|
||||
## 4.0 Testing the TEA(ish) Build (unix only)
|
||||
|
||||
This part requires following the setup instructions for Unix systems,
|
||||
at the top of this document.
|
||||
|
||||
The former TEA, now TEA(ish), build of this extension uses the same
|
||||
code as the builds described above but is provided in a form more
|
||||
convenient for downstream Tcl users.
|
||||
|
||||
It lives in `autoconf/tea` and, as part of the `autoconf` bundle,
|
||||
_cannot be tested directly from the canonical tree_. Instead it has to
|
||||
be packaged.
|
||||
|
||||
### 4.1 Teaish Setup
|
||||
|
||||
Follow the same Tcl- and environment-related related setup described
|
||||
in the first section of this document, up to and including the
|
||||
installation of Tcl (unless, of course, it was already installed using
|
||||
those same instructions).
|
||||
|
||||
### 4.2 Teaish Testing
|
||||
|
||||
<ol>
|
||||
<li>`cd $SQLITESOURCE`
|
||||
<li>Run either `make snapshot-tarball` or `make amalgamation-tarball`
|
||||
↑
|
||||
Those steps will leave behind a temp dir called `mkpkg_tmp_dir`,
|
||||
under which the extension is most readily reached. It can optionally
|
||||
be extracted from the generated tarball, but that tarball was
|
||||
generated from this dir, and reusing this dir is a time saver
|
||||
during development.
|
||||
<li> `cd mkpkg_tmp/tea`
|
||||
<li> `./configure --with-tcl=$TCLHOME`
|
||||
<li> `make test install` <br>
|
||||
↑ Should run to completion without any errors.
|
||||
<li> `make uninstall` <br>
|
||||
↑ Will uninstall the extension. This _can_ be run
|
||||
in the same invocation as the `install` target, but only
|
||||
if the `-j#` make flag is _not_ used. If it is, the
|
||||
install/uninstall steps will race and make a mess of things.
|
||||
Parallel builds do not help in this build, anyway, as there's
|
||||
only a single C file to compile.
|
||||
</ol>
|
||||
|
||||
When actively developing and testing the teaish build, which requires
|
||||
going through the tarball generation, there's a caveat about the
|
||||
`mkpkg_tmp_dir` dir: it will be deleted every time a tarball is
|
||||
built, the shell console which is parked in that
|
||||
directory for testing needs to add `cd $PWD &&` to the start of the
|
||||
build commands, like:
|
||||
|
||||
>
|
||||
```
|
||||
[user@host:.../mkpkg_tmp_dir/tea]$ \
|
||||
cd $PWD && ./configure CFLAGS=-O0 --with-tcl=$TCLHOME \
|
||||
&& make test install uninstall
|
||||
```
|
||||
|
||||
### 4.3 Teaish Cleanup
|
||||
|
||||
|
||||
<ol type="1">
|
||||
<li> `rm -rf $TCLHOME`
|
||||
<li> `cd $SQLITESOURCE; rm -fr mkpkg_tmp_dir; fossil clean -x`
|
||||
</ol>
|
||||
|
||||
[Fossil]: https://fossil-scm.org/home
|
||||
[tcl-fossil]: https://core.tcl-lang.org/tcl
|
||||
|
||||
+68
-20
@@ -15,6 +15,7 @@
|
||||
<li> 3.1. <a href=#commands_to_run_tests>Commands to Run SQLite Tests</a>
|
||||
<li> 3.2. <a href=#zipvfs_tests>Running ZipVFS Tests</a>
|
||||
<li> 3.3. <a href=#source_code_test_failures>Investigating Source Code Test Failures</a>
|
||||
<li> 3.4. <a href=#fuzzdb>External Fuzzcheck Databases</a>
|
||||
</ul>
|
||||
<li> 4. <a href=#testrunner_options>Extra testrunner.tcl Options</a>
|
||||
<li> 5. <a href=#cpu_cores>Controlling CPU Core Utilization</a>
|
||||
@@ -23,47 +24,62 @@
|
||||
<a name=overview></a>
|
||||
# 1. Overview
|
||||
|
||||
testrunner.tcl is a Tcl script used to run multiple SQLite tests using
|
||||
multiple jobs. It supports the following types of tests:
|
||||
The testrunner.tcl program is a Tcl script used to run multiple SQLite
|
||||
tests in parallel, thus reducing testing time on multi-core machines.
|
||||
It supports the following types of tests:
|
||||
|
||||
* Tcl test scripts.
|
||||
|
||||
* Fuzzcheck tests, including using an external fuzzcheck database.
|
||||
|
||||
* Tests run with `make` commands. Examples:
|
||||
- `make mdevtest`
|
||||
- `make devtest`
|
||||
- `make releasetest`
|
||||
- `make sdevtest`
|
||||
- `make testrunner`
|
||||
|
||||
testrunner.tcl pipes the output of all tests and builds run into log file
|
||||
**testrunner.log**, created in the current working directory. Search this
|
||||
file to find details of errors. Suggested search commands:
|
||||
The testrunner.tcl program stores output of all tests and builds run in
|
||||
log file **testrunner.log**, created in the current working directory.
|
||||
Search this file to find details of errors. Suggested search commands:
|
||||
|
||||
* `grep "^!" testrunner.log`
|
||||
* `grep failed testrunner.log`
|
||||
|
||||
testrunner.tcl also populates SQLite database **testrunner.db**. This database
|
||||
contains details of all tests run, running and to be run. A useful query
|
||||
might be:
|
||||
The testrunner.tcl program also populates SQLite database **testrunner.db**.
|
||||
This database contains details of all tests run, running and to be run.
|
||||
A useful query might be:
|
||||
|
||||
```
|
||||
SELECT * FROM script WHERE state='failed'
|
||||
```
|
||||
|
||||
You can get a summary of errors in a prior run by invoking commands like
|
||||
these:
|
||||
|
||||
```
|
||||
tclsh $(TESTDIR)/testrunner.tcl errors
|
||||
tclsh $(TESTDIR)/testrunner.tcl errors -v
|
||||
```
|
||||
|
||||
Running the command:
|
||||
|
||||
```
|
||||
./testfixture $(TESTDIR)/testrunner.tcl status
|
||||
tclsh $(TESTDIR)/testrunner.tcl status
|
||||
```
|
||||
|
||||
in the directory containing the testrunner.db database runs various queries
|
||||
to produce a succinct report on the state of a running testrunner.tcl script.
|
||||
Running:
|
||||
A good way to keep and eye on test progress is to run either of the two
|
||||
following commands:
|
||||
|
||||
```
|
||||
watch ./testfixture $(TESTDIR)/testrunner.tcl status
|
||||
watch tclsh $(TESTDIR)/testrunner.tcl status
|
||||
tclsh $(TESTDIR)/testrunner.tcl status -d 2
|
||||
```
|
||||
|
||||
in another terminal is a good way to keep an eye on a long running test.
|
||||
Both of the commands above accomplish about the same thing, but the second
|
||||
one has the advantage of not requiring "watch" to be installed on your
|
||||
system.
|
||||
|
||||
Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
|
||||
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
|
||||
@@ -239,9 +255,7 @@ of the specific tests run.
|
||||
|
||||
As with <a href=#source code tests>source code tests</a>, one or more patterns
|
||||
may be appended to any of the above commands (mdevtest, sdevtest or release).
|
||||
In that case only Tcl tests (no fuzz or other tests) that match the specified
|
||||
pattern are run. For example, to run the just the Tcl rtree tests in all
|
||||
builds and configurations supported by "release":
|
||||
Pattern matching is used for both Tcl tests and fuzz tests.
|
||||
|
||||
```
|
||||
tclsh $TESTDIR/testrunner.tcl release rtree%
|
||||
@@ -292,6 +306,34 @@ target to build. This may be used either to run a `make` command test directly,
|
||||
or else to build a testfixture (or testfixture.exe) binary with which to
|
||||
run a Tcl test script, as <a href=#binary_test_failures>described above</a>.
|
||||
|
||||
<a name=fuzzdb></a>
|
||||
## 3.4 External Fuzzcheck Databases
|
||||
|
||||
Testrunner.tcl will also run fuzzcheck against an external (out of tree)
|
||||
database, for example fuzzcheck databases generated by dbsqlfuzz. To do
|
||||
this, simply add the "`--fuzzdb` *FILENAME*" command-line option or set
|
||||
the FUZZDB environment variable to the name of the external
|
||||
database. For large external databases, testrunner.tcl will automatically use
|
||||
the "`--slice`" command-line option of fuzzcheck to divide the work up into
|
||||
multiple jobs, to increase parallelism.
|
||||
|
||||
Thus, for example, to run a full releasetest including an external
|
||||
dbsqlfuzz database, run a command like one of these:
|
||||
|
||||
```
|
||||
tclsh test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
|
||||
FUZZDB=../fuzz/20250415.db make releasetest
|
||||
nmake /f Makefile.msc FUZZDB=../fuzz/20250415.db releasetest
|
||||
```
|
||||
|
||||
The patternlist option to testrunner.tcl will match against fuzzcheck
|
||||
databases. So if you want to run *only* tests involving the external
|
||||
database, you can use a command something like this:
|
||||
|
||||
```
|
||||
tclsh test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
|
||||
```
|
||||
|
||||
<a name=testrunner_options></a>
|
||||
# 4. Extra testrunner.tcl Options
|
||||
|
||||
@@ -315,16 +357,22 @@ would normally execute into the testrunner.log file. Example:
|
||||
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
|
||||
```
|
||||
|
||||
The **--explain** option is similar to --dryrun in that it prevents testrunner.tcl
|
||||
from building any binaries or running any tests. The difference is that --explain
|
||||
prints on standard output a human-readable summary of all the builds and tests that
|
||||
would have been run.
|
||||
The **--explain** option is similar to --dryrun in that it prevents
|
||||
testrunner.tcl from building any binaries or running any tests. The
|
||||
difference is that --explain prints on standard output a human-readable
|
||||
summary of all the builds and tests that would have been run.
|
||||
|
||||
```
|
||||
# Show what builds and tests would have been run
|
||||
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
|
||||
```
|
||||
|
||||
The **--status** option uses VT100 escape sequences to display the test
|
||||
status full-screen. This is similar to running
|
||||
"`watch test/testrunner status`" in a separate window, just more convenient.
|
||||
Unfortunately, this option does not work correctly on Windows, due to the
|
||||
sketchy implementation of VT100 escapes on the Windows console.
|
||||
|
||||
<a name=cpu_cores></a>
|
||||
# 5. Controlling CPU Core Utilization
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
## Loadable Extensions
|
||||
|
||||
Various [loadable extensions](https://www.sqlite.org/loadext.html) for
|
||||
Various [loadable extensions](https://sqlite.org/loadext.html) for
|
||||
SQLite are found in subfolders.
|
||||
|
||||
Most subfolders are dedicated to a single loadable extension (for
|
||||
|
||||
+1
-1
@@ -213,7 +213,7 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
|
||||
** Macros needed to provide flexible arrays in a portable way
|
||||
*/
|
||||
#ifndef offsetof
|
||||
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define FLEXARRAY
|
||||
|
||||
@@ -1027,16 +1027,16 @@ static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
|
||||
break;
|
||||
|
||||
case FTS3_MATCHINFO_LHITS:
|
||||
nVal = pInfo->nCol * pInfo->nPhrase;
|
||||
nVal = (size_t)pInfo->nCol * pInfo->nPhrase;
|
||||
break;
|
||||
|
||||
case FTS3_MATCHINFO_LHITS_BM:
|
||||
nVal = pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
|
||||
nVal = (size_t)pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( cArg==FTS3_MATCHINFO_HITS );
|
||||
nVal = pInfo->nCol * pInfo->nPhrase * 3;
|
||||
nVal = (size_t)pInfo->nCol * pInfo->nPhrase * 3;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@ SQLITE_EXTENSION_INIT1
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
|
||||
@@ -79,7 +80,7 @@ typedef sqlite3_uint64 u64;
|
||||
** Macros needed to provide flexible arrays in a portable way
|
||||
*/
|
||||
#ifndef offsetof
|
||||
# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define FLEXARRAY
|
||||
|
||||
+125
-61
@@ -554,6 +554,36 @@ struct Fts5SegIter {
|
||||
u8 bDel; /* True if the delete flag is set */
|
||||
};
|
||||
|
||||
static int fts5IndexCorruptRowid(Fts5Index *pIdx, i64 iRowid){
|
||||
pIdx->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
|
||||
"fts5: corruption found reading blob %lld from table \"%s\"",
|
||||
iRowid, pIdx->pConfig->zName
|
||||
);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
#define FTS5_CORRUPT_ROWID(pIdx, iRowid) fts5IndexCorruptRowid(pIdx, iRowid)
|
||||
|
||||
static int fts5IndexCorruptIter(Fts5Index *pIdx, Fts5SegIter *pIter){
|
||||
pIdx->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
|
||||
"fts5: corruption on page %d, segment %d, table \"%s\"",
|
||||
pIter->iLeafPgno, pIter->pSeg->iSegid, pIdx->pConfig->zName
|
||||
);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
#define FTS5_CORRUPT_ITER(pIdx, pIter) fts5IndexCorruptIter(pIdx, pIter)
|
||||
|
||||
static int fts5IndexCorruptIdx(Fts5Index *pIdx){
|
||||
pIdx->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(pIdx->pConfig,
|
||||
"fts5: corruption in table \"%s\"", pIdx->pConfig->zName
|
||||
);
|
||||
return SQLITE_CORRUPT_VTAB;
|
||||
}
|
||||
#define FTS5_CORRUPT_IDX(pIdx) fts5IndexCorruptIdx(pIdx)
|
||||
|
||||
|
||||
/*
|
||||
** Array of tombstone pages. Reference counted.
|
||||
*/
|
||||
@@ -843,7 +873,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
|
||||
** All the reasons those functions might return SQLITE_ERROR - missing
|
||||
** table, missing row, non-blob/text in block column - indicate
|
||||
** backing store corruption. */
|
||||
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
|
||||
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
u8 *aOut = 0; /* Read blob data into this buffer */
|
||||
@@ -893,7 +923,7 @@ static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
|
||||
Fts5Data *pRet = fts5DataRead(p, iRowid);
|
||||
if( pRet ){
|
||||
if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
fts5DataRelease(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
@@ -1252,8 +1282,14 @@ static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
|
||||
/* TODO: Do we need this if the leaf-index is appended? Probably... */
|
||||
memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
|
||||
p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
|
||||
if( p->rc==SQLITE_OK && (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
|
||||
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( (pConfig->pgsz==0 || pConfig->iCookie!=iCookie) ){
|
||||
p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
|
||||
}
|
||||
}else if( p->rc==SQLITE_CORRUPT_VTAB ){
|
||||
sqlite3Fts5ConfigErrmsg(p->pConfig,
|
||||
"fts5: corrupt structure record for table \"%s\"", p->pConfig->zName
|
||||
);
|
||||
}
|
||||
fts5DataRelease(pData);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
@@ -1876,7 +1912,7 @@ static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
|
||||
while( iOff>=pIter->pLeaf->szLeaf ){
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
if( pIter->pLeaf==0 ){
|
||||
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
|
||||
if( p->rc==SQLITE_OK ) FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
iOff = 4;
|
||||
@@ -1908,7 +1944,7 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){
|
||||
|
||||
iOff += fts5GetVarint32(&a[iOff], nNew);
|
||||
if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
pIter->term.n = nKeep;
|
||||
@@ -1950,9 +1986,9 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
|
||||
** leave an error in the Fts5Index object.
|
||||
*/
|
||||
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
|
||||
const int nTomb = pIter->pSeg->nPgTombstone;
|
||||
const i64 nTomb = (i64)pIter->pSeg->nPgTombstone;
|
||||
if( nTomb>0 ){
|
||||
int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
|
||||
i64 nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
|
||||
Fts5TombstoneArray *pNew;
|
||||
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
|
||||
if( pNew ){
|
||||
@@ -2103,7 +2139,7 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
|
||||
iRowidOff = fts5LeafFirstRowidOff(pNew);
|
||||
if( iRowidOff ){
|
||||
if( iRowidOff>=pNew->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
}else{
|
||||
pIter->pLeaf = pNew;
|
||||
pIter->iLeafOffset = iRowidOff;
|
||||
@@ -2337,7 +2373,7 @@ static void fts5SegIterNext(
|
||||
}
|
||||
assert_nc( iOff<pLeaf->szLeaf );
|
||||
if( iOff>pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2445,18 +2481,20 @@ static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
|
||||
fts5DataRelease(pIter->pLeaf);
|
||||
pIter->pLeaf = pLast;
|
||||
pIter->iLeafPgno = pgnoLast;
|
||||
iOff = fts5LeafFirstRowidOff(pLast);
|
||||
if( iOff>pLast->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
return;
|
||||
}
|
||||
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
iOff = fts5LeafFirstRowidOff(pLast);
|
||||
if( iOff>pLast->szLeaf ){
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
|
||||
if( fts5LeafIsTermless(pLast) ){
|
||||
pIter->iEndofDoclist = pLast->nn+1;
|
||||
}else{
|
||||
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
|
||||
if( fts5LeafIsTermless(pLast) ){
|
||||
pIter->iEndofDoclist = pLast->nn+1;
|
||||
}else{
|
||||
pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2526,7 +2564,7 @@ static void fts5LeafSeek(
|
||||
iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
|
||||
iOff = iTermOff;
|
||||
if( iOff>n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2569,7 +2607,7 @@ static void fts5LeafSeek(
|
||||
iOff = iTermOff;
|
||||
|
||||
if( iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2591,7 +2629,7 @@ static void fts5LeafSeek(
|
||||
iPgidx = (u32)pIter->pLeaf->szLeaf;
|
||||
iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff);
|
||||
if( iOff<4 || (i64)iOff>=pIter->pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}else{
|
||||
nKeep = 0;
|
||||
@@ -2606,7 +2644,7 @@ static void fts5LeafSeek(
|
||||
|
||||
search_success:
|
||||
if( (i64)iOff+nNew>n || nNew<1 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ITER(p, pIter);
|
||||
return;
|
||||
}
|
||||
pIter->iLeafOffset = iOff + nNew;
|
||||
@@ -3071,7 +3109,7 @@ static void fts5SegIterGotoPage(
|
||||
assert( iLeafPgno>pIter->iLeafPgno );
|
||||
|
||||
if( iLeafPgno>pIter->pSeg->pgnoLast ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
fts5DataRelease(pIter->pNextLeaf);
|
||||
pIter->pNextLeaf = 0;
|
||||
@@ -3086,7 +3124,7 @@ static void fts5SegIterGotoPage(
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
int n = pIter->pLeaf->szLeaf;
|
||||
if( iOff<4 || iOff>=n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
|
||||
pIter->iLeafOffset = iOff;
|
||||
@@ -3565,7 +3603,7 @@ static void fts5ChunkIterate(
|
||||
if( nRem<=0 ){
|
||||
break;
|
||||
}else if( pSeg->pSeg==0 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
return;
|
||||
}else{
|
||||
pgno++;
|
||||
@@ -4668,7 +4706,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
|
||||
** a single page has been assigned to more than one segment. In
|
||||
** this case a prior iteration of this loop may have corrupted the
|
||||
** segment currently being trimmed. */
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iLeafRowid);
|
||||
}else{
|
||||
fts5BufferZero(&buf);
|
||||
fts5BufferGrow(&p->rc, &buf, pData->nn);
|
||||
@@ -5135,7 +5173,7 @@ static void fts5SecureDeleteOverflow(
|
||||
}else if( bDetailNone ){
|
||||
break;
|
||||
}else if( iNext>=pLeaf->szLeaf || pLeaf->nn<pLeaf->szLeaf || iNext<4 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
break;
|
||||
}else{
|
||||
int nShift = iNext - 4;
|
||||
@@ -5155,7 +5193,7 @@ static void fts5SecureDeleteOverflow(
|
||||
|
||||
i1 += fts5GetVarint32(&aPg[i1], iFirst);
|
||||
if( iFirst<iNext ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
break;
|
||||
}
|
||||
aIdx = sqlite3Fts5MallocZero(&p->rc, (pLeaf->nn-pLeaf->szLeaf)+2);
|
||||
@@ -5378,14 +5416,14 @@ static void fts5DoSecureDelete(
|
||||
nSuffix = (nPrefix2 + nSuffix2) - nPrefix;
|
||||
|
||||
if( (iKeyOff+nSuffix)>iPgIdx || (iNextOff+nSuffix2)>iPgIdx ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else{
|
||||
if( iKey!=1 ){
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nPrefix);
|
||||
}
|
||||
iOff += sqlite3Fts5PutVarint(&aPg[iOff], nSuffix);
|
||||
if( nPrefix2>pSeg->term.n ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
}else if( nPrefix2>nPrefix ){
|
||||
memcpy(&aPg[iOff], &pSeg->term.p[nPrefix], nPrefix2-nPrefix);
|
||||
iOff += (nPrefix2-nPrefix);
|
||||
@@ -5809,7 +5847,7 @@ static Fts5Structure *fts5IndexOptimizeStruct(
|
||||
}
|
||||
|
||||
nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
|
||||
assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
|
||||
assert( nByte==(i64)SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
|
||||
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
|
||||
|
||||
if( pNew ){
|
||||
@@ -6178,7 +6216,7 @@ static void fts5MergePrefixLists(
|
||||
}
|
||||
|
||||
if( pHead==0 || pHead->pNext==0 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -6215,7 +6253,7 @@ static void fts5MergePrefixLists(
|
||||
assert_nc( tmp.n+nTail<=nTmp );
|
||||
assert( tmp.n+nTail<=nTmp+nMerge*10 );
|
||||
if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
|
||||
if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
|
||||
if( p->rc==SQLITE_OK ) FTS5_CORRUPT_IDX(p);
|
||||
break;
|
||||
}
|
||||
fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
|
||||
@@ -6784,10 +6822,13 @@ int sqlite3Fts5IndexRollback(Fts5Index *p){
|
||||
*/
|
||||
int sqlite3Fts5IndexReinit(Fts5Index *p){
|
||||
Fts5Structure *pTmp;
|
||||
u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
|
||||
union {
|
||||
Fts5Structure sFts;
|
||||
u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
|
||||
} uFts;
|
||||
fts5StructureInvalidate(p);
|
||||
fts5IndexDiscardData(p);
|
||||
pTmp = (Fts5Structure*)tmpSpace;
|
||||
pTmp = &uFts.sFts;
|
||||
memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
|
||||
if( p->pConfig->bContentlessDelete ){
|
||||
pTmp->nOriginCntr = 1;
|
||||
@@ -8248,19 +8289,27 @@ static int fts5TestUtf8(const char *z, int n){
|
||||
/*
|
||||
** This function is also purely an internal test. It does not contribute to
|
||||
** FTS functionality, or even the integrity-check, in any way.
|
||||
**
|
||||
** This function sets output variable (*pbFail) to true if the test fails. Or
|
||||
** leaves it unchanged if the test succeeds.
|
||||
*/
|
||||
static void fts5TestTerm(
|
||||
Fts5Index *p,
|
||||
Fts5Buffer *pPrev, /* Previous term */
|
||||
const char *z, int n, /* Possibly new term to test */
|
||||
u64 expected,
|
||||
u64 *pCksum
|
||||
u64 *pCksum,
|
||||
int *pbFail
|
||||
){
|
||||
int rc = p->rc;
|
||||
if( pPrev->n==0 ){
|
||||
fts5BufferSet(&rc, pPrev, n, (const u8*)z);
|
||||
}else
|
||||
if( rc==SQLITE_OK && (pPrev->n!=n || memcmp(pPrev->p, z, n)) ){
|
||||
if( *pbFail==0
|
||||
&& rc==SQLITE_OK
|
||||
&& (pPrev->n!=n || memcmp(pPrev->p, z, n))
|
||||
&& (p->pHash==0 || p->pHash->nEntry==0)
|
||||
){
|
||||
u64 cksum3 = *pCksum;
|
||||
const char *zTerm = (const char*)&pPrev->p[1]; /* term sans prefix-byte */
|
||||
int nTerm = pPrev->n-1; /* Size of zTerm in bytes */
|
||||
@@ -8310,7 +8359,7 @@ static void fts5TestTerm(
|
||||
fts5BufferSet(&rc, pPrev, n, (const u8*)z);
|
||||
|
||||
if( rc==SQLITE_OK && cksum3!=expected ){
|
||||
rc = FTS5_CORRUPT;
|
||||
*pbFail = 1;
|
||||
}
|
||||
*pCksum = cksum3;
|
||||
}
|
||||
@@ -8319,7 +8368,7 @@ static void fts5TestTerm(
|
||||
|
||||
#else
|
||||
# define fts5TestDlidxReverse(x,y,z)
|
||||
# define fts5TestTerm(u,v,w,x,y,z)
|
||||
# define fts5TestTerm(t,u,v,w,x,y,z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -8344,14 +8393,17 @@ static void fts5IndexIntegrityCheckEmpty(
|
||||
for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
|
||||
Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
|
||||
if( pLeaf ){
|
||||
if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
|
||||
if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
|
||||
if( !fts5LeafIsTermless(pLeaf)
|
||||
|| (i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf))
|
||||
){
|
||||
FTS5_CORRUPT_ROWID(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
|
||||
}
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
}
|
||||
}
|
||||
|
||||
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
static void fts5IntegrityCheckPgidx(Fts5Index *p, i64 iRowid, Fts5Data *pLeaf){
|
||||
i64 iTermOff = 0;
|
||||
int ii;
|
||||
|
||||
@@ -8369,12 +8421,12 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
iOff = iTermOff;
|
||||
|
||||
if( iOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}else if( iTermOff==nIncr ){
|
||||
int nByte;
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
|
||||
if( (iOff+nByte)>pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}else{
|
||||
fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
|
||||
}
|
||||
@@ -8383,7 +8435,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep);
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
|
||||
if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}else{
|
||||
buf1.n = nKeep;
|
||||
fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
|
||||
@@ -8391,7 +8443,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
res = fts5BufferCompare(&buf1, &buf2);
|
||||
if( res<=0 ) p->rc = FTS5_CORRUPT;
|
||||
if( res<=0 ) FTS5_CORRUPT_ROWID(p, iRowid);
|
||||
}
|
||||
}
|
||||
fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p);
|
||||
@@ -8452,7 +8504,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
** entry even if all the terms are removed from it by secure-delete
|
||||
** operations. */
|
||||
}else{
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRow);
|
||||
}
|
||||
|
||||
}else{
|
||||
@@ -8464,15 +8516,15 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
iOff = fts5LeafFirstTermOff(pLeaf);
|
||||
iRowidOff = fts5LeafFirstRowidOff(pLeaf);
|
||||
if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iRow);
|
||||
}else{
|
||||
iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
|
||||
res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
|
||||
if( res==0 ) res = nTerm - nIdxTerm;
|
||||
if( res<0 ) p->rc = FTS5_CORRUPT;
|
||||
if( res<0 ) FTS5_CORRUPT_ROWID(p, iRow);
|
||||
}
|
||||
|
||||
fts5IntegrityCheckPgidx(p, pLeaf);
|
||||
fts5IntegrityCheckPgidx(p, iRow, pLeaf);
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
if( p->rc ) break;
|
||||
@@ -8502,7 +8554,7 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
|
||||
pLeaf = fts5DataRead(p, iKey);
|
||||
if( pLeaf ){
|
||||
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) p->rc = FTS5_CORRUPT;
|
||||
if( fts5LeafFirstRowidOff(pLeaf)!=0 ) FTS5_CORRUPT_ROWID(p, iKey);
|
||||
fts5DataRelease(pLeaf);
|
||||
}
|
||||
}
|
||||
@@ -8517,12 +8569,12 @@ static void fts5IndexIntegrityCheckSegment(
|
||||
int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
|
||||
ASSERT_SZLEAF_OK(pLeaf);
|
||||
if( iRowidOff>=pLeaf->szLeaf ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iKey);
|
||||
}else if( bSecureDelete==0 || iRowidOff>0 ){
|
||||
i64 iDlRowid = fts5DlidxIterRowid(pDlidx);
|
||||
fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
|
||||
if( iRowid<iDlRowid || (bSecureDelete==0 && iRowid!=iDlRowid) ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
FTS5_CORRUPT_ROWID(p, iKey);
|
||||
}
|
||||
}
|
||||
fts5DataRelease(pLeaf);
|
||||
@@ -8574,6 +8626,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
|
||||
/* Used by extra internal tests only run if NDEBUG is not defined */
|
||||
u64 cksum3 = 0; /* Checksum based on contents of indexes */
|
||||
Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
|
||||
int bTestFail = 0;
|
||||
#endif
|
||||
const int flags = FTS5INDEX_QUERY_NOOUTPUT;
|
||||
|
||||
@@ -8616,7 +8669,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
|
||||
char *z = (char*)fts5MultiIterTerm(pIter, &n);
|
||||
|
||||
/* If this is a new term, query for it. Update cksum3 with the results. */
|
||||
fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
|
||||
fts5TestTerm(p, &term, z, n, cksum2, &cksum3, &bTestFail);
|
||||
if( p->rc ) break;
|
||||
|
||||
if( eDetail==FTS5_DETAIL_NONE ){
|
||||
@@ -8634,15 +8687,26 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum, int bUseCksum){
|
||||
}
|
||||
}
|
||||
}
|
||||
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
|
||||
fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3, &bTestFail);
|
||||
|
||||
fts5MultiIterFree(pIter);
|
||||
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
|
||||
|
||||
fts5StructureRelease(pStruct);
|
||||
if( p->rc==SQLITE_OK && bUseCksum && cksum!=cksum2 ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
sqlite3Fts5ConfigErrmsg(p->pConfig,
|
||||
"fts5: checksum mismatch for table \"%s\"", p->pConfig->zName
|
||||
);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* In SQLITE_DEBUG builds, expensive extra checks were run as part of
|
||||
** the integrity-check above. If no other errors were detected, but one
|
||||
** of these tests failed, set the result to SQLITE_CORRUPT_VTAB here. */
|
||||
if( p->rc==SQLITE_OK && bTestFail ){
|
||||
p->rc = FTS5_CORRUPT;
|
||||
}
|
||||
fts5BufferFree(&term);
|
||||
#endif
|
||||
|
||||
fts5StructureRelease(pStruct);
|
||||
fts5BufferFree(&poslist);
|
||||
return fts5IndexReturn(p);
|
||||
}
|
||||
|
||||
@@ -3701,8 +3701,9 @@ static int fts5IntegrityMethod(
|
||||
" FTS5 table %s.%s: %s",
|
||||
zSchema, zTabname, sqlite3_errstr(rc));
|
||||
}
|
||||
}else if( (rc&0xff)==SQLITE_CORRUPT ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
|
||||
pTab->p.pConfig->pzErrmsg = 0;
|
||||
|
||||
|
||||
+18
-5
@@ -539,6 +539,7 @@ static int fts5StorageDeleteFromIndex(
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1]==0 ){
|
||||
sqlite3_value *pVal = 0;
|
||||
sqlite3_value *pFree = 0;
|
||||
const char *pText = 0;
|
||||
int nText = 0;
|
||||
const char *pLoc = 0;
|
||||
@@ -555,11 +556,22 @@ static int fts5StorageDeleteFromIndex(
|
||||
if( pConfig->bLocale && sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
|
||||
rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
|
||||
}else{
|
||||
pText = (const char*)sqlite3_value_text(pVal);
|
||||
nText = sqlite3_value_bytes(pVal);
|
||||
if( pConfig->bLocale && pSeek ){
|
||||
pLoc = (const char*)sqlite3_column_text(pSeek, iCol + pConfig->nCol);
|
||||
nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol);
|
||||
if( sqlite3_value_type(pVal)!=SQLITE_TEXT ){
|
||||
/* Make a copy of the value to work with. This is because the call
|
||||
** to sqlite3_value_text() below forces the type of the value to
|
||||
** SQLITE_TEXT, and we may need to use it again later. */
|
||||
pFree = pVal = sqlite3_value_dup(pVal);
|
||||
if( pVal==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pText = (const char*)sqlite3_value_text(pVal);
|
||||
nText = sqlite3_value_bytes(pVal);
|
||||
if( pConfig->bLocale && pSeek ){
|
||||
pLoc = (const char*)sqlite3_column_text(pSeek, iCol+pConfig->nCol);
|
||||
nLoc = sqlite3_column_bytes(pSeek, iCol + pConfig->nCol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,6 +587,7 @@ static int fts5StorageDeleteFromIndex(
|
||||
}
|
||||
sqlite3Fts5ClearLocale(pConfig);
|
||||
}
|
||||
sqlite3_value_free(pFree);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK && p->nTotalRow<1 ){
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
** versions of FTS5. It contains the implementation of an FTS5 auxiliary
|
||||
** function very similar to the FTS4 function matchinfo():
|
||||
**
|
||||
** https://www.sqlite.org/fts3.html#matchinfo
|
||||
** https://sqlite.org/fts3.html#matchinfo
|
||||
**
|
||||
** Known differences are that:
|
||||
**
|
||||
|
||||
@@ -778,4 +778,3 @@ void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
|
||||
}
|
||||
aAscii[0] = 0; /* 0x00 is never a token character */
|
||||
}
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ do_execsql_test 15.1 {
|
||||
}
|
||||
do_catchsql_test 15.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: checksum mismatch for table "t1"}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
@@ -47,11 +47,10 @@ do_test 1.3 {
|
||||
DELETE FROM t1_data WHERE rowid = fts5_rowid('segment', $segid, 4);
|
||||
}
|
||||
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: corruption found reading blob 137438953476 from table "t1"}}
|
||||
do_execsql_test 1.3b {
|
||||
PRAGMA integrity_check(t1);
|
||||
} {{malformed inverted index for FTS5 table main.t1}}
|
||||
|
||||
} {{fts5: corruption found reading blob 137438953476 from table "t1"}}
|
||||
|
||||
do_test 1.4 {
|
||||
db_restore_and_reopen
|
||||
@@ -61,7 +60,7 @@ do_test 1.4 {
|
||||
rowid = fts5_rowid('segment', $segid, 4);
|
||||
}
|
||||
catchsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: corruption found reading blob 137438953476 from table "t1"}}
|
||||
|
||||
db_restore_and_reopen
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
||||
|
||||
@@ -109,12 +109,12 @@ for {set i [expr $nbyte-2]} {$i>=0} {incr i -1} {
|
||||
|
||||
do_catchsql_test 2.$i.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/1.*fts5: corruption.*/}
|
||||
|
||||
do_test 2.$i.3 {
|
||||
set res [catchsql {SELECT rowid FROM t1 WHERE t1 MATCH 'x*'}]
|
||||
expr {
|
||||
$res=="1 {database disk image is malformed}"
|
||||
[string match {*fts5: corruption*} $res]
|
||||
|| $res=="0 {$all}"
|
||||
}
|
||||
} 1
|
||||
@@ -160,17 +160,17 @@ foreach {tn hdr} {
|
||||
close $fd
|
||||
|
||||
set res [catchsql {SELECT rowid FROM x3 WHERE x3 MATCH 'x AND a'}]
|
||||
if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
|
||||
if {[string match {*fts5: corruption*} $res]} {incr nCorrupt}
|
||||
set {} 1
|
||||
} {1}
|
||||
|
||||
if {($tn2 % 10)==0 && $existing != $hdr} {
|
||||
do_test 3.$tn.$tn2.2 {
|
||||
catchsql { INSERT INTO x3(x3) VALUES('integrity-check') }
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corruption.*/}
|
||||
do_execsql_test 3.$tn.$tn2.3 {
|
||||
PRAGMA integrity_check(x3);
|
||||
} {{malformed inverted index for FTS5 table main.x3}}
|
||||
} {/.*fts5: corruption.*/}
|
||||
}
|
||||
|
||||
execsql ROLLBACK
|
||||
@@ -209,7 +209,7 @@ foreach {tn nCut} {
|
||||
set res [catchsql {
|
||||
SELECT rowid FROM x4 WHERE x4 MATCH 'a' ORDER BY 1 DESC
|
||||
}]
|
||||
if {$res == "1 {database disk image is malformed}"} {incr nCorrupt}
|
||||
if {[string match {*fts5: corruption*} $res]} {incr nCorrupt}
|
||||
set {} 1
|
||||
} {1}
|
||||
|
||||
|
||||
+254
-43
File diff suppressed because one or more lines are too long
@@ -237,7 +237,7 @@ do_test 1.0 {
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
SELECT * FROM t1('R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC;
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -450,7 +450,7 @@ do_test 2.0 {
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
SELECT * FROM t1('R*R*R*R*') WHERE (a,b)<=(current_date,0) ORDER BY rowid DESC;
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -569,7 +569,7 @@ do_test 3.0 {
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
UPDATE t1 SET b=quote(zeroblob(200)) WHERE a MATCH 'thra*T';
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
@@ -878,7 +878,7 @@ do_execsql_test 5.1 {
|
||||
}
|
||||
do_catchsql_test 5.4 {
|
||||
UPDATE t1 SET content=randomblob(500);
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
@@ -123,6 +123,6 @@ do_execsql_test 2.2 {
|
||||
|
||||
do_catchsql_test 2.3 {
|
||||
DELETE FROM t1 WHERE rowid = 1
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -32,7 +32,7 @@ sqlite3 db test.db
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
SELECT * FROM t1
|
||||
} {1 {database disk image is malformed}}
|
||||
} {1 {fts5: corrupt structure record for table "t1"}}
|
||||
do_catchsql_test 1.3 {
|
||||
DROP TABLE t1
|
||||
} {0 {}}
|
||||
@@ -90,5 +90,58 @@ do_execsql_test 3.7 {
|
||||
SELECT * FROM sqlite_schema
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
proc hex_to_blob {hex} {
|
||||
binary encode hex $hex
|
||||
}
|
||||
db func hex_to_blob hex_to_blob
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x, content='', contentless_delete=1);
|
||||
BEGIN;
|
||||
INSERT INTO x1(rowid, x) VALUES(1, 'a b c d e f g h');
|
||||
INSERT INTO x1(rowid, x) VALUES(2, 'a b c d e f g h');
|
||||
COMMIT;
|
||||
DELETE FROM x1 WHERE rowid=1;
|
||||
}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
SELECT hex(block) FROM x1_data WHERE id=10
|
||||
} {
|
||||
00000000FF00000101010200010101010101010102
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
UPDATE x1_data SET block=
|
||||
X'00000000FF00000101010200010101010101819C9B95A8000102'
|
||||
WHERE id=10;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.2.2 {
|
||||
SELECT * FROM x1('c d e');
|
||||
} {1 {out of memory}}
|
||||
|
||||
do_execsql_test 4.3.1 {
|
||||
UPDATE x1_data SET block=
|
||||
X'00000000FF000001010102000101010101019282AFF9A0000102'
|
||||
WHERE id=10;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.3.2 {
|
||||
SELECT * FROM x1('c d e');
|
||||
} {1 {out of memory}}
|
||||
|
||||
do_execsql_test 4.4.1 {
|
||||
UPDATE x1_data SET block=
|
||||
X'00000000FF000001010102000101010101018181808080130102'
|
||||
WHERE id=10;
|
||||
}
|
||||
|
||||
do_catchsql_test 4.3.2 {
|
||||
SELECT * FROM x1('c d e');
|
||||
} {1 {out of memory}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -325,5 +325,25 @@ ifcapable foreignkey {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 13.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
INSERT INTO t1 VALUES('abc def', X'123456');
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
|
||||
do_faultsim_test 13 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
UPDATE t1 SET a='def abc'
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -37,6 +37,12 @@ do_execsql_test 2.1 {
|
||||
INSERT INTO yy(yy) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO yy(yy) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
|
||||
@@ -40,4 +40,53 @@ do_execsql_test 1.2 {
|
||||
SELECT * FROM t1 LEFT JOIN vt ON (vt MATCH 'abc')
|
||||
} {1 abc 2 abc}
|
||||
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(14);
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT * FROM vt LEFT JOIN t1 ON vt.rowid = 1;
|
||||
} {
|
||||
abc 14
|
||||
xyz {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t0 USING fts5(a,b);
|
||||
INSERT INTO t0(a,b)VALUES(1,0);
|
||||
CREATE TABLE t1(x);
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT * FROM t0 LEFT JOIN t1;
|
||||
} {1 0 {}}
|
||||
|
||||
breakpoint
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT * FROM t0 LEFT JOIN t1 ON t0.b MATCH '1';
|
||||
} {1 {no query solution}}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT * FROM t0 LEFT JOIN t1 ON +b MATCH '1';
|
||||
} {1 0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t0 USING fts5(c0, c1);
|
||||
INSERT INTO t0(c0,c1) VALUES (1,0);
|
||||
}
|
||||
|
||||
do_catchsql_test 3.1 {
|
||||
SELECT * FROM t0
|
||||
LEFT JOIN ( SELECT 0 AS col_0 )
|
||||
ON ((((t0.c1 MATCH '1')AND(CASE WHEN t0.c0 THEN CAST(t0.c1 AS INTEGER) ELSE 1 END))));
|
||||
} {1 {no query solution}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -528,9 +528,12 @@ do_execsql_test 16.0 {
|
||||
BEGIN EXCLUSIVE;
|
||||
}
|
||||
|
||||
sqlite3 db2 test.db
|
||||
do_test 16.1 {
|
||||
catchsql { SELECT * FROM t1 } db2
|
||||
set rc [catch {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {SELECT * FROM t1}
|
||||
} errmsg]
|
||||
lappend rc $errmsg
|
||||
} {1 {database is locked}}
|
||||
|
||||
do_execsql_test 16.2 {
|
||||
@@ -542,4 +545,3 @@ do_test 16.3 {
|
||||
} {0 {}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ do_execsql_test 1.5 {
|
||||
|
||||
do_catchsql_test 1.6 {
|
||||
INSERT INTO f1(f1) VALUES('integrity-check');
|
||||
} {1 {database disk image is malformed}}
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
INSERT INTO f1(f1) VALUES('rebuild');
|
||||
|
||||
@@ -28,4 +28,34 @@ do_execsql_test 1.1 {
|
||||
INSERT INTO sss VALUES('まりや');
|
||||
}
|
||||
|
||||
foreach {tn enc tok} {
|
||||
1 utf-8 ascii
|
||||
2 utf-16 ascii
|
||||
3 utf-8 unicode61
|
||||
4 utf-16 unicode61
|
||||
} {
|
||||
reset_db
|
||||
|
||||
do_execsql_test 1.$tn.0 "
|
||||
PRAGMA encoding = '$enc';
|
||||
CREATE VIRTUAL TABLE vt2 USING fts5(c0, c1, tokenize=$tok);
|
||||
"
|
||||
|
||||
do_execsql_test 1.$tn.1 {
|
||||
INSERT INTO vt2(c0, c1) VALUES ('bhal', x'17db');
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.2 {
|
||||
UPDATE vt2 SET c0='bhal';
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.3 {
|
||||
INSERT INTO vt2(vt2) VALUES('integrity-check')
|
||||
}
|
||||
|
||||
do_execsql_test 1.$tn.4 {
|
||||
SELECT quote(c1) FROM vt2
|
||||
} {X'17DB'}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-1
@@ -429,7 +429,7 @@ $(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
|
||||
.FORCE: doc
|
||||
doc: $(doc.index)
|
||||
javadoc: $(doc.index)
|
||||
# Force rebild of docs
|
||||
# Force rebuild of docs
|
||||
redoc:
|
||||
@rm -f $(doc.index)
|
||||
@$(MAKE) doc
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
** Which sqlite3.c we're using needs to be configurable to enable
|
||||
** building against a custom copy, e.g. the SEE variant. We have to
|
||||
** include sqlite3.c, as opposed to sqlite3.h, in order to get access
|
||||
** to some interal details like SQLITE_MAX_... and friends. This
|
||||
** to some internal details like SQLITE_MAX_... and friends. This
|
||||
** increases the rebuild time considerably but we need this in order
|
||||
** to access some internal functionality and keep the to-Java-exported
|
||||
** values of SQLITE_MAX_... and SQLITE_LIMIT_... in sync with the C
|
||||
@@ -5512,7 +5512,7 @@ static inline jobject new_java_fts5_api(JNIEnv * const env, fts5_api *sv){
|
||||
** Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton
|
||||
** instance, or NULL on OOM.
|
||||
*/
|
||||
static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){
|
||||
static jobject s3jni_getFts5ExtensionApi(JNIEnv * const env){
|
||||
if( !SJG.fts5.jExt ){
|
||||
S3JniGlobal_mutex_enter;
|
||||
if( !SJG.fts5.jExt ){
|
||||
@@ -5578,7 +5578,7 @@ JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){
|
||||
|
||||
|
||||
JniDeclFtsXA(jobject,getInstance)(JniArgsEnvClass){
|
||||
return s3jni_getFts5ExensionApi(env);
|
||||
return s3jni_getFts5ExtensionApi(env);
|
||||
}
|
||||
|
||||
JniDeclFtsXA(jint,xColumnCount)(JniArgsEnvObj,jobject jCtx){
|
||||
@@ -5641,7 +5641,7 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi,
|
||||
S3JniDeclLocal_env;
|
||||
|
||||
assert(pAux);
|
||||
jFXA = s3jni_getFts5ExensionApi(env);
|
||||
jFXA = s3jni_getFts5ExtensionApi(env);
|
||||
if( !jFXA ) goto error_oom;
|
||||
jpFts = new_java_Fts5Context(env, pFts);
|
||||
if( !jpFts ) goto error_oom;
|
||||
|
||||
@@ -1342,7 +1342,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1data_1count
|
||||
* Method: sqlite3_db_config
|
||||
* Signature: (Lorg/sqlite/jni/capi/sqlite3;IILorg/sqlite/jni/capi/OutputPointer/Int32;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1db_1config__Lorg_sqlite_jni_capi_sqlite3_2IILorg_sqlite_jni_capi_OutputPointer_Int32_2
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1db_1config__Lorg_sqlite_jni_capi_sqlite3_2IILorg_sqlite_jni_capi_OutputPointer_00024Int32_2
|
||||
(JNIEnv *, jclass, jobject, jint, jint, jobject);
|
||||
|
||||
/*
|
||||
|
||||
@@ -31,11 +31,11 @@ import java.lang.annotation.*;
|
||||
never pass a null value to the callback for that parameter.</p>
|
||||
|
||||
<p>Passing a null, for this annotation's definition of null, for
|
||||
any parameter marked with this annoation specifically invokes
|
||||
any parameter marked with this annotation specifically invokes
|
||||
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
|
||||
parameter marked with this annotation specifically invokes undefined
|
||||
behavior (see below). Such values are treated as C pointers in the
|
||||
JNI layer.</p>
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
argument, the context is set to the given initial value. On all other
|
||||
calls, the 2nd argument is ignored.
|
||||
|
||||
@see SQLFunction.PerContextState#getAggregateState
|
||||
@see AggregateFunction.PerContextState#getAggregateState
|
||||
*/
|
||||
protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){
|
||||
return map.getAggregateState(cx, initialValue);
|
||||
@@ -130,7 +130,7 @@ public abstract class AggregateFunction<T> implements SQLFunction {
|
||||
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
|
||||
see AggregateFunction.PerContextState#takeAggregateState
|
||||
*/
|
||||
protected final T takeAggregateState(sqlite3_context cx){
|
||||
return map.takeAggregateState(cx);
|
||||
|
||||
@@ -12,14 +12,9 @@
|
||||
** This file declares the main JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.capi;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
import java.util.Arrays;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
|
||||
/**
|
||||
This class contains the entire C-style sqlite3 JNI API binding,
|
||||
@@ -169,7 +164,7 @@ public final class CApi {
|
||||
<p>Like the C API, it returns 0 if allocation fails or if
|
||||
initialize is false and no prior aggregate context was allocated
|
||||
for cx. If initialize is true then it returns 0 only on
|
||||
allocation error. In all casses, 0 is considered the sentinel
|
||||
allocation error. In all cases, 0 is considered the sentinel
|
||||
"not a key" value.
|
||||
*/
|
||||
public static native long sqlite3_aggregate_context(sqlite3_context cx, boolean initialize);
|
||||
@@ -563,7 +558,7 @@ public final class CApi {
|
||||
sqlite3_blob_open(db.getNativePointer(), dbName, tableName, columnName,
|
||||
iRow, flags, out);
|
||||
return out.take();
|
||||
};
|
||||
}
|
||||
|
||||
private static native int sqlite3_blob_read(
|
||||
@NotNull long ptrToBlob, @NotNull byte[] target, int srcOffset
|
||||
@@ -1298,7 +1293,7 @@ public final class CApi {
|
||||
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
sqlite3_open(filename, out);
|
||||
return out.take();
|
||||
};
|
||||
}
|
||||
|
||||
public static native int sqlite3_open_v2(
|
||||
@Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb,
|
||||
@@ -1314,7 +1309,7 @@ public final class CApi {
|
||||
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
sqlite3_open_v2(filename, out, flags, zVfs);
|
||||
return out.take();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
The sqlite3_prepare() family of functions require slightly
|
||||
@@ -1410,7 +1405,7 @@ public final class CApi {
|
||||
|
||||
/**
|
||||
Works like the canonical sqlite3_prepare_v2() but its "tail"
|
||||
output paramter is returned as the index offset into the given
|
||||
output parameter is returned as the index offset into the given
|
||||
byte array at which SQL parsing stopped.
|
||||
*/
|
||||
public static int sqlite3_prepare_v2(
|
||||
@@ -1462,7 +1457,7 @@ public final class CApi {
|
||||
|
||||
/**
|
||||
Works like the canonical sqlite3_prepare_v2() but its "tail"
|
||||
output paramter is returned as the index offset into the given
|
||||
output parameter is returned as the index offset into the given
|
||||
byte array at which SQL parsing stopped.
|
||||
*/
|
||||
public static int sqlite3_prepare_v3(
|
||||
@@ -1542,7 +1537,7 @@ public final class CApi {
|
||||
int rc = 0;
|
||||
final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
|
||||
while( 0==rc && pos<sqlChunk.length ){
|
||||
sqlite3_stmt stmt = null;
|
||||
sqlite3_stmt stmt;
|
||||
if( pos>0 ){
|
||||
sqlChunk = Arrays.copyOfRange(sqlChunk, pos,
|
||||
sqlChunk.length);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
package org.sqlite.jni.capi;
|
||||
/**
|
||||
This marker interface exists soley for use as a documentation and
|
||||
This marker interface exists solely for use as a documentation and
|
||||
class-grouping tool. It should be applied to interfaces or
|
||||
classes which have a call() method implementing some specific
|
||||
callback interface on behalf of the C library.
|
||||
@@ -34,7 +34,7 @@ package org.sqlite.jni.capi;
|
||||
proxying for, minus the {@code sqlite3_} prefix, plus a {@code
|
||||
Callback} suffix. e.g. {@code sqlite3_busy_handler()}'s callback is
|
||||
named {@code BusyHandlerCallback}. Exceptions are made where that
|
||||
would potentially be ambiguous, e.g. {@link ConfigSqllogCallback}
|
||||
would potentially be ambiguous, e.g. {@link ConfigSqlLogCallback}
|
||||
instead of {@code ConfigCallback} because the {@code
|
||||
sqlite3_config()} interface may need to support more callback types
|
||||
in the future.
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.sqlite.jni.capi;
|
||||
from the native JNI code is unduly quirky due to a lack of
|
||||
autoboxing at that level.
|
||||
|
||||
<p>The usage is similar for all of thes types:
|
||||
<p>The usage is similar for all of these types:
|
||||
|
||||
<pre>{@code
|
||||
OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
@@ -55,9 +55,9 @@ public final class OutputPointer {
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.capi.sqlite3 get(){return value;}
|
||||
public org.sqlite.jni.capi.sqlite3 get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.capi.sqlite3 take(){
|
||||
public org.sqlite.jni.capi.sqlite3 take(){
|
||||
final org.sqlite.jni.capi.sqlite3 v = value;
|
||||
value = null;
|
||||
return v;
|
||||
@@ -76,9 +76,9 @@ public final class OutputPointer {
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.capi.sqlite3_blob get(){return value;}
|
||||
public org.sqlite.jni.capi.sqlite3_blob get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.capi.sqlite3_blob take(){
|
||||
public org.sqlite.jni.capi.sqlite3_blob take(){
|
||||
final org.sqlite.jni.capi.sqlite3_blob v = value;
|
||||
value = null;
|
||||
return v;
|
||||
@@ -98,9 +98,9 @@ public final class OutputPointer {
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.capi.sqlite3_stmt get(){return value;}
|
||||
public org.sqlite.jni.capi.sqlite3_stmt get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.capi.sqlite3_stmt take(){
|
||||
public org.sqlite.jni.capi.sqlite3_stmt take(){
|
||||
final org.sqlite.jni.capi.sqlite3_stmt v = value;
|
||||
value = null;
|
||||
return v;
|
||||
@@ -120,9 +120,9 @@ public final class OutputPointer {
|
||||
/** Sets the current value to null. */
|
||||
public void clear(){value = null;}
|
||||
/** Returns the current value. */
|
||||
public final org.sqlite.jni.capi.sqlite3_value get(){return value;}
|
||||
public org.sqlite.jni.capi.sqlite3_value get(){return value;}
|
||||
/** Equivalent to calling get() then clear(). */
|
||||
public final org.sqlite.jni.capi.sqlite3_value take(){
|
||||
public org.sqlite.jni.capi.sqlite3_value take(){
|
||||
final org.sqlite.jni.capi.sqlite3_value v = value;
|
||||
value = null;
|
||||
return v;
|
||||
@@ -144,9 +144,9 @@ public final class OutputPointer {
|
||||
/** Initializes with the value v. */
|
||||
public Bool(boolean v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final boolean get(){return value;}
|
||||
public boolean get(){return value;}
|
||||
/** Sets the current value to v. */
|
||||
public final void set(boolean v){value = v;}
|
||||
public void set(boolean v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,9 +164,9 @@ public final class OutputPointer {
|
||||
/** Initializes with the value v. */
|
||||
public Int32(int v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final int get(){return value;}
|
||||
public int get(){return value;}
|
||||
/** Sets the current value to v. */
|
||||
public final void set(int v){value = v;}
|
||||
public void set(int v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,9 +184,9 @@ public final class OutputPointer {
|
||||
/** Initializes with the value v. */
|
||||
public Int64(long v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final long get(){return value;}
|
||||
public long get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(long v){value = v;}
|
||||
public void set(long v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,9 +204,9 @@ public final class OutputPointer {
|
||||
/** Initializes with the value v. */
|
||||
public String(java.lang.String v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final java.lang.String get(){return value;}
|
||||
public java.lang.String get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(java.lang.String v){value = v;}
|
||||
public void set(java.lang.String v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,9 +224,9 @@ public final class OutputPointer {
|
||||
/** Initializes with the value v. */
|
||||
public ByteArray(byte[] v){value = v;}
|
||||
/** Returns the current value. */
|
||||
public final byte[] get(){return value;}
|
||||
public byte[] get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(byte[] v){value = v;}
|
||||
public void set(byte[] v){value = v;}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,8 +246,8 @@ public final class OutputPointer {
|
||||
/** 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;}
|
||||
public java.nio.ByteBuffer get(){return value;}
|
||||
/** Sets the current value. */
|
||||
public final void set(java.nio.ByteBuffer v){value = v;}
|
||||
public void set(java.nio.ByteBuffer v){value = v;}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
A PrepareMultiCallback impl which steps entirely through a result set,
|
||||
ignoring all non-error results.
|
||||
*/
|
||||
public static final class StepAll implements PrepareMultiCallback {
|
||||
final class StepAll implements PrepareMultiCallback {
|
||||
public StepAll(){}
|
||||
/**
|
||||
Calls sqlite3_step() on st until it returns something other than
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
** SQLTester framework.
|
||||
*/
|
||||
package org.sqlite.jni.capi;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -31,7 +30,7 @@ enum ResultBufferMode {
|
||||
ESCAPED,
|
||||
//! Append output as-is
|
||||
ASIS
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Modes to specify how to emit multi-row output from
|
||||
@@ -42,7 +41,7 @@ enum ResultRowMode {
|
||||
ONELINE,
|
||||
//! Add a newline between each result row.
|
||||
NEWLINE
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Base exception type for test-related failures.
|
||||
@@ -278,7 +277,7 @@ public class SQLTester {
|
||||
}
|
||||
|
||||
private StringBuilder clearBuffer(StringBuilder b){
|
||||
b.setLength(0);;
|
||||
b.setLength(0);
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -334,7 +333,7 @@ public class SQLTester {
|
||||
return this;
|
||||
}
|
||||
|
||||
sqlite3 setCurrentDb(int n) throws Exception{
|
||||
sqlite3 setCurrentDb(int n){
|
||||
affirmDbId(n);
|
||||
iCurrentDb = n;
|
||||
return this.aDb[n];
|
||||
@@ -342,7 +341,7 @@ public class SQLTester {
|
||||
|
||||
sqlite3 getCurrentDb(){ return aDb[iCurrentDb]; }
|
||||
|
||||
sqlite3 getDbById(int id) throws Exception{
|
||||
sqlite3 getDbById(int id){
|
||||
return affirmDbId(id).aDb[id];
|
||||
}
|
||||
|
||||
@@ -363,7 +362,7 @@ public class SQLTester {
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3 openDb(String name, boolean createIfNeeded) throws DbException {
|
||||
sqlite3 openDb(String name, boolean createIfNeeded) {
|
||||
closeDb();
|
||||
int flags = SQLITE_OPEN_READWRITE;
|
||||
if( createIfNeeded ) flags |= SQLITE_OPEN_CREATE;
|
||||
@@ -755,7 +754,7 @@ abstract class Command {
|
||||
fall in the inclusive range (min,max) then this function throws. Use
|
||||
a max value of -1 to mean unlimited.
|
||||
*/
|
||||
protected final void argcCheck(TestScript ts, String[] argv, int min, int max) throws Exception{
|
||||
protected final void argcCheck(TestScript ts, String[] argv, int min, int max){
|
||||
int argc = argv.length-1;
|
||||
if(argc<min || (max>=0 && argc>max)){
|
||||
if( min==max ){
|
||||
@@ -771,16 +770,16 @@ abstract class Command {
|
||||
/**
|
||||
Equivalent to argcCheck(argv,argc,argc).
|
||||
*/
|
||||
protected final void argcCheck(TestScript ts, String[] argv, int argc) throws Exception{
|
||||
protected final void argcCheck(TestScript ts, String[] argv, int argc){
|
||||
argcCheck(ts, argv, argc, argc);
|
||||
}
|
||||
}
|
||||
|
||||
//! --close command
|
||||
class CloseDbCommand extends Command {
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,0,1);
|
||||
Integer id;
|
||||
int id;
|
||||
if(argv.length>1){
|
||||
String arg = argv[1];
|
||||
if("all".equals(arg)){
|
||||
@@ -801,7 +800,7 @@ class CloseDbCommand extends Command {
|
||||
class ColumnNamesCommand extends Command {
|
||||
public void process(
|
||||
SQLTester st, TestScript ts, String[] argv
|
||||
) throws Exception{
|
||||
){
|
||||
argcCheck(ts,argv,1);
|
||||
st.outputColumnNames( Integer.parseInt(argv[1])!=0 );
|
||||
}
|
||||
@@ -809,7 +808,7 @@ class ColumnNamesCommand extends Command {
|
||||
|
||||
//! --db command
|
||||
class DbCommand extends Command {
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,1);
|
||||
t.setCurrentDb( Integer.parseInt(argv[1]) );
|
||||
}
|
||||
@@ -821,7 +820,7 @@ class GlobCommand extends Command {
|
||||
public GlobCommand(){}
|
||||
protected GlobCommand(boolean negate){ this.negate = negate; }
|
||||
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,1,-1);
|
||||
t.incrementTestCounter();
|
||||
final String sql = t.takeInputBuffer();
|
||||
@@ -851,7 +850,7 @@ class JsonBlockCommand extends TableResultCommand {
|
||||
//! --new command
|
||||
class NewDbCommand extends OpenDbCommand {
|
||||
public NewDbCommand(){ super(true); }
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
if(argv.length>1){
|
||||
Util.unlink(argv[1]);
|
||||
}
|
||||
@@ -867,7 +866,7 @@ class NoopCommand extends Command {
|
||||
this.verbose = verbose;
|
||||
}
|
||||
public NoopCommand(){}
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
if( this.verbose ){
|
||||
t.outln("Skipping unhandled command: "+argv[0]);
|
||||
}
|
||||
@@ -885,7 +884,7 @@ class NotGlobCommand extends GlobCommand {
|
||||
class NullCommand extends Command {
|
||||
public void process(
|
||||
SQLTester st, TestScript ts, String[] argv
|
||||
) throws Exception{
|
||||
){
|
||||
argcCheck(ts,argv,1);
|
||||
st.setNullValue( argv[1] );
|
||||
}
|
||||
@@ -896,7 +895,7 @@ class OpenDbCommand extends Command {
|
||||
private boolean createIfNeeded = false;
|
||||
public OpenDbCommand(){}
|
||||
protected OpenDbCommand(boolean c){createIfNeeded = c;}
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,1);
|
||||
t.openDb(argv[1], createIfNeeded);
|
||||
}
|
||||
@@ -906,7 +905,7 @@ class OpenDbCommand extends Command {
|
||||
class PrintCommand extends Command {
|
||||
public void process(
|
||||
SQLTester st, TestScript ts, String[] argv
|
||||
) throws Exception{
|
||||
){
|
||||
st.out(ts.getOutputPrefix(),": ");
|
||||
if( 1==argv.length ){
|
||||
st.out( st.getInputText() );
|
||||
@@ -921,7 +920,7 @@ class ResultCommand extends Command {
|
||||
private final ResultBufferMode bufferMode;
|
||||
protected ResultCommand(ResultBufferMode bm){ bufferMode = bm; }
|
||||
public ResultCommand(){ this(ResultBufferMode.ESCAPED); }
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,0,-1);
|
||||
t.incrementTestCounter();
|
||||
final String sql = t.takeInputBuffer();
|
||||
@@ -939,7 +938,7 @@ class ResultCommand extends Command {
|
||||
|
||||
//! --run command
|
||||
class RunCommand extends Command {
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,0,1);
|
||||
final sqlite3 db = (1==argv.length)
|
||||
? t.getCurrentDb() : t.getDbById( Integer.parseInt(argv[1]) );
|
||||
@@ -959,7 +958,7 @@ class TableResultCommand extends Command {
|
||||
private final boolean jsonMode;
|
||||
protected TableResultCommand(boolean jsonMode){ this.jsonMode = jsonMode; }
|
||||
public TableResultCommand(){ this(false); }
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,0);
|
||||
t.incrementTestCounter();
|
||||
String body = ts.fetchCommandBody(t);
|
||||
@@ -1002,7 +1001,7 @@ class TableResultCommand extends Command {
|
||||
|
||||
//! --testcase command
|
||||
class TestCaseCommand extends Command {
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,1);
|
||||
ts.setTestCaseName(argv[1]);
|
||||
t.clearResultBuffer();
|
||||
@@ -1012,7 +1011,7 @@ class TestCaseCommand extends Command {
|
||||
|
||||
//! --verbosity command
|
||||
class VerbosityCommand extends Command {
|
||||
public void process(SQLTester t, TestScript ts, String[] argv) throws Exception{
|
||||
public void process(SQLTester t, TestScript ts, String[] argv){
|
||||
argcCheck(ts,argv,1);
|
||||
ts.setVerbosity( Integer.parseInt(argv[1]) );
|
||||
}
|
||||
@@ -1020,7 +1019,7 @@ class VerbosityCommand extends Command {
|
||||
|
||||
class CommandDispatcher {
|
||||
|
||||
private static java.util.Map<String,Command> commandMap =
|
||||
private static final java.util.Map<String,Command> commandMap =
|
||||
new java.util.HashMap<>();
|
||||
|
||||
/**
|
||||
@@ -1244,7 +1243,7 @@ class TestScript {
|
||||
}
|
||||
cur.pos = i;
|
||||
final String rv = cur.sb.toString();
|
||||
if( i==cur.src.length && 0==rv.length() ){
|
||||
if( i==cur.src.length && rv.isEmpty() ){
|
||||
return null /* EOF */;
|
||||
}
|
||||
return rv;
|
||||
@@ -1364,7 +1363,7 @@ class TestScript {
|
||||
if( m.find() ){
|
||||
throw new IncompatibleDirective(this, m.group(1)+": "+m.group(3));
|
||||
}
|
||||
if( line.indexOf("\n|")>=0 ){
|
||||
if( line.contains("\n|") ){
|
||||
throw new IncompatibleDirective(this, "newline-pipe combination.");
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -18,11 +18,11 @@ package org.sqlite.jni.capi;
|
||||
They are populated only via that interface.
|
||||
*/
|
||||
public final class TableColumnMetadata {
|
||||
OutputPointer.Bool pNotNull = new OutputPointer.Bool();
|
||||
OutputPointer.Bool pPrimaryKey = new OutputPointer.Bool();
|
||||
OutputPointer.Bool pAutoinc = new OutputPointer.Bool();
|
||||
OutputPointer.String pzCollSeq = new OutputPointer.String();
|
||||
OutputPointer.String pzDataType = new OutputPointer.String();
|
||||
final OutputPointer.Bool pNotNull = new OutputPointer.Bool();
|
||||
final OutputPointer.Bool pPrimaryKey = new OutputPointer.Bool();
|
||||
final OutputPointer.Bool pAutoinc = new OutputPointer.Bool();
|
||||
final OutputPointer.String pzCollSeq = new OutputPointer.String();
|
||||
final OutputPointer.String pzDataType = new OutputPointer.String();
|
||||
|
||||
public TableColumnMetadata(){
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
An annotation for Tester1 tests which we do not want to run in
|
||||
@@ -62,13 +61,13 @@ public class Tester1 implements Runnable {
|
||||
//! List of test*() methods to run.
|
||||
private static List<java.lang.reflect.Method> testMethods = null;
|
||||
//! List of exceptions collected by run()
|
||||
private static List<Exception> listErrors = new ArrayList<>();
|
||||
private static final List<Exception> listErrors = new ArrayList<>();
|
||||
private static final class Metrics {
|
||||
//! Number of times createNewDb() (or equivalent) is invoked.
|
||||
volatile int dbOpen = 0;
|
||||
}
|
||||
|
||||
private Integer tId;
|
||||
private final Integer tId;
|
||||
|
||||
Tester1(Integer id){
|
||||
tId = id;
|
||||
@@ -78,7 +77,7 @@ public class Tester1 implements Runnable {
|
||||
|
||||
public static synchronized void outln(){
|
||||
if( !quietMode ){
|
||||
System.out.println("");
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +522,7 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
affirm(3 == n);
|
||||
affirm("w😃rldhell🤩!🤩".equals(sbuf.toString()));
|
||||
affirm("w😃rldhell🤩!🤩".contentEquals(sbuf));
|
||||
|
||||
try( sqlite3_stmt stmt2 = prepare(db, "SELECT ?, ?") ){
|
||||
rc = sqlite3_bind_text(stmt2, 1, "");
|
||||
@@ -668,7 +667,7 @@ public class Tester1 implements Runnable {
|
||||
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
|
||||
final ValueHolder<Integer> xDestroyCalled = new ValueHolder<>(0);
|
||||
final CollationCallback myCollation = new CollationCallback() {
|
||||
private String myState =
|
||||
private final String myState =
|
||||
"this is local state. There is much like it, but this is mine.";
|
||||
@Override
|
||||
// Reverse-sorts its inputs...
|
||||
@@ -847,7 +846,7 @@ public class Tester1 implements Runnable {
|
||||
SQLFunction funcAgg = new AggregateFunction<Integer>(){
|
||||
@Override public void xStep(sqlite3_context cx, sqlite3_value[] args){
|
||||
/** Throwing from here should emit loud noise on stdout or stderr
|
||||
but the exception is supressed because we have no way to inform
|
||||
but the exception is suppressed because we have no way to inform
|
||||
sqlite about it from these callbacks. */
|
||||
//throw new RuntimeException("Throwing from an xStep");
|
||||
}
|
||||
@@ -1850,7 +1849,7 @@ public class Tester1 implements Runnable {
|
||||
"; insert into t(a) values(1),(2),(3);",
|
||||
"select a from t;"
|
||||
};
|
||||
final List<sqlite3_stmt> liStmt = new ArrayList<sqlite3_stmt>();
|
||||
final List<sqlite3_stmt> liStmt = new ArrayList<>();
|
||||
final PrepareMultiCallback proxy = new PrepareMultiCallback.StepAll();
|
||||
final ValueHolder<String> toss = new ValueHolder<>(null);
|
||||
PrepareMultiCallback m = new PrepareMultiCallback() {
|
||||
@@ -1984,9 +1983,9 @@ public class Tester1 implements Runnable {
|
||||
-v: emit some developer-mode info at the end.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
Integer nThread = 1;
|
||||
int nThread = 1;
|
||||
boolean doSomethingForDev = false;
|
||||
Integer nRepeat = 1;
|
||||
int nRepeat = 1;
|
||||
boolean forceFail = false;
|
||||
boolean sqlLog = false;
|
||||
boolean configLog = false;
|
||||
@@ -2048,7 +2047,7 @@ public class Tester1 implements Runnable {
|
||||
final ConfigLogCallback log = new ConfigLogCallback() {
|
||||
@Override public void call(int code, String msg){
|
||||
outln("ConfigLogCallback: ",ResultCode.getEntryForInt(code),": ", msg);
|
||||
};
|
||||
}
|
||||
};
|
||||
int rc = sqlite3_config( log );
|
||||
affirm( 0==rc );
|
||||
|
||||
@@ -33,5 +33,5 @@ public interface XDestroyCallback {
|
||||
each individual reference, leading to memory corruption or a
|
||||
crash via duplicate free().
|
||||
*/
|
||||
public void xDestroy();
|
||||
void xDestroy();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
** This file is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import org.sqlite.jni.capi.*;
|
||||
import org.sqlite.jni.annotation.*;
|
||||
|
||||
@@ -24,7 +23,7 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
private final int iVersion = 2;
|
||||
|
||||
/* Callback type for used by xQueryPhrase(). */
|
||||
public static interface XQueryPhraseCallback {
|
||||
public interface XQueryPhraseCallback {
|
||||
int call(Fts5ExtensionApi fapi, Fts5Context cx);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,11 @@
|
||||
** This file contains a set of tests for the sqlite3 JNI bindings.
|
||||
*/
|
||||
package org.sqlite.jni.fts5;
|
||||
import java.util.*;
|
||||
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.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class TesterFts5 {
|
||||
|
||||
@@ -88,7 +87,7 @@ public class TesterFts5 {
|
||||
** thrown.
|
||||
*/
|
||||
private static String[] sqlite3_exec(sqlite3 db, String sql) {
|
||||
List<String> aOut = new ArrayList<String>();
|
||||
List<String> aOut = new ArrayList<>();
|
||||
|
||||
/* Iterate through the list of SQL statements. For each, step through
|
||||
** it and add any results to the aOut[] array. */
|
||||
@@ -137,14 +136,14 @@ public class TesterFts5 {
|
||||
** fts5_columncount()
|
||||
*/
|
||||
private static void create_test_functions(sqlite3 db){
|
||||
/*
|
||||
/*
|
||||
** A user-defined-function fts5_rowid() that uses xRowid()
|
||||
*/
|
||||
fts5_extension_function fts5_rowid = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
long rowid = ext.xRowid(fCx);
|
||||
@@ -153,14 +152,14 @@ public class TesterFts5 {
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columncount() - xColumnCount()
|
||||
/*
|
||||
** fts5_columncount() - xColumnCount()
|
||||
*/
|
||||
fts5_extension_function fts5_columncount = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
int nCol = ext.xColumnCount(fCx);
|
||||
@@ -169,14 +168,14 @@ public class TesterFts5 {
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columnsize() - xColumnSize()
|
||||
/*
|
||||
** fts5_columnsize() - xColumnSize()
|
||||
*/
|
||||
fts5_extension_function fts5_columnsize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
@@ -194,14 +193,14 @@ public class TesterFts5 {
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columntext() - xColumnText()
|
||||
/*
|
||||
** fts5_columntext() - xColumnText()
|
||||
*/
|
||||
fts5_extension_function fts5_columntext = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
@@ -219,14 +218,14 @@ public class TesterFts5 {
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
|
||||
/*
|
||||
** fts5_columntotalsize() - xColumnTotalSize()
|
||||
/*
|
||||
** fts5_columntotalsize() - xColumnTotalSize()
|
||||
*/
|
||||
fts5_extension_function fts5_columntsize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
@@ -251,9 +250,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
class fts5_aux implements fts5_extension_function {
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length>1 ){
|
||||
@@ -268,13 +267,13 @@ public class TesterFts5 {
|
||||
|
||||
if( argv.length==1 ){
|
||||
String val = sqlite3_value_text16(argv[0]);
|
||||
if( !val.equals("") ){
|
||||
if( !val.isEmpty() ){
|
||||
ext.xSetAuxdata(fCx, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void xDestroy(){ }
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
** fts5_inst(<fts>);
|
||||
@@ -286,9 +285,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_inst = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
@@ -299,7 +298,7 @@ public class TesterFts5 {
|
||||
OutputPointer.Int32 piPhrase = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piCol = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piOff = new OutputPointer.Int32();
|
||||
String ret = new String();
|
||||
String ret = "";
|
||||
|
||||
int rc = ext.xInstCount(fCx, pnInst);
|
||||
int nInst = pnInst.get();
|
||||
@@ -327,9 +326,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_pinst = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
@@ -338,7 +337,7 @@ public class TesterFts5 {
|
||||
|
||||
OutputPointer.Int32 piCol = new OutputPointer.Int32();
|
||||
OutputPointer.Int32 piOff = new OutputPointer.Int32();
|
||||
String ret = new String();
|
||||
String ret = "";
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
int nPhrase = ext.xPhraseCount(fCx);
|
||||
@@ -350,7 +349,7 @@ public class TesterFts5 {
|
||||
rc==SQLITE_OK && piCol.get()>=0;
|
||||
ext.xPhraseNext(fCx, pIter, piCol, piOff)
|
||||
){
|
||||
if( !ret.equals("") ) ret += " ";
|
||||
if( !ret.isEmpty() ) ret += " ";
|
||||
ret += "{"+ii+" "+piCol.get()+" "+piOff.get()+"}";
|
||||
}
|
||||
}
|
||||
@@ -374,9 +373,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_pcolinst = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
@@ -384,7 +383,7 @@ public class TesterFts5 {
|
||||
}
|
||||
|
||||
OutputPointer.Int32 piCol = new OutputPointer.Int32();
|
||||
String ret = new String();
|
||||
String ret = "";
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
int nPhrase = ext.xPhraseCount(fCx);
|
||||
@@ -396,7 +395,7 @@ public class TesterFts5 {
|
||||
rc==SQLITE_OK && piCol.get()>=0;
|
||||
ext.xPhraseNextColumn(fCx, pIter, piCol)
|
||||
){
|
||||
if( !ret.equals("") ) ret += " ";
|
||||
if( !ret.isEmpty() ) ret += " ";
|
||||
ret += "{"+ii+" "+piCol.get()+"}";
|
||||
}
|
||||
}
|
||||
@@ -415,9 +414,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_rowcount = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=0 ){
|
||||
@@ -440,9 +439,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_phrasesize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
@@ -464,9 +463,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_phrasehits = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
@@ -503,9 +502,9 @@ public class TesterFts5 {
|
||||
*/
|
||||
fts5_extension_function fts5_tokenize = new fts5_extension_function(){
|
||||
@Override public void call(
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5ExtensionApi ext,
|
||||
Fts5Context fCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_context pCx,
|
||||
sqlite3_value argv[]
|
||||
){
|
||||
if( argv.length!=1 ){
|
||||
@@ -515,7 +514,7 @@ public class TesterFts5 {
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
class MyCallback implements XTokenizeCallback {
|
||||
private List<String> myList = new ArrayList<String>();
|
||||
private List<String> myList = new ArrayList<>();
|
||||
|
||||
public String getval() {
|
||||
return String.join("+", myList);
|
||||
@@ -524,7 +523,7 @@ public class TesterFts5 {
|
||||
@Override
|
||||
public int call(int tFlags, byte[] txt, int iStart, int iEnd){
|
||||
try {
|
||||
String str = new String(txt, "UTF-8");
|
||||
String str = new String(txt, StandardCharsets.UTF_8);
|
||||
myList.add(str);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
@@ -39,7 +39,11 @@ public interface fts5_extension_function {
|
||||
*/
|
||||
void xDestroy();
|
||||
|
||||
public static abstract class Abstract implements fts5_extension_function {
|
||||
/**
|
||||
A base implementation of fts5_extension_function() which has a
|
||||
no-op xDestroy() method.
|
||||
*/
|
||||
abstract class Abstract implements fts5_extension_function {
|
||||
@Override public abstract void call(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]);
|
||||
@Override public void xDestroy(){}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
The purpose of the Test Script Interpreter is to read and interpret
|
||||
script files that contain SQL commands and desired results. The
|
||||
interpreter will check results and report any discrepencies found.
|
||||
interpreter will check results and report any discrepancies found.
|
||||
|
||||
The test script files are ASCII text files. The filename always ends with
|
||||
".test". Each script is evaluated independently; context does not carry
|
||||
@@ -160,7 +160,7 @@ the result buffer. This distinction does not matter for the --result
|
||||
command itself, but it is important for related commands like --glob
|
||||
and --notglob. Sometimes test cases will contains a bunch of SQL
|
||||
followed by multiple --glob and/or --notglob statements. All of the
|
||||
globs should be evaluated agains the result buffer, but the SQL should
|
||||
globs should be evaluated against the result buffer, but the SQL should
|
||||
only be run once. This is accomplished by resetting the input buffer
|
||||
but not the result buffer.
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
** 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.
|
||||
|
||||
@@ -22,13 +22,13 @@ import org.sqlite.jni.capi.sqlite3_value;
|
||||
*/
|
||||
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;
|
||||
int DETERMINISTIC = CApi.SQLITE_DETERMINISTIC;
|
||||
int INNOCUOUS = CApi.SQLITE_INNOCUOUS;
|
||||
int DIRECTONLY = CApi.SQLITE_DIRECTONLY;
|
||||
int SUBTYPE = CApi.SQLITE_SUBTYPE;
|
||||
int RESULT_SUBTYPE = CApi.SQLITE_RESULT_SUBTYPE;
|
||||
int UTF8 = CApi.SQLITE_UTF8;
|
||||
int UTF16 = CApi.SQLITE_UTF16;
|
||||
|
||||
/**
|
||||
The Arguments type is an abstraction on top of the lower-level
|
||||
@@ -36,7 +36,7 @@ public interface SqlFunction {
|
||||
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>{
|
||||
final class Arguments implements Iterable<SqlFunction.Arguments.Arg>{
|
||||
private final sqlite3_context cx;
|
||||
private final sqlite3_value args[];
|
||||
public final int length;
|
||||
@@ -144,7 +144,7 @@ public interface SqlFunction {
|
||||
range.
|
||||
*/
|
||||
public void setAuxData(int argNdx, Object o){
|
||||
/* From the API docs: https://www.sqlite.org/c3ref/get_auxdata.html
|
||||
/* From the API docs: https://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
|
||||
@@ -207,7 +207,7 @@ public interface SqlFunction {
|
||||
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 {
|
||||
final class ScalarAdapter extends org.sqlite.jni.capi.ScalarFunction {
|
||||
private final ScalarFunction impl;
|
||||
ScalarAdapter(ScalarFunction impl){
|
||||
this.impl = impl;
|
||||
@@ -234,8 +234,8 @@ public interface SqlFunction {
|
||||
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 {
|
||||
/*cannot be final without duplicating the whole body in WindowAdapter*/
|
||||
private final AggregateFunction impl;
|
||||
AggregateAdapter(AggregateFunction impl){
|
||||
this.impl = impl;
|
||||
@@ -277,7 +277,7 @@ public interface SqlFunction {
|
||||
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 {
|
||||
final class WindowAdapter extends AggregateAdapter {
|
||||
private final WindowFunction impl;
|
||||
WindowAdapter(WindowFunction impl){
|
||||
super(impl);
|
||||
|
||||
@@ -19,7 +19,6 @@ import org.sqlite.jni.capi.sqlite3_stmt;
|
||||
import org.sqlite.jni.capi.sqlite3_backup;
|
||||
import org.sqlite.jni.capi.sqlite3_blob;
|
||||
import org.sqlite.jni.capi.OutputPointer;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
This class represents a database connection, analog to the C-side
|
||||
@@ -388,10 +387,10 @@ public final class Sqlite implements AutoCloseable {
|
||||
return CApi.sqlite3_compileoption_used(optName);
|
||||
}
|
||||
|
||||
private static boolean hasNormalizeSql =
|
||||
private static final boolean hasNormalizeSql =
|
||||
compileOptionUsed("ENABLE_NORMALIZE");
|
||||
|
||||
private static boolean hasSqlLog =
|
||||
private static final boolean hasSqlLog =
|
||||
compileOptionUsed("ENABLE_SQLLOG");
|
||||
|
||||
/**
|
||||
@@ -450,7 +449,7 @@ public final class Sqlite implements AutoCloseable {
|
||||
long current;
|
||||
/** The peak value for the requested status() or libStatus() metric. */
|
||||
long peak;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
As per sqlite3_status64(), but returns its current and high-water
|
||||
@@ -696,7 +695,7 @@ public final class Sqlite implements AutoCloseable {
|
||||
final org.sqlite.jni.capi.OutputPointer.Int32 oTail =
|
||||
new org.sqlite.jni.capi.OutputPointer.Int32();
|
||||
while( pos < sqlChunk.length ){
|
||||
sqlite3_stmt stmt = null;
|
||||
sqlite3_stmt stmt;
|
||||
if( pos>0 ){
|
||||
sqlChunk = java.util.Arrays.copyOfRange(sqlChunk, pos, sqlChunk.length);
|
||||
}
|
||||
@@ -988,15 +987,15 @@ public final class Sqlite implements AutoCloseable {
|
||||
}
|
||||
};
|
||||
checkRc( CApi.sqlite3_trace_v2(thisDb(), traceMask, tc) );
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
|
||||
create new instances.
|
||||
*/
|
||||
public static final class Stmt implements AutoCloseable {
|
||||
private Sqlite _db = null;
|
||||
private sqlite3_stmt stmt = null;
|
||||
private Sqlite _db;
|
||||
private sqlite3_stmt stmt;
|
||||
|
||||
/** Only called by the prepare() factory functions. */
|
||||
Stmt(Sqlite db, sqlite3_stmt stmt){
|
||||
@@ -1379,9 +1378,9 @@ public final class Sqlite implements AutoCloseable {
|
||||
Sqlite.initBackup() to create new instances.
|
||||
*/
|
||||
public static final class Backup implements AutoCloseable {
|
||||
private sqlite3_backup b = null;
|
||||
private Sqlite dbTo = null;
|
||||
private Sqlite dbFrom = null;
|
||||
private sqlite3_backup b;
|
||||
private Sqlite dbTo;
|
||||
private Sqlite dbFrom;
|
||||
|
||||
Backup(Sqlite dbDest, String schemaDest,Sqlite dbSrc, String schemaSrc){
|
||||
this.dbTo = dbDest;
|
||||
@@ -1491,7 +1490,7 @@ public final class Sqlite implements AutoCloseable {
|
||||
Warning: the SQLite core has no mechanism for reporting errors
|
||||
from custom collations and its workflow does not accommodate
|
||||
propagation of exceptions from callbacks. Any exceptions thrown
|
||||
from collations will be silently supressed and sorting results
|
||||
from collations will be silently suppressed and sorting results
|
||||
will be unpredictable.
|
||||
*/
|
||||
int call(byte[] lhs, byte[] rhs);
|
||||
@@ -1506,7 +1505,7 @@ public final class Sqlite implements AutoCloseable {
|
||||
*/
|
||||
public void createCollation(String name, int encoding, Collation c){
|
||||
thisDb();
|
||||
if( null==name || 0==name.length()){
|
||||
if( null==name || name.isEmpty()){
|
||||
throw new IllegalArgumentException("Collation name may not be null or empty.");
|
||||
}
|
||||
if( null==c ){
|
||||
@@ -1599,11 +1598,12 @@ public final class Sqlite implements AutoCloseable {
|
||||
public void setBusyHandler( BusyHandler b ){
|
||||
org.sqlite.jni.capi.BusyHandlerCallback bhc = null;
|
||||
if( null!=b ){
|
||||
bhc = new org.sqlite.jni.capi.BusyHandlerCallback(){
|
||||
/*bhc = new org.sqlite.jni.capi.BusyHandlerCallback(){
|
||||
@Override public int call(int n){
|
||||
return b.call(n);
|
||||
}
|
||||
};
|
||||
};*/
|
||||
bhc = b::call;
|
||||
}
|
||||
checkRc( CApi.sqlite3_busy_handler(thisDb(), bhc) );
|
||||
}
|
||||
@@ -1781,9 +1781,10 @@ public final class Sqlite implements AutoCloseable {
|
||||
public void setProgressHandler( int n, ProgressHandler p ){
|
||||
org.sqlite.jni.capi.ProgressHandlerCallback phc = null;
|
||||
if( null!=p ){
|
||||
phc = new org.sqlite.jni.capi.ProgressHandlerCallback(){
|
||||
/*phc = new org.sqlite.jni.capi.ProgressHandlerCallback(){
|
||||
@Override public int call(){ return p.call(); }
|
||||
};
|
||||
};*/
|
||||
phc = p::call;
|
||||
}
|
||||
CApi.sqlite3_progress_handler( thisDb(), n, phc );
|
||||
}
|
||||
@@ -1808,11 +1809,12 @@ public final class Sqlite implements AutoCloseable {
|
||||
public void setAuthorizer( Authorizer a ) {
|
||||
org.sqlite.jni.capi.AuthorizerCallback ac = null;
|
||||
if( null!=a ){
|
||||
ac = new org.sqlite.jni.capi.AuthorizerCallback(){
|
||||
/*ac = new org.sqlite.jni.capi.AuthorizerCallback(){
|
||||
@Override public int call(int opId, String s1, String s2, String s3, String s4){
|
||||
return a.call(opId, s1, s2, s3, s4);
|
||||
}
|
||||
};
|
||||
};*/
|
||||
ac = a::call;
|
||||
}
|
||||
checkRc( CApi.sqlite3_set_authorizer( thisDb(), ac ) );
|
||||
}
|
||||
@@ -1932,11 +1934,12 @@ public final class Sqlite implements AutoCloseable {
|
||||
final org.sqlite.jni.capi.ConfigLogCallback l =
|
||||
null==log
|
||||
? null
|
||||
: new org.sqlite.jni.capi.ConfigLogCallback() {
|
||||
/*: new org.sqlite.jni.capi.ConfigLogCallback() {
|
||||
@Override public void call(int errCode, String msg){
|
||||
log.call(errCode, msg);
|
||||
}
|
||||
};
|
||||
};*/
|
||||
: log::call;
|
||||
checkRcStatic(CApi.sqlite3_config(l));
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
*/
|
||||
package org.sqlite.jni.wrapper1;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@@ -53,14 +52,14 @@ public class Tester2 implements Runnable {
|
||||
//! List of test*() methods to run.
|
||||
private static List<java.lang.reflect.Method> testMethods = null;
|
||||
//! List of exceptions collected by run()
|
||||
private static List<Exception> listErrors = new ArrayList<>();
|
||||
private static final List<Exception> listErrors = new ArrayList<>();
|
||||
private static final class Metrics {
|
||||
//! Number of times createNewDb() (or equivalent) is invoked.
|
||||
volatile int dbOpen = 0;
|
||||
}
|
||||
|
||||
//! Instance ID.
|
||||
private Integer tId;
|
||||
private final Integer tId;
|
||||
|
||||
Tester2(Integer id){
|
||||
tId = id;
|
||||
@@ -70,7 +69,7 @@ public class Tester2 implements Runnable {
|
||||
|
||||
public static synchronized void outln(){
|
||||
if( !quietMode ){
|
||||
System.out.println("");
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +546,7 @@ public class Tester2 implements Runnable {
|
||||
err = e;
|
||||
}
|
||||
affirm( err!=null );
|
||||
affirm( err.getMessage().indexOf(toss.value)>=0 );
|
||||
affirm( err.getMessage().contains(toss.value) );
|
||||
toss.value = null;
|
||||
|
||||
val.value = 0;
|
||||
@@ -616,7 +615,7 @@ public class Tester2 implements Runnable {
|
||||
final Sqlite db = openDb();
|
||||
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
|
||||
final Sqlite.Collation myCollation = new Sqlite.Collation() {
|
||||
private String myState =
|
||||
private final String myState =
|
||||
"this is local state. There is much like it, but this is mine.";
|
||||
@Override
|
||||
// Reverse-sorts its inputs...
|
||||
@@ -1038,9 +1037,9 @@ public class Tester2 implements Runnable {
|
||||
-v: emit some developer-mode info at the end.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
Integer nThread = 1;
|
||||
int nThread = 1;
|
||||
int nRepeat = 1;
|
||||
boolean doSomethingForDev = false;
|
||||
Integer nRepeat = 1;
|
||||
boolean forceFail = false;
|
||||
boolean sqlLog = false;
|
||||
boolean configLog = false;
|
||||
@@ -1097,7 +1096,7 @@ public class Tester2 implements Runnable {
|
||||
Sqlite.libConfigLog( new Sqlite.ConfigLog() {
|
||||
@Override public void call(int code, String msg){
|
||||
outln("ConfigLog: ",Sqlite.errstr(code),": ", msg);
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
## Miscellaneous Extensions
|
||||
|
||||
This folder contains a collection of smaller loadable extensions.
|
||||
See <https://www.sqlite.org/loadext.html> for instructions on how
|
||||
See <https://sqlite.org/loadext.html> for instructions on how
|
||||
to compile and use loadable extensions.
|
||||
Each extension in this folder is implemented in a single file of C code.
|
||||
|
||||
@@ -10,9 +10,9 @@ header comments for details about each extension. Additional notes are
|
||||
as follows:
|
||||
|
||||
* **carray.c** — This module implements the
|
||||
[carray](https://www.sqlite.org/carray.html) table-valued function.
|
||||
[carray](https://sqlite.org/carray.html) table-valued function.
|
||||
It is a good example of how to go about implementing a custom
|
||||
[table-valued function](https://www.sqlite.org/vtab.html#tabfunc2).
|
||||
[table-valued function](https://sqlite.org/vtab.html#tabfunc2).
|
||||
|
||||
* **csv.c** — A [virtual table](https://sqlite.org/vtab.html)
|
||||
for reading
|
||||
@@ -21,7 +21,7 @@ as follows:
|
||||
* **dbdump.c** — This is not actually a loadable extension, but
|
||||
rather a library that implements an approximate equivalent to the
|
||||
".dump" command of the
|
||||
[command-line shell](https://www.sqlite.org/cli.html).
|
||||
[command-line shell](https://sqlite.org/cli.html).
|
||||
|
||||
* **json1.c** — Various SQL functions and table-valued functions
|
||||
for processing JSON. This extension is already built into the
|
||||
@@ -29,7 +29,7 @@ as follows:
|
||||
<https://sqlite.org/json1.html> for additional information.
|
||||
|
||||
* **memvfs.c** — This file implements a custom
|
||||
[VFS](https://www.sqlite.org/vfs.html) that stores an entire database
|
||||
[VFS](https://sqlite.org/vfs.html) that stores an entire database
|
||||
file in a single block of RAM. It serves as a good example of how
|
||||
to implement a simple custom VFS.
|
||||
|
||||
@@ -38,7 +38,7 @@ as follows:
|
||||
new custom SQL functions for SQLite.
|
||||
|
||||
* **series.c** — This is an implementation of the
|
||||
"generate_series" [virtual table](https://www.sqlite.org/vtab.html).
|
||||
"generate_series" [virtual table](https://sqlite.org/vtab.html).
|
||||
It can make a good template for new custom virtual table implementations.
|
||||
|
||||
* **shathree.c** — An implementation of the sha3() and
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@
|
||||
** AND closure.idname='groupId'
|
||||
** AND closure.parentname='parentId';
|
||||
**
|
||||
** See the documentation at http://www.sqlite.org/loadext.html for information
|
||||
** See the documentation at http://sqlite.org/loadext.html for information
|
||||
** on how to compile and use loadable extensions such as this one.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
|
||||
+144
-58
@@ -67,6 +67,7 @@
|
||||
** data: For a regular file, a blob containing the file data. For a
|
||||
** symlink, a text value containing the text of the link. For a
|
||||
** directory, NULL.
|
||||
** level: Directory hierarchy level. Topmost is 1.
|
||||
**
|
||||
** If a non-NULL value is specified for the optional $dir parameter and
|
||||
** $path is a relative path, then $path is interpreted relative to $dir.
|
||||
@@ -92,20 +93,13 @@ SQLITE_EXTENSION_INIT1
|
||||
# include <dirent.h>
|
||||
# include <utime.h>
|
||||
# include <sys/time.h>
|
||||
# define STRUCT_STAT struct stat
|
||||
#else
|
||||
# include "windows.h"
|
||||
# include <io.h>
|
||||
# include "windirent.h"
|
||||
# include <direct.h>
|
||||
# include "test_windirent.h"
|
||||
# define dirent DIRENT
|
||||
# ifndef chmod
|
||||
# define chmod _chmod
|
||||
# endif
|
||||
# ifndef stat
|
||||
# define stat _stat
|
||||
# endif
|
||||
# define mkdir(path,mode) _mkdir(path)
|
||||
# define lstat(path,buf) stat(path,buf)
|
||||
# define STRUCT_STAT struct _stat
|
||||
# define chmod(path,mode) fileio_chmod(path,mode)
|
||||
# define mkdir(path,mode) fileio_mkdir(path)
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
@@ -121,14 +115,50 @@ SQLITE_EXTENSION_INIT1
|
||||
/*
|
||||
** Structure of the fsdir() table-valued function
|
||||
*/
|
||||
/* 0 1 2 3 4 5 */
|
||||
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
|
||||
/* 0 1 2 3 4 5 6 */
|
||||
#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
|
||||
|
||||
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
|
||||
#define FSDIR_COLUMN_MODE 1 /* Access mode */
|
||||
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
|
||||
#define FSDIR_COLUMN_DATA 3 /* File content */
|
||||
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
|
||||
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
|
||||
#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
|
||||
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
|
||||
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
|
||||
|
||||
/*
|
||||
** UTF8 chmod() function for Windows
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
static int fileio_chmod(const char *zPath, int pmode){
|
||||
sqlite3_int64 sz = strlen(zPath);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
|
||||
int rc;
|
||||
if( b1==0 ) return -1;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
rc = _wchmod(b1, pmode);
|
||||
sqlite3_free(b1);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** UTF8 mkdir() function for Windows
|
||||
*/
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
static int fileio_mkdir(const char *zPath){
|
||||
sqlite3_int64 sz = strlen(zPath);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
|
||||
int rc;
|
||||
if( b1==0 ) return -1;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
rc = _wmkdir(b1);
|
||||
sqlite3_free(b1);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ -260,7 +290,7 @@ LPWSTR utf8_to_utf16(const char *z){
|
||||
*/
|
||||
static void statTimesToUtc(
|
||||
const char *zPath,
|
||||
struct stat *pStatBuf
|
||||
STRUCT_STAT *pStatBuf
|
||||
){
|
||||
HANDLE hFindFile;
|
||||
WIN32_FIND_DATAW fd;
|
||||
@@ -288,10 +318,16 @@ static void statTimesToUtc(
|
||||
*/
|
||||
static int fileStat(
|
||||
const char *zPath,
|
||||
struct stat *pStatBuf
|
||||
STRUCT_STAT *pStatBuf
|
||||
){
|
||||
#if defined(_WIN32)
|
||||
int rc = stat(zPath, pStatBuf);
|
||||
sqlite3_int64 sz = strlen(zPath);
|
||||
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
|
||||
int rc;
|
||||
if( b1==0 ) return 1;
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
|
||||
b1[sz] = 0;
|
||||
rc = _wstat(b1, pStatBuf);
|
||||
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
||||
return rc;
|
||||
#else
|
||||
@@ -306,12 +342,10 @@ static int fileStat(
|
||||
*/
|
||||
static int fileLinkStat(
|
||||
const char *zPath,
|
||||
struct stat *pStatBuf
|
||||
STRUCT_STAT *pStatBuf
|
||||
){
|
||||
#if defined(_WIN32)
|
||||
int rc = lstat(zPath, pStatBuf);
|
||||
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
|
||||
return rc;
|
||||
return fileStat(zPath, pStatBuf);
|
||||
#else
|
||||
return lstat(zPath, pStatBuf);
|
||||
#endif
|
||||
@@ -341,7 +375,7 @@ static int makeDirectory(
|
||||
int i = 1;
|
||||
|
||||
while( rc==SQLITE_OK ){
|
||||
struct stat sStat;
|
||||
STRUCT_STAT sStat;
|
||||
int rc2;
|
||||
|
||||
for(; zCopy[i]!='/' && i<nCopy; i++);
|
||||
@@ -391,7 +425,7 @@ static int writeFile(
|
||||
** be an error though - if there is already a directory at the same
|
||||
** path and either the permissions already match or can be changed
|
||||
** to do so using chmod(), it is not an error. */
|
||||
struct stat sStat;
|
||||
STRUCT_STAT sStat;
|
||||
if( errno!=EEXIST
|
||||
|| 0!=fileStat(zFile, &sStat)
|
||||
|| !S_ISDIR(sStat.st_mode)
|
||||
@@ -587,13 +621,14 @@ struct fsdir_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
|
||||
int nLvl; /* Number of entries in aLvl[] array */
|
||||
int mxLvl; /* Maximum level */
|
||||
int iLvl; /* Index of current entry */
|
||||
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
|
||||
|
||||
const char *zBase;
|
||||
int nBase;
|
||||
|
||||
struct stat sStat; /* Current lstat() results */
|
||||
STRUCT_STAT sStat; /* Current lstat() results */
|
||||
char *zPath; /* Path to current entry */
|
||||
sqlite3_int64 iRowid; /* Current rowid */
|
||||
};
|
||||
@@ -705,7 +740,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
|
||||
mode_t m = pCur->sStat.st_mode;
|
||||
|
||||
pCur->iRowid++;
|
||||
if( S_ISDIR(m) ){
|
||||
if( S_ISDIR(m) && pCur->iLvl+3<pCur->mxLvl ){
|
||||
/* Descend into this directory */
|
||||
int iNew = pCur->iLvl + 1;
|
||||
FsdirLevel *pLvl;
|
||||
@@ -813,7 +848,11 @@ static int fsdirColumn(
|
||||
}else{
|
||||
readFileContents(ctx, pCur->zPath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_LEVEL:
|
||||
sqlite3_result_int(ctx, pCur->iLvl+2);
|
||||
break;
|
||||
case FSDIR_COLUMN_PATH:
|
||||
default: {
|
||||
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
|
||||
@@ -847,8 +886,11 @@ static int fsdirEof(sqlite3_vtab_cursor *cur){
|
||||
/*
|
||||
** xFilter callback.
|
||||
**
|
||||
** idxNum==1 PATH parameter only
|
||||
** idxNum==2 Both PATH and DIR supplied
|
||||
** idxNum bit Meaning
|
||||
** 0x01 PATH=N
|
||||
** 0x02 DIR=N
|
||||
** 0x04 LEVEL<N
|
||||
** 0x08 LEVEL<=N
|
||||
*/
|
||||
static int fsdirFilter(
|
||||
sqlite3_vtab_cursor *cur,
|
||||
@@ -857,6 +899,7 @@ static int fsdirFilter(
|
||||
){
|
||||
const char *zDir = 0;
|
||||
fsdir_cursor *pCur = (fsdir_cursor*)cur;
|
||||
int i;
|
||||
(void)idxStr;
|
||||
fsdirResetCursor(pCur);
|
||||
|
||||
@@ -865,14 +908,24 @@ static int fsdirFilter(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
assert( argc==idxNum && (argc==1 || argc==2) );
|
||||
assert( (idxNum & 0x01)!=0 && argc>0 );
|
||||
zDir = (const char*)sqlite3_value_text(argv[0]);
|
||||
if( zDir==0 ){
|
||||
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
if( argc==2 ){
|
||||
pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
|
||||
i = 1;
|
||||
if( (idxNum & 0x02)!=0 ){
|
||||
assert( argc>i );
|
||||
pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
|
||||
}
|
||||
if( (idxNum & 0x0c)!=0 ){
|
||||
assert( argc>i );
|
||||
pCur->mxLvl = sqlite3_value_int(argv[i++]);
|
||||
if( idxNum & 0x08 ) pCur->mxLvl++;
|
||||
if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000;
|
||||
}else{
|
||||
pCur->mxLvl = 1000000000;
|
||||
}
|
||||
if( pCur->zBase ){
|
||||
pCur->nBase = (int)strlen(pCur->zBase)+1;
|
||||
@@ -901,10 +954,11 @@ static int fsdirFilter(
|
||||
** In this implementation idxNum is used to represent the
|
||||
** query plan. idxStr is unused.
|
||||
**
|
||||
** The query plan is represented by values of idxNum:
|
||||
** The query plan is represented by bits in idxNum:
|
||||
**
|
||||
** (1) The path value is supplied by argv[0]
|
||||
** (2) Path is in argv[0] and dir is in argv[1]
|
||||
** 0x01 The path value is supplied by argv[0]
|
||||
** 0x02 dir is in argv[1]
|
||||
** 0x04 maxdepth is in argv[1] or [2]
|
||||
*/
|
||||
static int fsdirBestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
@@ -913,6 +967,9 @@ static int fsdirBestIndex(
|
||||
int i; /* Loop over constraints */
|
||||
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
|
||||
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
|
||||
int idxLevel = -1; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
|
||||
int idxLevelEQ = 0; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
|
||||
int omitLevel = 0; /* omit the LEVEL constraint */
|
||||
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
|
||||
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
@@ -920,25 +977,48 @@ static int fsdirBestIndex(
|
||||
(void)tab;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
|
||||
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
|
||||
switch( pConstraint->iColumn ){
|
||||
case FSDIR_COLUMN_PATH: {
|
||||
if( pConstraint->usable ){
|
||||
idxPath = i;
|
||||
seenPath = 0;
|
||||
}else if( idxPath<0 ){
|
||||
seenPath = 1;
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
|
||||
switch( pConstraint->iColumn ){
|
||||
case FSDIR_COLUMN_PATH: {
|
||||
if( pConstraint->usable ){
|
||||
idxPath = i;
|
||||
seenPath = 0;
|
||||
}else if( idxPath<0 ){
|
||||
seenPath = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_DIR: {
|
||||
if( pConstraint->usable ){
|
||||
idxDir = i;
|
||||
seenDir = 0;
|
||||
}else if( idxDir<0 ){
|
||||
seenDir = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_LEVEL: {
|
||||
if( pConstraint->usable && idxLevel<0 ){
|
||||
idxLevel = i;
|
||||
idxLevelEQ = 0x08;
|
||||
omitLevel = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FSDIR_COLUMN_DIR: {
|
||||
if( pConstraint->usable ){
|
||||
idxDir = i;
|
||||
seenDir = 0;
|
||||
}else if( idxDir<0 ){
|
||||
seenDir = 1;
|
||||
}
|
||||
break;
|
||||
}else
|
||||
if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
|
||||
&& pConstraint->usable
|
||||
&& idxLevel<0
|
||||
){
|
||||
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
|
||||
idxLevel = i;
|
||||
idxLevelEQ = 0x08;
|
||||
omitLevel = 1;
|
||||
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
|
||||
idxLevel = i;
|
||||
idxLevelEQ = 0x04;
|
||||
omitLevel = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -955,14 +1035,20 @@ static int fsdirBestIndex(
|
||||
}else{
|
||||
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
|
||||
pIdxInfo->idxNum = 0x01;
|
||||
pIdxInfo->estimatedCost = 1.0e9;
|
||||
i = 2;
|
||||
if( idxDir>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
|
||||
pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
|
||||
pIdxInfo->idxNum = 2;
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
}else{
|
||||
pIdxInfo->idxNum = 1;
|
||||
pIdxInfo->estimatedCost = 100.0;
|
||||
pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
|
||||
pIdxInfo->idxNum |= 0x02;
|
||||
pIdxInfo->estimatedCost /= 1.0e4;
|
||||
}
|
||||
if( idxLevel>=0 ){
|
||||
pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
|
||||
pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
|
||||
pIdxInfo->idxNum |= idxLevelEQ;
|
||||
pIdxInfo->estimatedCost /= 1.0e4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
** The delta format is the Fossil delta format, described in a comment
|
||||
** on the delete_create() function implementation below, and also at
|
||||
**
|
||||
** https://www.fossil-scm.org/fossil/doc/trunk/www/delta_format.wiki
|
||||
** https://fossil-scm.org/fossil/doc/trunk/www/delta_format.wiki
|
||||
**
|
||||
** This delta format is used by the RBU extension, which is the main
|
||||
** reason that these routines are included in the extension library.
|
||||
|
||||
+1
-1
@@ -580,7 +580,7 @@ static int seriesFilter(
|
||||
for(i=0; i<argc; i++){
|
||||
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
|
||||
/* If any of the constraints have a NULL value, then return no rows.
|
||||
** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
|
||||
** See ticket https://sqlite.org/src/info/fac496b61722daf2 */
|
||||
returnNoRows = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
**
|
||||
** This module implements the spellfix1 VIRTUAL TABLE that can be used
|
||||
** to search a large vocabulary for close matches. See separate
|
||||
** documentation (http://www.sqlite.org/spellfix1.html) for details.
|
||||
** documentation (http://sqlite.org/spellfix1.html) for details.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ SQLITE_EXTENSION_INIT1
|
||||
** most VFS calls to be recorded.
|
||||
**
|
||||
** To use this module, first compile it as a loadable extension. See
|
||||
** https://www.sqlite.org/loadext.html#build for compilations instructions.
|
||||
** https://sqlite.org/loadext.html#build for compilations instructions.
|
||||
**
|
||||
** After compiling, load this extension, then open database connections to be
|
||||
** measured. Query usages status using the vfsstat virtual table:
|
||||
|
||||
+1
-1
@@ -581,7 +581,7 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
const char *const* a = (const char*const*)pArg;
|
||||
if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
|
||||
const u8 *zArg = (const u8*)a[2];
|
||||
if( zArg[0]>='0' && zArg[0]<=9 ){
|
||||
if( zArg[0]>='0' && zArg[0]<='9' ){
|
||||
pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
|
||||
}else{
|
||||
static const struct {
|
||||
|
||||
+54
-5
@@ -14,6 +14,13 @@
|
||||
** on stdout when its key interfaces are called. This is intended for
|
||||
** interactive analysis and debugging of virtual table interfaces.
|
||||
**
|
||||
** To build this extension as a separately loaded shared library or
|
||||
** DLL, use compiler command-lines similar to the following:
|
||||
**
|
||||
** (linux) gcc -fPIC -shared vtablog.c -o vtablog.so
|
||||
** (mac) clang -fPIC -dynamiclib vtablog.c -o vtablog.dylib
|
||||
** (windows) cl vtablog.c -link -dll -out:vtablog.dll
|
||||
**
|
||||
** Usage example:
|
||||
**
|
||||
** .load ./vtablog
|
||||
@@ -240,7 +247,7 @@ static int vtablogConnect(
|
||||
|
||||
|
||||
/*
|
||||
** This method is the destructor for vtablog_cursor objects.
|
||||
** This method is the destructor for vtablog_vtab objects.
|
||||
*/
|
||||
static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
@@ -252,7 +259,7 @@ static int vtablogDisconnect(sqlite3_vtab *pVtab){
|
||||
}
|
||||
|
||||
/*
|
||||
** This method is the destructor for vtablog_cursor objects.
|
||||
** This method is (also) the destructor for vtablog_vtab objects.
|
||||
*/
|
||||
static int vtablogDestroy(sqlite3_vtab *pVtab){
|
||||
vtablog_vtab *pTab = (vtablog_vtab*)pVtab;
|
||||
@@ -435,6 +442,39 @@ static int vtablogFilter(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return an sqlite3_index_info operator name in static space.
|
||||
** The name is possibly overwritten on subsequent calls.
|
||||
*/
|
||||
static char *vtablogOpName(unsigned char op){
|
||||
static char zUnknown[30];
|
||||
char *zOut;
|
||||
switch( op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ: zOut = "EQ"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GT: zOut = "GT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LE: zOut = "LE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LT: zOut = "LT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GE: zOut = "GE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_MATCH: zOut = "MATCH"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LIKE: zOut = "LIKE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_GLOB: zOut = "GLOB"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_REGEXP: zOut = "REGEXP"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_NE: zOut = "NE"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNOT: zOut = "ISNOT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNOTNULL: zOut = "ISNOTNULL"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_ISNULL: zOut = "ISNULL"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_IS: zOut = "IS"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_LIMIT: zOut = "LIMIT"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_OFFSET: zOut = "OFFSET"; break;
|
||||
case SQLITE_INDEX_CONSTRAINT_FUNCTION: zOut = "FUNCTION"; break;
|
||||
default:
|
||||
sqlite3_snprintf(sizeof(zUnknown),zUnknown,"%d",op);
|
||||
zOut = zUnknown;
|
||||
break;
|
||||
}
|
||||
return zOut;
|
||||
}
|
||||
|
||||
/*
|
||||
** SQLite will invoke this method one or more times while planning a query
|
||||
** that uses the vtablog virtual table. This routine needs to create
|
||||
@@ -451,14 +491,23 @@ static int vtablogBestIndex(
|
||||
printf(" colUsed: 0x%016llx\n", p->colUsed);
|
||||
printf(" nConstraint: %d\n", p->nConstraint);
|
||||
for(i=0; i<p->nConstraint; i++){
|
||||
sqlite3_value *pVal = 0;
|
||||
int rc = sqlite3_vtab_rhs_value(p, i, &pVal);
|
||||
printf(
|
||||
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
|
||||
" constraint[%d]: col=%d termid=%d op=%s usabled=%d coll=%s rhs=",
|
||||
i,
|
||||
p->aConstraint[i].iColumn,
|
||||
p->aConstraint[i].iTermOffset,
|
||||
p->aConstraint[i].op,
|
||||
vtablogOpName(p->aConstraint[i].op),
|
||||
p->aConstraint[i].usable,
|
||||
sqlite3_vtab_collation(p,i));
|
||||
sqlite3_vtab_collation(p,i)
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
vtablogQuote(pVal);
|
||||
printf("\n");
|
||||
}else{
|
||||
printf("N/A\n");
|
||||
}
|
||||
}
|
||||
printf(" nOrderBy: %d\n", p->nOrderBy);
|
||||
for(i=0; i<p->nOrderBy; i++){
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
** 2025-06-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** An implementation of opendir(), readdir(), and closedir() for Windows,
|
||||
** based on the FindFirstFile(), FindNextFile(), and FindClose() APIs
|
||||
** of Win32.
|
||||
**
|
||||
** #include this file inside any C-code module that needs to use
|
||||
** opendir()/readdir()/closedir(). This file is a no-op on non-Windows
|
||||
** machines. On Windows, static functions are defined that implement
|
||||
** those standard interfaces.
|
||||
*/
|
||||
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
|
||||
#define SQLITE_WINDIRENT_H
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#ifndef FILENAME_MAX
|
||||
# define FILENAME_MAX (260)
|
||||
#endif
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#endif
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(m) (0)
|
||||
#endif
|
||||
typedef unsigned short mode_t;
|
||||
|
||||
/* The dirent object for Windows is abbreviated. The only field really
|
||||
** usable by applications is d_name[].
|
||||
*/
|
||||
struct dirent {
|
||||
int d_ino; /* Inode number (synthesized) */
|
||||
unsigned d_attributes; /* File attributes */
|
||||
char d_name[FILENAME_MAX]; /* Null-terminated filename */
|
||||
};
|
||||
|
||||
/* The internals of DIR are opaque according to standards. So it
|
||||
** does not matter what we put here. */
|
||||
typedef struct DIR DIR;
|
||||
struct DIR {
|
||||
intptr_t d_handle; /* Handle for findfirst()/findnext() */
|
||||
struct dirent cur; /* Current entry */
|
||||
};
|
||||
|
||||
/* Ignore hidden and system files */
|
||||
#define WindowsFileToIgnore(a) \
|
||||
((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
|
||||
|
||||
/*
|
||||
** Close a previously opened directory
|
||||
*/
|
||||
static int closedir(DIR *pDir){
|
||||
int rc = 0;
|
||||
if( pDir==0 ){
|
||||
return EINVAL;
|
||||
}
|
||||
if( pDir->d_handle!=0 && pDir->d_handle!=(-1) ){
|
||||
rc = _findclose(pDir->d_handle);
|
||||
}
|
||||
sqlite3_free(pDir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new directory. The directory name should be UTF-8 encoded.
|
||||
** appropriate translations happen automatically.
|
||||
*/
|
||||
static DIR *opendir(const char *zDirName){
|
||||
DIR *pDir;
|
||||
wchar_t *b1;
|
||||
sqlite3_int64 sz;
|
||||
struct _wfinddata_t data;
|
||||
|
||||
pDir = sqlite3_malloc64( sizeof(DIR) );
|
||||
if( pDir==0 ) return 0;
|
||||
memset(pDir, 0, sizeof(DIR));
|
||||
memset(&data, 0, sizeof(data));
|
||||
sz = strlen(zDirName);
|
||||
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
|
||||
if( b1==0 ){
|
||||
closedir(pDir);
|
||||
return NULL;
|
||||
}
|
||||
sz = MultiByteToWideChar(CP_UTF8, 0, zDirName, sz, b1, sz);
|
||||
b1[sz++] = '\\';
|
||||
b1[sz++] = '*';
|
||||
b1[sz] = 0;
|
||||
if( sz+1>sizeof(data.name)/sizeof(data.name[0]) ){
|
||||
closedir(pDir);
|
||||
sqlite3_free(b1);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
|
||||
sqlite3_free(b1);
|
||||
pDir->d_handle = _wfindfirst(data.name, &data);
|
||||
if( pDir->d_handle<0 ){
|
||||
closedir(pDir);
|
||||
return NULL;
|
||||
}
|
||||
while( WindowsFileToIgnore(data) ){
|
||||
memset(&data, 0, sizeof(data));
|
||||
if( _wfindnext(pDir->d_handle, &data)==-1 ){
|
||||
closedir(pDir);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
pDir->cur.d_ino = 0;
|
||||
pDir->cur.d_attributes = data.attrib;
|
||||
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
|
||||
pDir->cur.d_name, FILENAME_MAX, 0, 0);
|
||||
return pDir;
|
||||
}
|
||||
|
||||
/*
|
||||
** Read the next entry from a directory.
|
||||
**
|
||||
** The returned struct-dirent object is managed by DIR. It is only
|
||||
** valid until the next readdir() or closedir() call. Only the
|
||||
** d_name[] field is meaningful. The d_name[] value has been
|
||||
** translated into UTF8.
|
||||
*/
|
||||
static struct dirent *readdir(DIR *pDir){
|
||||
struct _wfinddata_t data;
|
||||
if( pDir==0 ) return 0;
|
||||
if( (pDir->cur.d_ino++)==0 ){
|
||||
return &pDir->cur;
|
||||
}
|
||||
do{
|
||||
memset(&data, 0, sizeof(data));
|
||||
if( _wfindnext(pDir->d_handle, &data)==-1 ){
|
||||
return NULL;
|
||||
}
|
||||
}while( WindowsFileToIgnore(data) );
|
||||
pDir->cur.d_attributes = data.attrib;
|
||||
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
|
||||
pDir->cur.d_name, FILENAME_MAX, 0, 0);
|
||||
return &pDir->cur;
|
||||
}
|
||||
|
||||
#endif /* defined(_WIN32) && defined(_MSC_VER) */
|
||||
@@ -1008,6 +1008,9 @@ static int sqlite3DbdataRegister(sqlite3 *db){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_dbdata_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
|
||||
+10
-2
@@ -64,6 +64,8 @@
|
||||
#endif
|
||||
int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
** If building separately, we will need some setup that is normally
|
||||
** found in sqliteInt.h
|
||||
@@ -95,7 +97,7 @@ typedef unsigned int u32;
|
||||
# define NEVER(X) (X)
|
||||
#endif
|
||||
#ifndef offsetof
|
||||
#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
|
||||
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
|
||||
#endif
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
# define FLEXARRAY
|
||||
@@ -1133,6 +1135,12 @@ static void resetCursor(RtreeCursor *pCsr){
|
||||
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
|
||||
pCsr->pReadAux = pStmt;
|
||||
|
||||
/* The following will only fail if the previous sqlite3_step() call failed,
|
||||
** in which case the error has already been caught. This statement never
|
||||
** encounters an error within an sqlite3_column_xxx() function, as it
|
||||
** calls sqlite3_column_value(), which does not use malloc(). So it is safe
|
||||
** to ignore the error code here. */
|
||||
sqlite3_reset(pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2848,7 +2856,7 @@ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Insert cell pCell into node pNode. Node pNode is the head of a
|
||||
** subtree iHeight high (leaf nodes have iHeight==0).
|
||||
|
||||
@@ -99,5 +99,24 @@ do_execsql_test rtreeH-300 {
|
||||
ORDER BY id;
|
||||
} {box-48,48 box-49,48 box-48,49 xbox-49,49}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test rtreeH-300 {
|
||||
CREATE TABLE t0(c0);
|
||||
INSERT INTO t0(c0) VALUES (NULL);
|
||||
INSERT INTO t0(c0) VALUES (1);
|
||||
CREATE VIRTUAL TABLE t1 USING rtree(c0, c1, c2, +c3 BLOB );
|
||||
INSERT INTO t1(c2, c3, c0) VALUES (1, 2, 1);
|
||||
}
|
||||
|
||||
do_execsql_test rtreeH-310 {
|
||||
SELECT 1 FROM t1 WHERE t1.c3;
|
||||
} {1}
|
||||
|
||||
do_execsql_test rtreeH-320 {
|
||||
SELECT * FROM t0 WHERE NOT EXISTS (
|
||||
SELECT 1 FROM t1 WHERE t1.c3 OR t0.c0 ISNULL
|
||||
);
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
+66
-11
@@ -28,11 +28,17 @@ static void usage(const char *argv0){
|
||||
fprintf(stderr, "Usage: %s FILENAME COMMAND ...\n", argv0);
|
||||
fprintf(stderr,
|
||||
"COMMANDs:\n"
|
||||
" apply DB Apply the changeset to database file DB\n"
|
||||
" concat FILE2 OUT Concatenate FILENAME and FILE2 into OUT\n"
|
||||
" dump Show the complete content of the changeset\n"
|
||||
" invert OUT Write an inverted changeset into file OUT\n"
|
||||
" sql Give a pseudo-SQL rendering of the changeset\n"
|
||||
" apply DB [OPTIONS] Apply the changeset to database file DB. OPTIONS:\n"
|
||||
" -n|--dryrun Test run. Don't apply changes\n"
|
||||
" --enablefk Enable FOREIGN KEY support\n"
|
||||
" --nosavepoint \\\n"
|
||||
" --invert \\___ Flags passed into\n"
|
||||
" --ignorenoop / changeset_apply_v2()\n"
|
||||
" --fknoaction /\n"
|
||||
" concat FILE2 OUT Concatenate FILENAME and FILE2 into OUT\n"
|
||||
" dump Show the complete content of the changeset\n"
|
||||
" invert OUT Write an inverted changeset into file OUT\n"
|
||||
" sql Give a pseudo-SQL rendering of the changeset\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
@@ -161,7 +167,7 @@ static int conflictCallback(
|
||||
case SQLITE_DELETE: zOp = "DELETE from"; break;
|
||||
}
|
||||
printf("%s conflict on %s table %s with primary key", zType, zOp, zTab);
|
||||
for(i=0; i<nCol; i++){
|
||||
for(i=0; i<nCol && abPK; i++){
|
||||
sqlite3_value *pVal;
|
||||
if( abPK[i]==0 ) continue;
|
||||
printf("%s", zSep);
|
||||
@@ -188,21 +194,70 @@ int main(int argc, char **argv){
|
||||
*/
|
||||
if( strcmp(argv[2],"apply")==0 ){
|
||||
sqlite3 *db;
|
||||
if( argc!=4 ) usage(argv[0]);
|
||||
rc = sqlite3_open(argv[3], &db);
|
||||
int bDryRun = 0;
|
||||
int bEnableFK = 0;
|
||||
const char *zDb = 0;
|
||||
int i;
|
||||
int applyFlags = 0;
|
||||
for(i=3; i<argc; i++){
|
||||
const char *zArg = argv[i];
|
||||
if( zArg[0]=='-' ){
|
||||
if( zArg[1]=='-' && zArg[2]!=0 ) zArg++;
|
||||
if( strcmp(zArg, "-n")==0 || strcmp(zArg,"-dryrun")==0 ){
|
||||
bDryRun = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(zArg, "-nosavepoint")==0 ){
|
||||
applyFlags |= SQLITE_CHANGESETAPPLY_NOSAVEPOINT;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(zArg, "-invert")==0 ){
|
||||
applyFlags |= SQLITE_CHANGESETAPPLY_INVERT;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(zArg, "-ignorenoop")==0 ){
|
||||
applyFlags |= SQLITE_CHANGESETAPPLY_IGNORENOOP;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(zArg, "-fknoaction")==0 ){
|
||||
applyFlags |= SQLITE_CHANGESETAPPLY_FKNOACTION;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(zArg, "-enablefk")==0 ){
|
||||
bEnableFK = 1;
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "unknown option: \"%s\"\n", argv[i]);
|
||||
exit(1);
|
||||
}else if( zDb ){
|
||||
fprintf(stderr, "unknown argument: \"%s\"\n", argv[i]);
|
||||
exit(1);
|
||||
}else{
|
||||
zDb = zArg;
|
||||
}
|
||||
}
|
||||
rc = sqlite3_open(zDb, &db);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fprintf(stderr, "unable to open database file \"%s\": %s\n",
|
||||
argv[3], sqlite3_errmsg(db));
|
||||
zDb, sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
exit(1);
|
||||
}
|
||||
if( bEnableFK ){
|
||||
sqlite3_exec(db, "PRAGMA foreign_keys=1;", 0, 0, 0);
|
||||
}
|
||||
sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
nConflict = 0;
|
||||
rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0);
|
||||
if( applyFlags ){
|
||||
rc = sqlite3changeset_apply_v2(db, sz, pBuf, 0, conflictCallback, 0,
|
||||
0, 0, applyFlags);
|
||||
}else{
|
||||
rc = sqlite3changeset_apply(db, sz, pBuf, 0, conflictCallback, 0);
|
||||
}
|
||||
if( rc ){
|
||||
fprintf(stderr, "sqlite3changeset_apply() returned %d\n", rc);
|
||||
}
|
||||
if( nConflict ){
|
||||
if( nConflict || bDryRun ){
|
||||
fprintf(stderr, "%d conflicts - no changes applied\n", nConflict);
|
||||
sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
|
||||
}else if( rc ){
|
||||
|
||||
@@ -202,7 +202,7 @@ do_test 4.3.1 {
|
||||
S attach t4
|
||||
execsql { CREATE TABLE t4(i PRIMARY KEY, b) }
|
||||
list [catch { S diff ixua t4 } msg] $msg
|
||||
} {1 {SQLITE_SCHEMA - table schemas do not match}}
|
||||
} {1 {SQLITE_SCHEMA - no such table: ixua.t4}}
|
||||
S delete
|
||||
do_catchsql_test 4.3.2 {
|
||||
SELECT * FROM ixua.t4;
|
||||
@@ -214,7 +214,7 @@ do_test 4.4.1 {
|
||||
execsql { ANALYZE }
|
||||
execsql { DROP TABLE ixua.sqlite_stat1 }
|
||||
list [catch { S diff ixua sqlite_stat1 } msg] $msg
|
||||
} {1 {SQLITE_SCHEMA - table schemas do not match}}
|
||||
} {1 {SQLITE_SCHEMA - no such table: ixua.sqlite_stat1}}
|
||||
S delete
|
||||
do_catchsql_test 4.4.2 {
|
||||
SELECT * FROM ixua.sqlite_stat1;
|
||||
@@ -230,4 +230,79 @@ do_catchsql_test 4.5.2 {
|
||||
SELECT * FROM ixua.i8;
|
||||
} {1 {no such table: ixua.i8}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that sqlite3session_diff() really does automatically attach tables,
|
||||
# as documented.
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 4.0 {
|
||||
ATTACH 'test.db2' AS two;
|
||||
CREATE TABLE two.t1(a PRIMARY KEY, b, c);
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
|
||||
INSERT INTO two.t1 VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);
|
||||
}
|
||||
|
||||
do_test 4.1 {
|
||||
sqlite3session S db main
|
||||
S attach t8
|
||||
S diff two t1
|
||||
} {}
|
||||
|
||||
do_changeset_test 4.2 S {
|
||||
{DELETE t1 0 X.. {i 1 i 2 i 3} {}}
|
||||
{DELETE t1 0 X.. {i 4 i 5 i 6} {}}
|
||||
{DELETE t1 0 X.. {i 7 i 8 i 9} {}}
|
||||
}
|
||||
|
||||
S delete
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that sqlite3session_diff() really does return errors if the named
|
||||
# table or database do not exist.
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 5.0 {
|
||||
ATTACH 'test.db2' AS two;
|
||||
CREATE TABLE main.t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE main.t2(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE two.t1(a, b INTEGER PRIMARY KEY);
|
||||
}
|
||||
|
||||
proc do_sessions_diff_error {tn db tbl err} {
|
||||
sqlite3session S db main
|
||||
set rc [catch {S diff $db $tbl} msg]
|
||||
|
||||
set ::sdgot [list $rc $msg]
|
||||
do_test $tn [list set sdgot] [list {*}$err]
|
||||
|
||||
S delete
|
||||
}
|
||||
|
||||
# Test that it is an error if the named db is missing.
|
||||
do_sessions_diff_error 5.1 nosuchdb t1 {
|
||||
1 {SQLITE_SCHEMA - no such table: nosuchdb.t1}
|
||||
}
|
||||
|
||||
# Test that it is an error if the named db is present, but named table is not.
|
||||
do_sessions_diff_error 5.2 two t2 {
|
||||
1 {SQLITE_SCHEMA - no such table: two.t2}
|
||||
}
|
||||
|
||||
# Test that it is an error if the tables are present, but schemas do not match.
|
||||
do_sessions_diff_error 5.3 two t1 {
|
||||
1 {SQLITE_SCHEMA - table schemas do not match}
|
||||
}
|
||||
|
||||
do_test 5.4 {
|
||||
sqlite3session S db main
|
||||
|
||||
catch {S diff two blue}
|
||||
catch {S diff two blue}
|
||||
|
||||
S delete
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# 2015 July 14
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
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 sessionI
|
||||
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 1.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
}
|
||||
} {}
|
||||
|
||||
set C [changeset_from_sql {
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
INSERT INTO t1 VALUES(6, 'six');
|
||||
}]
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 one 2 two 3 three 4 four 5 five 6 six
|
||||
}
|
||||
|
||||
proc xFilter {data} {
|
||||
foreach {op tname flag pk old new} $data {}
|
||||
if {$op=="INSERT"} {
|
||||
set ipk [lindex $new 1]
|
||||
return [expr $ipk % 2]
|
||||
}
|
||||
return 1
|
||||
}
|
||||
proc xConflict {args} {
|
||||
}
|
||||
|
||||
sqlite3changeset_apply_v3 db2 $C xConflict xFilter
|
||||
|
||||
do_execsql_test -db db2 1.2 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 one 3 three 5 five
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 1.3 {
|
||||
DELETE FROM t1
|
||||
}
|
||||
sqlite3changeset_apply_v3 db2 $C xConflict
|
||||
|
||||
do_execsql_test -db db2 1.4 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 one 2 two 3 three 4 four 5 five 6 six
|
||||
}
|
||||
|
||||
proc xFilter2 {data} {
|
||||
return 0
|
||||
}
|
||||
do_execsql_test -db db2 1.5 {
|
||||
DELETE FROM t1
|
||||
}
|
||||
sqlite3changeset_apply_v3 db2 $C xConflict xFilter2
|
||||
do_execsql_test -db db2 1.6 {
|
||||
SELECT * FROM t1
|
||||
} { }
|
||||
|
||||
finish_test
|
||||
@@ -96,6 +96,22 @@ do_test 1.5 {
|
||||
$iter finalize
|
||||
grp delete
|
||||
|
||||
do_test 1.6 {
|
||||
set C [changeset_from_sql {
|
||||
INSERT INTO t1 VALUES(1001, hex(randomblob(10000)), hex(randomblob(10000)));
|
||||
INSERT INTO t1 VALUES(2001, hex(randomblob(10000)), hex(randomblob(10000)));
|
||||
INSERT INTO t1 VALUES(3001, hex(randomblob(10000)), hex(randomblob(10000)));
|
||||
}]
|
||||
|
||||
sqlite3changegroup grp
|
||||
set iter [sqlite3changeset_start $C]
|
||||
while {[$iter next]=="SQLITE_ROW"} {
|
||||
grp add_change $iter
|
||||
}
|
||||
$iter finalize
|
||||
} SQLITE_OK
|
||||
|
||||
grp delete
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+207
-38
@@ -1209,6 +1209,8 @@ static int sessionInitTable(
|
||||
if( pTab->nCol==0 ){
|
||||
u8 *abPK;
|
||||
assert( pTab->azCol==0 || pTab->abPK==0 );
|
||||
sqlite3_free(pTab->azCol);
|
||||
pTab->abPK = 0;
|
||||
rc = sessionTableInfo(pSession, db, zDb,
|
||||
pTab->zName, &pTab->nCol, &pTab->nTotalCol, 0, &pTab->azCol,
|
||||
&pTab->azDflt, &pTab->aiIdx, &abPK,
|
||||
@@ -2216,7 +2218,9 @@ int sqlite3session_diff(
|
||||
SessionTable *pTo; /* Table zTbl */
|
||||
|
||||
/* Locate and if necessary initialize the target table object */
|
||||
pSession->bAutoAttach++;
|
||||
rc = sessionFindTable(pSession, zTbl, &pTo);
|
||||
pSession->bAutoAttach--;
|
||||
if( pTo==0 ) goto diff_out;
|
||||
if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){
|
||||
rc = pSession->rc;
|
||||
@@ -2227,17 +2231,43 @@ int sqlite3session_diff(
|
||||
if( rc==SQLITE_OK ){
|
||||
int bHasPk = 0;
|
||||
int bMismatch = 0;
|
||||
int nCol; /* Columns in zFrom.zTbl */
|
||||
int nCol = 0; /* Columns in zFrom.zTbl */
|
||||
int bRowid = 0;
|
||||
u8 *abPK;
|
||||
u8 *abPK = 0;
|
||||
const char **azCol = 0;
|
||||
rc = sessionTableInfo(0, db, zFrom, zTbl,
|
||||
&nCol, 0, 0, &azCol, 0, 0, &abPK,
|
||||
pSession->bImplicitPK ? &bRowid : 0
|
||||
);
|
||||
char *zDbExists = 0;
|
||||
|
||||
/* Check that database zFrom is attached. */
|
||||
zDbExists = sqlite3_mprintf("SELECT * FROM %Q.sqlite_schema", zFrom);
|
||||
if( zDbExists==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
sqlite3_stmt *pDbExists = 0;
|
||||
rc = sqlite3_prepare_v2(db, zDbExists, -1, &pDbExists, 0);
|
||||
if( rc==SQLITE_ERROR ){
|
||||
rc = SQLITE_OK;
|
||||
nCol = -1;
|
||||
}
|
||||
sqlite3_finalize(pDbExists);
|
||||
sqlite3_free(zDbExists);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && nCol==0 ){
|
||||
rc = sessionTableInfo(0, db, zFrom, zTbl,
|
||||
&nCol, 0, 0, &azCol, 0, 0, &abPK,
|
||||
pSession->bImplicitPK ? &bRowid : 0
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTo->nCol!=nCol ){
|
||||
bMismatch = 1;
|
||||
if( nCol<=0 ){
|
||||
rc = SQLITE_SCHEMA;
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("no such table: %s.%s", zFrom, zTbl);
|
||||
}
|
||||
}else{
|
||||
bMismatch = 1;
|
||||
}
|
||||
}else{
|
||||
int i;
|
||||
for(i=0; i<nCol; i++){
|
||||
@@ -3366,14 +3396,15 @@ int sqlite3changeset_start_v2_strm(
|
||||
** object and the buffer is full, discard some data to free up space.
|
||||
*/
|
||||
static void sessionDiscardData(SessionInput *pIn){
|
||||
if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){
|
||||
int nMove = pIn->buf.nBuf - pIn->iNext;
|
||||
if( pIn->xInput && pIn->iCurrent>=sessions_strm_chunk_size ){
|
||||
int nMove = pIn->buf.nBuf - pIn->iCurrent;
|
||||
assert( nMove>=0 );
|
||||
if( nMove>0 ){
|
||||
memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove);
|
||||
memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iCurrent], nMove);
|
||||
}
|
||||
pIn->buf.nBuf -= pIn->iNext;
|
||||
pIn->iNext = 0;
|
||||
pIn->buf.nBuf -= pIn->iCurrent;
|
||||
pIn->iNext -= pIn->iCurrent;
|
||||
pIn->iCurrent = 0;
|
||||
pIn->nData = pIn->buf.nBuf;
|
||||
}
|
||||
}
|
||||
@@ -3727,8 +3758,8 @@ static int sessionChangesetNextOne(
|
||||
p->rc = sessionInputBuffer(&p->in, 2);
|
||||
if( p->rc!=SQLITE_OK ) return p->rc;
|
||||
|
||||
sessionDiscardData(&p->in);
|
||||
p->in.iCurrent = p->in.iNext;
|
||||
sessionDiscardData(&p->in);
|
||||
|
||||
/* If the iterator is already at the end of the changeset, return DONE. */
|
||||
if( p->in.iNext>=p->in.nData ){
|
||||
@@ -5151,6 +5182,10 @@ static int sessionChangesetApply(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
int(*xFilterIter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of fifth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
@@ -5291,6 +5326,9 @@ static int sessionChangesetApply(
|
||||
** next change. A log message has already been issued. */
|
||||
if( schemaMismatch ) continue;
|
||||
|
||||
/* If this is a call to apply_v3(), invoke xFilterIter here. */
|
||||
if( xFilterIter && 0==xFilterIter(pCtx, pIter) ) continue;
|
||||
|
||||
rc = sessionApplyOneWithRetry(db, pIter, &sApply, xConflict, pCtx);
|
||||
}
|
||||
|
||||
@@ -5358,6 +5396,87 @@ static int sessionChangesetApply(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by all six sqlite3changeset_apply() variants:
|
||||
**
|
||||
** + sqlite3changeset_apply()
|
||||
** + sqlite3changeset_apply_v2()
|
||||
** + sqlite3changeset_apply_v3()
|
||||
** + sqlite3changeset_apply_strm()
|
||||
** + sqlite3changeset_apply_strm_v2()
|
||||
** + sqlite3changeset_apply_strm_v3()
|
||||
**
|
||||
** Arguments passed to this function are as follows:
|
||||
**
|
||||
** db:
|
||||
** Database handle to apply changeset to main database of.
|
||||
**
|
||||
** nChangeset/pChangeset:
|
||||
** These are both passed zero for the streaming variants. For the normal
|
||||
** apply() functions, these are passed the size of and the buffer containing
|
||||
** the changeset, respectively.
|
||||
**
|
||||
** xInput/pIn:
|
||||
** These are both passed zero for the normal variants. For the streaming
|
||||
** apply() functions, these are passed the input callback and context
|
||||
** pointer, respectively.
|
||||
**
|
||||
** xFilter:
|
||||
** The filter function as passed to apply() or apply_v2() (to filter by
|
||||
** table name), if any. This is always NULL for apply_v3() calls.
|
||||
**
|
||||
** xFilterIter:
|
||||
** The filter function as passed to apply_v3(), if any.
|
||||
**
|
||||
** xConflict:
|
||||
** The conflict handler callback (must not be NULL).
|
||||
**
|
||||
** pCtx:
|
||||
** The context pointer passed to the xFilter and xConflict handler callbacks.
|
||||
**
|
||||
** ppRebase, pnRebase:
|
||||
** Zero for apply(). The rebase changeset output pointers, if any, for
|
||||
** apply_v2() and apply_v3().
|
||||
**
|
||||
** flags:
|
||||
** Zero for apply(). The flags parameter for apply_v2() and apply_v3().
|
||||
*/
|
||||
static int sessionChangesetApplyV23(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int nChangeset, /* Size of changeset in bytes */
|
||||
void *pChangeset, /* Changeset blob */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
const char *zTab /* Table name */
|
||||
),
|
||||
int(*xFilterIter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p /* Handle describing current change */
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(
|
||||
&pIter, xInput, pIn, nChangeset, pChangeset, bInverse, 1
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(db, pIter,
|
||||
xFilter, xFilterIter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Apply the changeset passed via pChangeset/nChangeset to the main
|
||||
** database attached to handle "db".
|
||||
@@ -5379,17 +5498,39 @@ int sqlite3changeset_apply_v2(
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
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);
|
||||
return sessionChangesetApplyV23(db,
|
||||
nChangeset, pChangeset, 0, 0,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
|
||||
return rc;
|
||||
/*
|
||||
** Apply the changeset passed via pChangeset/nChangeset to the main
|
||||
** database attached to handle "db".
|
||||
*/
|
||||
int sqlite3changeset_apply_v3(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int nChangeset, /* Size of changeset in bytes */
|
||||
void *pChangeset, /* Changeset blob */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p /* Handle describing current change */
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
return sessionChangesetApplyV23(db,
|
||||
nChangeset, pChangeset, 0, 0,
|
||||
0, xFilter, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5412,8 +5553,10 @@ int sqlite3changeset_apply(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
return sqlite3changeset_apply_v2(
|
||||
db, nChangeset, pChangeset, xFilter, xConflict, pCtx, 0, 0, 0
|
||||
return sessionChangesetApplyV23(db,
|
||||
nChangeset, pChangeset, 0, 0,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
0, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5422,6 +5565,29 @@ int sqlite3changeset_apply(
|
||||
** attached to handle "db". Invoke the supplied conflict handler callback
|
||||
** to resolve any conflicts encountered while applying the change.
|
||||
*/
|
||||
int sqlite3changeset_apply_v3_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
return sessionChangesetApplyV23(db,
|
||||
0, 0, xInput, pIn,
|
||||
0, xFilter, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
int sqlite3changeset_apply_v2_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
@@ -5439,15 +5605,11 @@ int sqlite3changeset_apply_v2_strm(
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
){
|
||||
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
|
||||
int bInverse = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
int rc = sessionChangesetStart(&pIter, xInput, pIn, 0, 0, bInverse, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionChangesetApply(
|
||||
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
return rc;
|
||||
return sessionChangesetApplyV23(db,
|
||||
0, 0, xInput, pIn,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
ppRebase, pnRebase, flags
|
||||
);
|
||||
}
|
||||
int sqlite3changeset_apply_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
@@ -5464,8 +5626,10 @@ int sqlite3changeset_apply_strm(
|
||||
),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
return sqlite3changeset_apply_v2_strm(
|
||||
db, xInput, pIn, xFilter, xConflict, pCtx, 0, 0, 0
|
||||
return sessionChangesetApplyV23(db,
|
||||
0, 0, xInput, pIn,
|
||||
xFilter, 0, xConflict, pCtx,
|
||||
0, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6087,14 +6251,19 @@ int sqlite3changegroup_add_change(
|
||||
sqlite3_changegroup *pGrp,
|
||||
sqlite3_changeset_iter *pIter
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
if( pIter->in.iCurrent==pIter->in.iNext
|
||||
|| pIter->rc!=SQLITE_OK
|
||||
|| pIter->bInvert
|
||||
){
|
||||
/* Iterator does not point to any valid entry or is an INVERT iterator. */
|
||||
return SQLITE_ERROR;
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
pIter->in.bNoDiscard = 1;
|
||||
rc = sessionOneChangeToHash(pGrp, pIter, 0);
|
||||
}
|
||||
return sessionOneChangeToHash(pGrp, pIter, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -358,9 +358,10 @@ void sqlite3session_table_filter(
|
||||
** is inserted while a session object is enabled, then later deleted while
|
||||
** the same session object is disabled, no INSERT record will appear in the
|
||||
** changeset, even though the delete took place while the session was disabled.
|
||||
** Or, if one field of a row is updated while a session is disabled, and
|
||||
** another field of the same row is updated while the session is enabled, the
|
||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
||||
** Or, if one field of a row is updated while a session is enabled, and
|
||||
** then another field of the same row is updated while the session is disabled,
|
||||
** the resulting changeset will contain an UPDATE change that updates both
|
||||
** fields.
|
||||
*/
|
||||
int sqlite3session_changeset(
|
||||
sqlite3_session *pSession, /* Session object */
|
||||
@@ -432,8 +433,9 @@ sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);
|
||||
** database zFrom the contents of the two compatible tables would be
|
||||
** identical.
|
||||
**
|
||||
** It an error if database zFrom does not exist or does not contain the
|
||||
** required compatible table.
|
||||
** Unless the call to this function is a no-op as described above, it is an
|
||||
** error if database zFrom does not exist or does not contain the required
|
||||
** compatible table.
|
||||
**
|
||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||
@@ -568,7 +570,7 @@ int sqlite3changeset_start_v2(
|
||||
** The following flags may passed via the 4th parameter to
|
||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||
**
|
||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
||||
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||
** Invert the changeset while iterating through it. This is equivalent to
|
||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||
** It is an error to specify this flag with a patchset.
|
||||
@@ -1113,13 +1115,22 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
** the changeset passed via the second and third arguments.
|
||||
**
|
||||
** The fourth argument (xFilter) passed to these functions is the "filter
|
||||
** callback". If it is not NULL, then for each table affected by at least one
|
||||
** change in the changeset, the filter callback is invoked with
|
||||
** the table name as the second argument, and a copy of the context pointer
|
||||
** passed as the sixth argument as the first. If the "filter callback"
|
||||
** returns zero, then no attempt is made to apply any changes to the table.
|
||||
** Otherwise, if the return value is non-zero or the xFilter argument to
|
||||
** is NULL, all changes related to the table are attempted.
|
||||
** callback". This may be passed NULL, in which case all changes in the
|
||||
** changeset are applied to the database. For sqlite3changeset_apply() and
|
||||
** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
|
||||
** for each table affected by at least one change in the changeset. In this
|
||||
** case the table name is passed as the second argument, and a copy of
|
||||
** the context pointer passed as the sixth argument to apply() or apply_v2()
|
||||
** as the first. If the "filter callback" returns zero, then no attempt is
|
||||
** made to apply any changes to the table. Otherwise, if the return value is
|
||||
** non-zero, all changes related to the table are attempted.
|
||||
**
|
||||
** For sqlite3_changeset_apply_v3(), the xFilter callback is invoked once
|
||||
** per change. The second argument in this case is an sqlite3_changeset_iter
|
||||
** that may be queried using the usual APIs for the details of the current
|
||||
** change. If the "filter callback" returns zero in this case, then no attempt
|
||||
** is made to apply the current change. If it returns non-zero, the change
|
||||
** is applied.
|
||||
**
|
||||
** For each table that is not excluded by the filter callback, this function
|
||||
** tests that the target database contains a compatible table. A table is
|
||||
@@ -1140,11 +1151,11 @@ void sqlite3changegroup_delete(sqlite3_changegroup*);
|
||||
** one such warning is issued for each table in the changeset.
|
||||
**
|
||||
** For each change for which there is a compatible table, an attempt is made
|
||||
** to modify the table contents according to the UPDATE, INSERT or DELETE
|
||||
** change. If a change cannot be applied cleanly, the conflict handler
|
||||
** function passed as the fifth argument to sqlite3changeset_apply() may be
|
||||
** invoked. A description of exactly when the conflict handler is invoked for
|
||||
** each type of change is below.
|
||||
** to modify the table contents according to each UPDATE, INSERT or DELETE
|
||||
** change that is not excluded by a filter callback. If a change cannot be
|
||||
** applied cleanly, the conflict handler function passed as the fifth argument
|
||||
** to sqlite3changeset_apply() may be invoked. A description of exactly when
|
||||
** the conflict handler is invoked for each type of change is below.
|
||||
**
|
||||
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
|
||||
** of passing anything other than a valid function pointer as the xConflict
|
||||
@@ -1295,6 +1306,23 @@ int sqlite3changeset_apply_v2(
|
||||
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
||||
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
||||
);
|
||||
int sqlite3changeset_apply_v3(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int nChangeset, /* Size of changeset in bytes */
|
||||
void *pChangeset, /* Changeset blob */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p /* Handle describing change */
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
||||
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags for sqlite3changeset_apply_v2
|
||||
@@ -1714,6 +1742,23 @@ int sqlite3changeset_apply_v2_strm(
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
);
|
||||
int sqlite3changeset_apply_v3_strm(
|
||||
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||
void *pIn, /* First arg for xInput */
|
||||
int(*xFilter)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
sqlite3_changeset_iter *p
|
||||
),
|
||||
int(*xConflict)(
|
||||
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||
),
|
||||
void *pCtx, /* First argument passed to xConflict */
|
||||
void **ppRebase, int *pnRebase,
|
||||
int flags
|
||||
);
|
||||
int sqlite3changeset_concat_strm(
|
||||
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
||||
void *pInA,
|
||||
|
||||
+167
-88
@@ -520,6 +520,65 @@ static int test_obj_eq_string(Tcl_Obj *p, const char *z){
|
||||
return (nObj==n && (n==0 || 0==memcmp(zObj, z, n)));
|
||||
}
|
||||
|
||||
static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
|
||||
Tcl_Obj *pVar = 0;
|
||||
int nCol; /* Number of columns in table */
|
||||
int nCol2; /* Number of columns in table */
|
||||
int op; /* SQLITE_INSERT, UPDATE or DELETE */
|
||||
const char *zTab; /* Name of table change applies to */
|
||||
Tcl_Obj *pOld; /* Vector of old.* values */
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
op==SQLITE_INSERT ? "INSERT" :
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol2);
|
||||
assert( nCol==nCol2 );
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
test_append_value(pOld, pVal);
|
||||
}
|
||||
}
|
||||
pNew = Tcl_NewObj();
|
||||
if( op!=SQLITE_DELETE ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
test_append_value(pNew, pVal);
|
||||
}
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, pOld);
|
||||
Tcl_ListObjAppendElement(0, pVar, pNew);
|
||||
|
||||
return pVar;
|
||||
}
|
||||
|
||||
|
||||
static int test_filter_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
const char *zTab /* Table name */
|
||||
@@ -543,6 +602,29 @@ static int test_filter_handler(
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_filter_v3_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
sqlite3_changeset_iter *pIter
|
||||
){
|
||||
TestConflictHandler *p = (TestConflictHandler *)pCtx;
|
||||
int res = 1;
|
||||
Tcl_Obj *pEval = 0;
|
||||
Tcl_Interp *interp = p->interp;
|
||||
|
||||
pEval = Tcl_DuplicateObj(p->pFilterScript);
|
||||
Tcl_IncrRefCount(pEval);
|
||||
Tcl_ListObjAppendElement(0, pEval, testIterData(pIter));
|
||||
|
||||
if( TCL_OK!=Tcl_EvalObjEx(interp, pEval, TCL_EVAL_GLOBAL)
|
||||
|| TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &res)
|
||||
){
|
||||
Tcl_BackgroundError(interp);
|
||||
}
|
||||
|
||||
Tcl_DecrRefCount(pEval);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int test_conflict_handler(
|
||||
void *pCtx, /* Pointer to TestConflictHandler structure */
|
||||
int eConf, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||
@@ -776,7 +858,7 @@ static int testStreamInput(
|
||||
|
||||
|
||||
static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
int bV2,
|
||||
int iVersion,
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
@@ -793,11 +875,13 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
int nRebase = 0;
|
||||
int flags = 0; /* Flags for apply_v2() */
|
||||
|
||||
assert( iVersion==1 || iVersion==2 || iVersion==3 );
|
||||
|
||||
memset(&sStr, 0, sizeof(sStr));
|
||||
sStr.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
|
||||
/* Check for the -nosavepoint, -invert or -ignorenoop switches */
|
||||
if( bV2 ){
|
||||
if( iVersion==2 || iVersion==3 ){
|
||||
while( objc>1 ){
|
||||
const char *z1 = Tcl_GetString(objv[1]);
|
||||
int n = (int)strlen(z1);
|
||||
@@ -822,7 +906,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
const char *zMsg;
|
||||
if( bV2 ){
|
||||
if( iVersion==2 || iVersion==3 ){
|
||||
zMsg = "?-nosavepoint? ?-inverse? ?-ignorenoop? "
|
||||
"DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?";
|
||||
}else{
|
||||
@@ -842,30 +926,49 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
ctx.interp = interp;
|
||||
|
||||
if( sStr.nStream==0 ){
|
||||
if( bV2==0 ){
|
||||
rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
}else{
|
||||
rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
switch( iVersion ){
|
||||
case 1:
|
||||
rc = sqlite3changeset_apply(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void*)&ctx
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
rc = sqlite3changeset_apply_v2(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_handler:0, test_conflict_handler, (void*)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
case 3:
|
||||
rc = sqlite3changeset_apply_v3(db, (int)nChangeset, pChangeset,
|
||||
(objc==5)?test_filter_v3_handler:0, test_conflict_handler,
|
||||
(void*)&ctx, &pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
sStr.aData = (unsigned char*)pChangeset;
|
||||
sStr.nData = (int)nChangeset;
|
||||
if( bV2==0 ){
|
||||
rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
}else{
|
||||
rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
switch( iVersion ){
|
||||
case 1:
|
||||
rc = sqlite3changeset_apply_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
rc = sqlite3changeset_apply_v2_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
case 3:
|
||||
rc = sqlite3changeset_apply_v3_strm(db, testStreamInput, (void*)&sStr,
|
||||
(objc==5) ? test_filter_v3_handler : 0,
|
||||
test_conflict_handler, (void *)&ctx,
|
||||
&pRebase, &nRebase, flags
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,7 +976,7 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
return test_session_error(interp, rc, 0);
|
||||
}else{
|
||||
Tcl_ResetResult(interp);
|
||||
if( bV2 && pRebase ){
|
||||
if( (iVersion==2 || iVersion==3) && pRebase ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pRebase, nRebase));
|
||||
}
|
||||
}
|
||||
@@ -890,7 +993,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(0, clientData, interp, objc, objv);
|
||||
return testSqlite3changesetApply(1, clientData, interp, objc, objv);
|
||||
}
|
||||
/*
|
||||
** sqlite3changeset_apply_v2 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
@@ -901,7 +1004,18 @@ static int SQLITE_TCLAPI test_sqlite3changeset_apply_v2(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(1, clientData, interp, objc, objv);
|
||||
return testSqlite3changesetApply(2, clientData, interp, objc, objv);
|
||||
}
|
||||
/*
|
||||
** sqlite3changeset_apply_v3 DB CHANGESET CONFLICT-SCRIPT ?FILTER-SCRIPT?
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_sqlite3changeset_apply_v3(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
return testSqlite3changesetApply(3, clientData, interp, objc, objv);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1034,64 +1148,6 @@ static int SQLITE_TCLAPI test_sqlite3changeset_concat(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Tcl_Obj *testIterData(sqlite3_changeset_iter *pIter){
|
||||
Tcl_Obj *pVar = 0;
|
||||
int nCol; /* Number of columns in table */
|
||||
int nCol2; /* Number of columns in table */
|
||||
int op; /* SQLITE_INSERT, UPDATE or DELETE */
|
||||
const char *zTab; /* Name of table change applies to */
|
||||
Tcl_Obj *pOld; /* Vector of old.* values */
|
||||
Tcl_Obj *pNew; /* Vector of new.* values */
|
||||
int bIndirect;
|
||||
|
||||
char *zPK;
|
||||
unsigned char *abPK;
|
||||
int i;
|
||||
|
||||
sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
|
||||
pVar = Tcl_NewObj();
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(
|
||||
op==SQLITE_INSERT ? "INSERT" :
|
||||
op==SQLITE_UPDATE ? "UPDATE" :
|
||||
"DELETE", -1
|
||||
));
|
||||
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zTab, -1));
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewBooleanObj(bIndirect));
|
||||
|
||||
zPK = ckalloc(nCol+1);
|
||||
memset(zPK, 0, nCol+1);
|
||||
sqlite3changeset_pk(pIter, &abPK, &nCol2);
|
||||
assert( nCol==nCol2 );
|
||||
for(i=0; i<nCol; i++){
|
||||
zPK[i] = (abPK[i] ? 'X' : '.');
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, Tcl_NewStringObj(zPK, -1));
|
||||
ckfree(zPK);
|
||||
|
||||
pOld = Tcl_NewObj();
|
||||
if( op!=SQLITE_INSERT ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_old(pIter, i, &pVal);
|
||||
test_append_value(pOld, pVal);
|
||||
}
|
||||
}
|
||||
pNew = Tcl_NewObj();
|
||||
if( op!=SQLITE_DELETE ){
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_value *pVal;
|
||||
sqlite3changeset_new(pIter, i, &pVal);
|
||||
test_append_value(pNew, pVal);
|
||||
}
|
||||
}
|
||||
Tcl_ListObjAppendElement(0, pVar, pOld);
|
||||
Tcl_ListObjAppendElement(0, pVar, pNew);
|
||||
|
||||
return pVar;
|
||||
}
|
||||
|
||||
/*
|
||||
** sqlite3session_foreach VARNAME CHANGESET SCRIPT
|
||||
*/
|
||||
@@ -1459,6 +1515,9 @@ struct TestChangegroup {
|
||||
typedef struct TestChangeIter TestChangeIter;
|
||||
struct TestChangeIter {
|
||||
sqlite3_changeset_iter *pIter;
|
||||
|
||||
/* If this iter uses streaming. */
|
||||
TestStreamInput in;
|
||||
};
|
||||
|
||||
|
||||
@@ -1681,6 +1740,7 @@ static int SQLITE_TCLAPI test_sqlite3changeset_start(
|
||||
sqlite3_changeset_iter *pIter = 0;
|
||||
int flags = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int nAlloc = 0; /* Bytes of space to allocate */
|
||||
|
||||
static int iCmd = 1;
|
||||
char zCmd[64];
|
||||
@@ -1696,18 +1756,36 @@ static int SQLITE_TCLAPI test_sqlite3changeset_start(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
|
||||
pChangeset = (void *)Tcl_GetByteArrayFromObj(objv[objc-1], &nChangeset);
|
||||
rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, flags);
|
||||
flags = isInvert ? SQLITE_CHANGESETSTART_INVERT : 0;
|
||||
|
||||
nAlloc = sizeof(TestChangeIter);
|
||||
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
|
||||
nAlloc += nChangeset;
|
||||
}
|
||||
pNew = (TestChangeIter*)ckalloc(nAlloc);
|
||||
memset(pNew, 0, nAlloc);
|
||||
if( test_tcl_integer(interp, SESSION_STREAM_TCL_VAR) ){
|
||||
pNew->in.nStream = test_tcl_integer(interp, SESSION_STREAM_TCL_VAR);
|
||||
pNew->in.nData = nChangeset;
|
||||
pNew->in.aData = (unsigned char*)&pNew[1];
|
||||
memcpy(pNew->in.aData, pChangeset, nChangeset);
|
||||
}
|
||||
|
||||
if( pNew->in.nStream ){
|
||||
void *pCtx = (void*)&pNew->in;
|
||||
rc = sqlite3changeset_start_v2_strm(&pIter, testStreamInput, pCtx, flags);
|
||||
}else{
|
||||
rc = sqlite3changeset_start_v2(&pIter, (int)nChangeset, pChangeset, flags);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
char *zErr = sqlite3_mprintf(
|
||||
"error in sqlite3changeset_start_v2() - %d", rc
|
||||
);
|
||||
Tcl_AppendResult(interp, zErr, (char*)0);
|
||||
ckfree(pNew);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
pNew = (TestChangeIter*)ckalloc(sizeof(TestChangeIter));
|
||||
pNew->pIter = pIter;
|
||||
|
||||
sprintf(zCmd, "csiter%d", iCmd++);
|
||||
@@ -1729,6 +1807,7 @@ int TestSession_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3changeset_concat", test_sqlite3changeset_concat },
|
||||
{ "sqlite3changeset_apply", test_sqlite3changeset_apply },
|
||||
{ "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 },
|
||||
{ "sqlite3changeset_apply_v3", test_sqlite3changeset_apply_v3 },
|
||||
{ "sqlite3changeset_apply_replace_all",
|
||||
test_sqlite3changeset_apply_replace_all },
|
||||
{ "sql_exec_changeset", test_sql_exec_changeset },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user