Compare commits
320 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c997c6e75 | |||
| 5587d86ddd | |||
| d43748aa98 | |||
| 3fb406083a | |||
| 0c2433003d | |||
| 7ccf95dbab | |||
| 980a89e630 | |||
| 971ecd7eaa | |||
| ec360a8d01 | |||
| 47ebd4cadd | |||
| f0f44b79c9 | |||
| 1cd523cf0f | |||
| 416b8694fa | |||
| 8999798e60 | |||
| ba334bb20d | |||
| db3e0456d2 | |||
| b7bfc3b12d | |||
| 36c68234d6 | |||
| 1be5051923 | |||
| 56eb09bc23 | |||
| 2ce15c3f4c | |||
| e2b01012bc | |||
| f3c0009d8b | |||
| dc4dde6bd9 | |||
| 3265b5649e | |||
| b2950c48e1 | |||
| 4a9e14077e | |||
| 240e7ef4bb | |||
| a868639656 | |||
| e1869a91d6 | |||
| 764fb30330 | |||
| 67d5bf9118 | |||
| 14d5ff4202 | |||
| 91ea8cf4ac | |||
| 7ff64dc2bc | |||
| ff7733db52 | |||
| dbc26722cd | |||
| 90e39ec349 | |||
| 66a0bf31cc | |||
| e24b92bce7 | |||
| d3930b1266 | |||
| 8674ec5a7f | |||
| ebc63013b7 | |||
| 9f6e14c1ef | |||
| ff4fa772f5 | |||
| e75be1aedb | |||
| 155507b344 | |||
| d7ca600ede | |||
| 2aa31333c2 | |||
| 12ca0fc259 | |||
| 50963c9da8 | |||
| 85bd1cd52b | |||
| 1d91e9f26a | |||
| 08f1efeb16 | |||
| a690ff360b | |||
| b11c650c01 | |||
| 73518c5876 | |||
| a2d85cbbfa | |||
| 9a7a77ad36 | |||
| a7a0bfab91 | |||
| 9b8c5f6819 | |||
| d5eae667f4 | |||
| 8ae11aac93 | |||
| e60092b90c | |||
| baebf3cc4b | |||
| 45158fa5d9 | |||
| 3bc909b0af | |||
| 5bd9e61f05 | |||
| d429ec222c | |||
| 40cf27cb63 | |||
| ab53bb635c | |||
| 4676462f92 | |||
| d10866798d | |||
| c2d14a9a33 | |||
| b98a2e351e | |||
| 3da70a6112 | |||
| 0db5bfe5de | |||
| a0c3927510 | |||
| 9d8e401c2d | |||
| ebaaa67db2 | |||
| 3b02e0f615 | |||
| 666e86625c | |||
| 07a3b11a50 | |||
| 169a689f7b | |||
| a35d851892 | |||
| adea0d1c39 | |||
| 7181395a4d | |||
| 269ba80469 | |||
| 431a53fde2 | |||
| 038af0bdc6 | |||
| 92395c59ff | |||
| 518ee8f06f | |||
| 45408a108f | |||
| cde336e0dc | |||
| 05dda7ba8e | |||
| 24f6904e99 | |||
| c7b1ee5f6e | |||
| 3561dd4afe | |||
| 3a96a5d919 | |||
| fd76b71cac | |||
| 04222ce52e | |||
| 88f769f967 | |||
| a5b55e370f | |||
| a501f7dab4 | |||
| c31b79d2a4 | |||
| e755072c3b | |||
| d681d3472d | |||
| 28508ddb42 | |||
| 97e8b3ea32 | |||
| 1d1982c324 | |||
| de680efbc3 | |||
| c2586f77ad | |||
| 01e6d540a7 | |||
| 14e61df283 | |||
| c6603af755 | |||
| 3881d23c72 | |||
| c79983312f | |||
| 2ca62cb102 | |||
| e4b45f536e | |||
| 366050e89a | |||
| 3039e3698d | |||
| d90dbb8c1c | |||
| 00d11d400b | |||
| 5aa307e2a6 | |||
| cffbd543d3 | |||
| c080422642 | |||
| 44ab70db7e | |||
| 3e380a4485 | |||
| 37f906087b | |||
| d0bbe4e9c2 | |||
| f00f530b7d | |||
| 2fb960b545 | |||
| 8b471e7e79 | |||
| 6559e2cec7 | |||
| 6be95366c7 | |||
| bd8fcc130a | |||
| 6e11b16282 | |||
| 59a386ea03 | |||
| 3144df1264 | |||
| a5e906f307 | |||
| ec99c0f632 | |||
| b5fdc26c21 | |||
| 59b1dd3830 | |||
| 210ec4c855 | |||
| 8e1ddde079 | |||
| 02679b0466 | |||
| af38cdbc08 | |||
| d5fbde80a2 | |||
| 7df7475d0d | |||
| 169dd928c5 | |||
| 4261a4065f | |||
| 3a31c67aae | |||
| e6f98bcf09 | |||
| 07cae6ae74 | |||
| a22dd3860a | |||
| 5aa550cf3b | |||
| 7f2d1cd2b0 | |||
| 54ac445d53 | |||
| 45e7d7dd1c | |||
| 65c2a71042 | |||
| c456a76fb3 | |||
| c45fdb2a7f | |||
| f383f35e6c | |||
| bd11a2acbb | |||
| a5907a86b8 | |||
| 40db2fd7a3 | |||
| ea03adc6b2 | |||
| 814aad618c | |||
| f417f28bd7 | |||
| da1f49b83f | |||
| ba87c6c2cd | |||
| 76cf858d3d | |||
| 0942559a14 | |||
| f217f3b68c | |||
| b74013300c | |||
| 90cdec0672 | |||
| 594ccd09b1 | |||
| 20c9c3f4eb | |||
| c74d962a1f | |||
| 2ebf8f474e | |||
| 6e3bccd5c9 | |||
| 56790ea567 | |||
| eafc6dfe2b | |||
| e46c63d565 | |||
| c62aab5ba8 | |||
| e9acf4b48e | |||
| c097b309aa | |||
| c5d353fb33 | |||
| d96cc6fbcb | |||
| ca66f6c6f4 | |||
| cc97ca4c08 | |||
| 41ac439dbe | |||
| 0282c03abc | |||
| d1417ee1cb | |||
| 3b328522f4 | |||
| cf1393df94 | |||
| b1d66c5986 | |||
| a4327e65bb | |||
| 64bf76d2b2 | |||
| 3b77506ba5 | |||
| 4c88348729 | |||
| 5930f663a1 | |||
| 629a75f69a | |||
| 1346fdad65 | |||
| 16d62901a3 | |||
| 59ce97a013 | |||
| f151890791 | |||
| a2e71ced79 | |||
| 170ad68a40 | |||
| 6c2d94f928 | |||
| 38d6985509 | |||
| c5e56b3473 | |||
| 2c2f392dca | |||
| cdbb126be7 | |||
| 3528f6b62b | |||
| 6b37e0adc9 | |||
| d4ab003d4e | |||
| d5bdd5effb | |||
| b8a1290a28 | |||
| 02f18cc5ac | |||
| a8b9793c86 | |||
| d03257c141 | |||
| 2ab792e4c0 | |||
| d26d2c7de2 | |||
| e70d01f143 | |||
| 1e669f02bc | |||
| 8c9ed054b3 | |||
| 77f6af2b96 | |||
| 5f5a2d1c3c | |||
| 8abc54e2e8 | |||
| 979dd1be57 | |||
| d8a295669c | |||
| e16a350f17 | |||
| ae2ac854b3 | |||
| 14e845a9d4 | |||
| 137c46f8b6 | |||
| 65ff63ea62 | |||
| dc6de47925 | |||
| 074ce1e7fd | |||
| 8389bff0a8 | |||
| f43ce0b445 | |||
| 831265f962 | |||
| 6fb139e087 | |||
| 63ad86e70e | |||
| ce13b99f99 | |||
| 92b71757f7 | |||
| 3c790f2a83 | |||
| 1d1fc5e385 | |||
| 7c1544e051 | |||
| eff0a7b25e | |||
| f352cf57e3 | |||
| 44bdc92938 | |||
| 10d33c1781 | |||
| 3f1e9e00e6 | |||
| d4fb196531 | |||
| 07d0f15e93 | |||
| 453ca043a2 | |||
| ee841262d9 | |||
| d758722449 | |||
| 2435fa6916 | |||
| 44ec1d6eda | |||
| 21766c0c16 | |||
| 78436d4c66 | |||
| d8025e43ae | |||
| e46515b53f | |||
| bcbb066534 | |||
| 748465b49e | |||
| 8c3bde05ce | |||
| 22025ef198 | |||
| 1e1c4226ee | |||
| 9ea258d712 | |||
| dd1bb43ab5 | |||
| 96a27f4cbb | |||
| c8d3e836ca | |||
| ee3333b202 | |||
| 9715f0e544 | |||
| 6b4fbabd6a | |||
| 3fb153cb29 | |||
| f7fa4e71f4 | |||
| 9418921c59 | |||
| ee92eb80db | |||
| 2336c935af | |||
| e694139788 | |||
| 0d236a8bb0 | |||
| 701caf1eb1 | |||
| 7b458519f2 | |||
| e4eb0497d8 | |||
| 614c6a09f7 | |||
| 2617c9bdd8 | |||
| ffe280596e | |||
| f8a2e8c2d2 | |||
| 050b124ce2 | |||
| 30f01249a3 | |||
| 313e6fd222 | |||
| ad012d09dc | |||
| e780764b4e | |||
| 3f4df4c6f3 | |||
| a1f5c0c90b | |||
| ab314001b8 | |||
| 8d5cee166e | |||
| c478c8daec | |||
| 4b2667c8b8 | |||
| 874080bbb6 | |||
| ed71298085 | |||
| 33543c237b | |||
| e08e8d6b61 | |||
| 181a1167dc | |||
| ab31a8450b | |||
| 7555581fc1 | |||
| bb6783b86f | |||
| bc43995f99 | |||
| 16b7dcc717 | |||
| 6627fd6126 | |||
| 078b99f806 | |||
| 7dc1918bbf | |||
| 99a1ab7ebc | |||
| c83ef77bb9 | |||
| a87cc18948 | |||
| eb8e7adb16 | |||
| a0b7ffb184 |
+8
-2
@@ -181,7 +181,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
|
||||
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
|
||||
pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
|
||||
random.lo resolve.lo rowset.lo rtree.lo \
|
||||
sqlite3session.lo select.lo sqlite3rbu.lo status.lo \
|
||||
sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmt.lo \
|
||||
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
|
||||
update.lo util.lo vacuum.lo \
|
||||
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
|
||||
@@ -350,7 +350,8 @@ SRC += \
|
||||
$(TOP)/ext/rbu/sqlite3rbu.h \
|
||||
$(TOP)/ext/rbu/sqlite3rbu.c
|
||||
SRC += \
|
||||
$(TOP)/ext/misc/json1.c
|
||||
$(TOP)/ext/misc/json1.c \
|
||||
$(TOP)/ext/misc/stmt.c
|
||||
|
||||
# Generated source code files
|
||||
#
|
||||
@@ -564,6 +565,7 @@ SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4
|
||||
# SHELL_OPT += -DSQLITE_ENABLE_FTS5
|
||||
SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
|
||||
FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
|
||||
@@ -1034,6 +1036,9 @@ sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR)
|
||||
json1.lo: $(TOP)/ext/misc/json1.c
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c
|
||||
|
||||
stmt.lo: $(TOP)/ext/misc/stmt.c
|
||||
$(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c
|
||||
|
||||
# FTS5 things
|
||||
#
|
||||
FTS5_SRC = \
|
||||
@@ -1083,6 +1088,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
|
||||
TESTFIXTURE_FLAGS += -DBUILD_sqlite
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
|
||||
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
|
||||
TESTFIXTURE_SRC1 = sqlite3.c
|
||||
|
||||
+9
-3
@@ -1294,7 +1294,8 @@ SRC07 = \
|
||||
$(TOP)\ext\rtree\rtree.c \
|
||||
$(TOP)\ext\session\sqlite3session.c \
|
||||
$(TOP)\ext\rbu\sqlite3rbu.c \
|
||||
$(TOP)\ext\misc\json1.c
|
||||
$(TOP)\ext\misc\json1.c \
|
||||
$(TOP)\ext\misc\stmt.c
|
||||
|
||||
# Extension header files, part 1.
|
||||
#
|
||||
@@ -1505,7 +1506,7 @@ FUZZDATA = \
|
||||
# when the shell is not being dynamically linked.
|
||||
#
|
||||
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
SHELL_COMPILE_OPTS = $(SHELL_COMPILE_OPTS) -DSQLITE_SHELL_JSON1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_STMTVTAB
|
||||
!ENDIF
|
||||
|
||||
# <<mark>>
|
||||
@@ -1562,7 +1563,7 @@ $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
|
||||
sqlite3.def: libsqlite3.lib
|
||||
echo EXPORTS > sqlite3.def
|
||||
dumpbin /all libsqlite3.lib \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset)?_[^@]*)(?:@\d+)?$$" \1 \
|
||||
| $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup)?_[^@]*)(?:@\d+)?$$" \1 \
|
||||
| sort >> sqlite3.def
|
||||
# <</block2>>
|
||||
|
||||
@@ -2089,6 +2090,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB
|
||||
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
|
||||
|
||||
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
|
||||
@@ -2242,6 +2244,9 @@ rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H
|
||||
$(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \
|
||||
$(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
|
||||
|
||||
LSMDIR=$(TOP)\ext\lsm1
|
||||
!INCLUDE $(LSMDIR)\Makefile.msc
|
||||
|
||||
moreclean: clean
|
||||
del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL
|
||||
# <</mark>>
|
||||
@@ -2261,6 +2266,7 @@ clean:
|
||||
-rmdir /Q/S tsrc 2>NUL
|
||||
del /Q .target_source 2>NUL
|
||||
del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 2>NUL
|
||||
del /Q lsm.dll lsmtest.exe 2>NUL
|
||||
del /Q testloadext.dll 2>NUL
|
||||
del /Q testfixture.exe test.db 2>NUL
|
||||
del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL
|
||||
|
||||
@@ -34,7 +34,9 @@ archives as follows:
|
||||
If you do want to use Fossil to check out the source tree,
|
||||
first install Fossil version 2.0 or later.
|
||||
(Source tarballs and precompiled binaries available
|
||||
[here](https://www.fossil-scm.org/fossil/uv/download.html).)
|
||||
[here](https://www.fossil-scm.org/fossil/uv/download.html). Fossil is
|
||||
a stand-alone program. To install, simply download or build the single
|
||||
executable file and put that file someplace on your $PATH.)
|
||||
Then run commands like this:
|
||||
|
||||
mkdir ~/sqlite
|
||||
@@ -106,19 +108,22 @@ The makefiles also require AWK.
|
||||
|
||||
## Source Code Tour
|
||||
|
||||
Most of the core source files are in the **src/** subdirectory. But
|
||||
src/ also contains files used to build the "testfixture" test harness;
|
||||
those file all begin with "test". And src/ contains the "shell.c" file
|
||||
which is the main program for the "sqlite3.exe" command-line shell and
|
||||
the "tclsqlite.c" file which implements the bindings to SQLite from the
|
||||
Tcl programming language. (Historical note: SQLite began as a Tcl
|
||||
Most of the core source files are in the **src/** subdirectory. The
|
||||
**src/** folder also contains files used to build the "testfixture" test
|
||||
harness. The names of the source files used by "testfixture" all begin
|
||||
with "test".
|
||||
The **src/** also contains the "shell.c" file
|
||||
which is the main program for the "sqlite3.exe"
|
||||
[command-line shell](https://sqlite.org/cli.html) and
|
||||
the "tclsqlite.c" file which implements the
|
||||
[TCL bindings](https://sqlite.org/tclsqlite.html) for SQLite.
|
||||
(Historical note: SQLite began as a Tcl
|
||||
extension and only later escaped to the wild as an independent library.)
|
||||
|
||||
Test scripts and programs are found in the **test/** subdirectory.
|
||||
There are other test suites for SQLite (see
|
||||
[How SQLite Is Tested](http://www.sqlite.org/testing.html))
|
||||
but those other test suites are
|
||||
in separate source repositories.
|
||||
Addtional test code is found in other source repositories.
|
||||
See [How SQLite Is Tested](http://www.sqlite.org/testing.html) for
|
||||
additional information.
|
||||
|
||||
The **ext/** subdirectory contains code for extensions. The
|
||||
Full-text search engine is in **ext/fts3**. The R-Tree engine is in
|
||||
@@ -142,7 +147,7 @@ manually-edited files and automatically-generated files.
|
||||
The SQLite interface is defined by the **sqlite3.h** header file, which is
|
||||
generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The
|
||||
[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion.
|
||||
The manifest.uuid file contains the SHA1 hash of the particular check-in
|
||||
The manifest.uuid file contains the SHA3 hash of the particular check-in
|
||||
and is used to generate the SQLITE\_SOURCE\_ID macro. The VERSION file
|
||||
contains the current SQLite version number. The sqlite3.h header is really
|
||||
just a copy of src/sqlite.h.in with the source-id and version number inserted
|
||||
@@ -153,9 +158,8 @@ used to generate that documentation are in a separate source repository.
|
||||
The SQL language parser is **parse.c** which is generate from a grammar in
|
||||
the src/parse.y file. The conversion of "parse.y" into "parse.c" is done
|
||||
by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code
|
||||
for lemon is at tool/lemon.c. Lemon uses a
|
||||
template for generating its parser. A generic template is in tool/lempar.c,
|
||||
but SQLite uses a slightly modified template found in src/lempar.c.
|
||||
for lemon is at tool/lemon.c. Lemon uses the tool/lempar.c file as a
|
||||
template for generating its parser.
|
||||
|
||||
Lemon also generates the **parse.h** header file, at the same time it
|
||||
generates parse.c. But the parse.h header file is
|
||||
@@ -175,6 +179,13 @@ that maps SQL language keywords (ex: "CREATE", "SELECT", "INDEX", etc.) into
|
||||
the numeric codes used by the parse.c parser. The keywordhash.h file is
|
||||
generated by a C-language program at tool mkkeywordhash.c.
|
||||
|
||||
The **pragma.h** header file contains various definitions used to parse
|
||||
and implement the PRAGMA statements. The header is generated by a
|
||||
script **tool/mkpragmatab.tcl**. If you want to add a new PRAGMA, edit
|
||||
the **tool/mkpragmatab.tcl** file to insert the information needed by the
|
||||
parser for your new PRAGMA, then run the script to regenerate the
|
||||
**pragma.h** header file.
|
||||
|
||||
### The Amalgamation
|
||||
|
||||
All of the individual C source code and header files (both manually-edited
|
||||
@@ -192,7 +203,7 @@ subdirectory (using the equivalent of "make target_source") then the
|
||||
tool/mksqlite3c.tcl script is run to copy them all together in just the
|
||||
right order while resolving internal "#include" references.
|
||||
|
||||
The amalgamation source file is more than 100K lines long. Some symbolic
|
||||
The amalgamation source file is more than 200K lines long. Some symbolic
|
||||
debuggers (most notably MSVC) are unable to deal with files longer than 64K
|
||||
lines. To work around this, a separate Tcl script, tool/split-sqlite3c.tcl,
|
||||
can be run on the amalgamation to break it up into a single small C file
|
||||
@@ -209,14 +220,15 @@ See the [architectural description](http://www.sqlite.org/arch.html)
|
||||
for details. Other documents that are useful in
|
||||
(helping to understand how SQLite works include the
|
||||
[file format](http://www.sqlite.org/fileformat2.html) description,
|
||||
the [virtual machine](http://www.sqlite.org/vdbe.html) that runs
|
||||
the [virtual machine](http://www.sqlite.org/opcode.html) that runs
|
||||
prepared statements, the description of
|
||||
[how transactions work](http://www.sqlite.org/atomiccommit.html), and
|
||||
the [overview of the query planner](http://www.sqlite.org/optoverview.html).
|
||||
|
||||
Unfortunately, years of effort have gone into optimizating SQLite, both
|
||||
Years of effort have gone into optimizating SQLite, both
|
||||
for small size and high performance. And optimizations tend to result in
|
||||
complex code. So there is a lot of complexity in the SQLite implementation.
|
||||
complex code. So there is a lot of complexity in the current SQLite
|
||||
implementation. It will not be the easiest library in the world to hack.
|
||||
|
||||
Key files:
|
||||
|
||||
@@ -227,7 +239,7 @@ Key files:
|
||||
* **sqliteInt.h** - this header file defines many of the data objects
|
||||
used internally by SQLite.
|
||||
|
||||
* **parse.y** - This file describes the LALR(1) grammer that SQLite uses
|
||||
* **parse.y** - This file describes the LALR(1) grammar that SQLite uses
|
||||
to parse SQL statements, and the actions that are taken at each step
|
||||
in the parsing process.
|
||||
|
||||
@@ -260,13 +272,13 @@ Key files:
|
||||
is not part of the core SQLite library. But as most of the tests in this
|
||||
repository are written in Tcl, the Tcl language bindings are important.
|
||||
|
||||
There are many other source files. Each has a suscinct header comment that
|
||||
There are many other source files. Each has a succinct header comment that
|
||||
describes its purpose and role within the larger system.
|
||||
|
||||
|
||||
## Contacts
|
||||
|
||||
The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/)
|
||||
with geographically distributed backup servers at
|
||||
with geographically distributed backups at
|
||||
[http://www2.sqlite.org/](http://www2.sqlite.org) and
|
||||
[http://www3.sqlite.org/](http://www3.sqlite.org).
|
||||
|
||||
@@ -21,7 +21,7 @@ TOP = .
|
||||
# Set this non-0 to enable full warnings (-W4, etc) when compiling.
|
||||
#
|
||||
!IFNDEF USE_FULLWARN
|
||||
USE_FULLWARN = 0
|
||||
USE_FULLWARN = 1
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable treating warnings as errors (-WX, etc) when
|
||||
@@ -954,7 +954,7 @@ Replace.exe:
|
||||
sqlite3.def: Replace.exe $(LIBOBJ)
|
||||
echo EXPORTS > sqlite3.def
|
||||
dumpbin /all $(LIBOBJ) \
|
||||
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
||||
| .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
|
||||
| sort >> sqlite3.def
|
||||
|
||||
$(SQLITE3EXE): $(TOP)\shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.19.0.
|
||||
# Generated by GNU Autoconf 2.69 for sqlite 3.20.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
|
||||
@@ -726,8 +726,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.19.0'
|
||||
PACKAGE_STRING='sqlite 3.19.0'
|
||||
PACKAGE_VERSION='3.20.0'
|
||||
PACKAGE_STRING='sqlite 3.20.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -1463,7 +1463,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures sqlite 3.19.0 to adapt to many kinds of systems.
|
||||
\`configure' configures sqlite 3.20.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1528,7 +1528,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.19.0:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.20.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1652,7 +1652,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.19.0
|
||||
sqlite configure 3.20.0
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2071,7 +2071,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by sqlite $as_me 3.19.0, which was
|
||||
It was created by sqlite $as_me 3.20.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -11356,7 +11356,7 @@ fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5
|
||||
$as_echo_n "checking whether to support MEMSYS5... " >&6; }
|
||||
if test "${enable_memsys5}" = "yes"; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MEMSYS5"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS5"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
@@ -11373,7 +11373,7 @@ fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5
|
||||
$as_echo_n "checking whether to support MEMSYS3... " >&6; }
|
||||
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MEMSYS3"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS3"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
else
|
||||
@@ -11391,7 +11391,7 @@ else
|
||||
fi
|
||||
|
||||
if test "${enable_fts3}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3"
|
||||
fi
|
||||
# Check whether --enable-fts4 was given.
|
||||
if test "${enable_fts4+set}" = set; then :
|
||||
@@ -11401,7 +11401,7 @@ else
|
||||
fi
|
||||
|
||||
if test "${enable_fts4}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS4"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
|
||||
$as_echo_n "checking for library containing log... " >&6; }
|
||||
if ${ac_cv_search_log+:} false; then :
|
||||
@@ -11467,7 +11467,7 @@ else
|
||||
fi
|
||||
|
||||
if test "${enable_fts5}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
|
||||
$as_echo_n "checking for library containing log... " >&6; }
|
||||
if ${ac_cv_search_log+:} false; then :
|
||||
@@ -11536,7 +11536,7 @@ else
|
||||
fi
|
||||
|
||||
if test "${enable_json1}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1"
|
||||
fi
|
||||
|
||||
#########
|
||||
@@ -11549,7 +11549,7 @@ else
|
||||
fi
|
||||
|
||||
if test "${enable_rtree}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE"
|
||||
fi
|
||||
|
||||
#########
|
||||
@@ -11562,12 +11562,12 @@ else
|
||||
fi
|
||||
|
||||
if test "${enable_session}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION"
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
fi
|
||||
|
||||
#########
|
||||
# attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter
|
||||
# attempt to duplicate any OMITS and ENABLES into the ${OPT_FEATURE_FLAGS} parameter
|
||||
for option in $CFLAGS $CPPFLAGS
|
||||
do
|
||||
case $option in
|
||||
@@ -12151,7 +12151,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by sqlite $as_me 3.19.0, which was
|
||||
This file was extended by sqlite $as_me 3.20.0, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -12217,7 +12217,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.19.0
|
||||
sqlite config.status 3.20.0
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+10
-10
@@ -596,7 +596,7 @@ AC_ARG_ENABLE(memsys5,
|
||||
[enable_memsys5=yes],[enable_memsys5=no])
|
||||
AC_MSG_CHECKING([whether to support MEMSYS5])
|
||||
if test "${enable_memsys5}" = "yes"; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MEMSYS5"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS5"
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
@@ -606,7 +606,7 @@ AC_ARG_ENABLE(memsys3,
|
||||
[enable_memsys3=yes],[enable_memsys3=no])
|
||||
AC_MSG_CHECKING([whether to support MEMSYS3])
|
||||
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_MEMSYS3"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_MEMSYS3"
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
@@ -618,20 +618,20 @@ AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3],
|
||||
[Enable the FTS3 extension]),
|
||||
[enable_fts3=yes],[enable_fts3=no])
|
||||
if test "${enable_fts3}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS3"
|
||||
fi
|
||||
AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4],
|
||||
[Enable the FTS4 extension]),
|
||||
[enable_fts4=yes],[enable_fts4=no])
|
||||
if test "${enable_fts4}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS4"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS4"
|
||||
AC_SEARCH_LIBS([log],[m])
|
||||
fi
|
||||
AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5],
|
||||
[Enable the FTS5 extension]),
|
||||
[enable_fts5=yes],[enable_fts5=no])
|
||||
if test "${enable_fts5}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS5"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_FTS5"
|
||||
AC_SEARCH_LIBS([log],[m])
|
||||
fi
|
||||
|
||||
@@ -641,7 +641,7 @@ AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1],
|
||||
[Enable the JSON1 extension]),
|
||||
[enable_json1=yes],[enable_json1=no])
|
||||
if test "${enable_json1}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_JSON1"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_JSON1"
|
||||
fi
|
||||
|
||||
#########
|
||||
@@ -650,7 +650,7 @@ AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree],
|
||||
[Enable the RTREE extension]),
|
||||
[enable_rtree=yes],[enable_rtree=no])
|
||||
if test "${enable_rtree}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_RTREE"
|
||||
fi
|
||||
|
||||
#########
|
||||
@@ -659,12 +659,12 @@ AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session],
|
||||
[Enable the SESSION extension]),
|
||||
[enable_session=yes],[enable_session=no])
|
||||
if test "${enable_session}" = "yes" ; then
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION"
|
||||
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_SESSION"
|
||||
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_PREUPDATE_HOOK"
|
||||
fi
|
||||
|
||||
#########
|
||||
# attempt to duplicate any OMITS and ENABLES into the $(OPT_FEATURE_FLAGS) parameter
|
||||
# attempt to duplicate any OMITS and ENABLES into the ${OPT_FEATURE_FLAGS} parameter
|
||||
for option in $CFLAGS $CPPFLAGS
|
||||
do
|
||||
case $option in
|
||||
|
||||
@@ -23,6 +23,26 @@ or embedded controllers.</p>
|
||||
<p>This document is an introduction to the Lemon
|
||||
parser generator.</p>
|
||||
|
||||
<h2>Security Note</h2>
|
||||
|
||||
<p>The language parser code created by Lemon is very robust and
|
||||
is well-suited for use in internet-facing applications that need to
|
||||
safely process maliciously crafted inputs.
|
||||
|
||||
<p>The "lemon.exe" command-line tool itself works great when given a valid
|
||||
input grammar file and almost always gives helpful
|
||||
error messages for malformed inputs. However, it is possible for
|
||||
a malicious user to craft a grammar file that will cause
|
||||
lemon.exe to crash.
|
||||
We do not see this as a problem, as lemon.exe is not intended to be used
|
||||
with hostile inputs.
|
||||
To summarize:</p>
|
||||
|
||||
<ul>
|
||||
<li>Parser code generated by lemon → Robust and secure
|
||||
<li>The "lemon.exe" command line tool itself → Not so much
|
||||
</ul>
|
||||
|
||||
<h2>Theory of Operation</h2>
|
||||
|
||||
<p>The main goal of Lemon is to translate a context free grammar (CFG)
|
||||
|
||||
+28
-19
@@ -1707,6 +1707,19 @@ static void fts3CursorFinalizeStmt(Fts3Cursor *pCsr){
|
||||
sqlite3_finalize(pCsr->pStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all resources currently held by the cursor passed as the only
|
||||
** argument.
|
||||
*/
|
||||
static void fts3ClearCursor(Fts3Cursor *pCsr){
|
||||
fts3CursorFinalizeStmt(pCsr);
|
||||
sqlite3Fts3FreeDeferredTokens(pCsr);
|
||||
sqlite3_free(pCsr->aDoclist);
|
||||
sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
|
||||
sqlite3Fts3ExprFree(pCsr->pExpr);
|
||||
memset(&(&pCsr->base)[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
|
||||
}
|
||||
|
||||
/*
|
||||
** Close the cursor. For additional information see the documentation
|
||||
** on the xClose method of the virtual table interface.
|
||||
@@ -1714,11 +1727,7 @@ static void fts3CursorFinalizeStmt(Fts3Cursor *pCsr){
|
||||
static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){
|
||||
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
|
||||
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
|
||||
fts3CursorFinalizeStmt(pCsr);
|
||||
sqlite3Fts3ExprFree(pCsr->pExpr);
|
||||
sqlite3Fts3FreeDeferredTokens(pCsr);
|
||||
sqlite3_free(pCsr->aDoclist);
|
||||
sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
|
||||
fts3ClearCursor(pCsr);
|
||||
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
|
||||
sqlite3_free(pCsr);
|
||||
return SQLITE_OK;
|
||||
@@ -1744,7 +1753,7 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
|
||||
}else{
|
||||
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
|
||||
if( !zSql ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
|
||||
rc = sqlite3_prepare_v3(p->db, zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1;
|
||||
@@ -3219,11 +3228,7 @@ static int fts3FilterMethod(
|
||||
assert( iIdx==nVal );
|
||||
|
||||
/* In case the cursor has been used before, clear it now. */
|
||||
fts3CursorFinalizeStmt(pCsr);
|
||||
sqlite3_free(pCsr->aDoclist);
|
||||
sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
|
||||
sqlite3Fts3ExprFree(pCsr->pExpr);
|
||||
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
|
||||
fts3ClearCursor(pCsr);
|
||||
|
||||
/* Set the lower and upper bounds on docids to return */
|
||||
pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
|
||||
@@ -3281,7 +3286,7 @@ static int fts3FilterMethod(
|
||||
);
|
||||
}
|
||||
if( zSql ){
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
|
||||
rc = sqlite3_prepare_v3(p->db,zSql,-1,SQLITE_PREPARE_PERSISTENT,&pCsr->pStmt,0);
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
@@ -3302,7 +3307,12 @@ static int fts3FilterMethod(
|
||||
** routine to find out if it has reached the end of a result set.
|
||||
*/
|
||||
static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){
|
||||
return ((Fts3Cursor *)pCursor)->isEof;
|
||||
Fts3Cursor *pCsr = (Fts3Cursor*)pCursor;
|
||||
if( pCsr->isEof ){
|
||||
fts3ClearCursor(pCsr);
|
||||
pCsr->isEof = 1;
|
||||
}
|
||||
return pCsr->isEof;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3343,8 +3353,7 @@ static int fts3ColumnMethod(
|
||||
switch( iCol-p->nColumn ){
|
||||
case 0:
|
||||
/* The special 'table-name' column */
|
||||
sqlite3_result_blob(pCtx, &pCsr, sizeof(Fts3Cursor*), SQLITE_TRANSIENT);
|
||||
sqlite3_result_subtype(pCtx, SQLITE_BLOB);
|
||||
sqlite3_result_pointer(pCtx, pCsr);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
@@ -3562,9 +3571,10 @@ static int fts3FunctionArg(
|
||||
sqlite3_value *pVal, /* argv[0] passed to function */
|
||||
Fts3Cursor **ppCsr /* OUT: Store cursor handle here */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
if( sqlite3_value_subtype(pVal)==SQLITE_BLOB ){
|
||||
*ppCsr = *(Fts3Cursor**)sqlite3_value_blob(pVal);
|
||||
int rc;
|
||||
*ppCsr = (Fts3Cursor*)sqlite3_value_pointer(pVal);
|
||||
if( (*ppCsr)!=0 ){
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
|
||||
sqlite3_result_error(pContext, zErr, -1);
|
||||
@@ -4787,7 +4797,6 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
|
||||
** data stored in all rows of each column of the table, from left
|
||||
** to right.
|
||||
*/
|
||||
int rc;
|
||||
Fts3Table *p = (Fts3Table*)pCsr->base.pVtab;
|
||||
sqlite3_stmt *pStmt;
|
||||
sqlite3_int64 nDoc = 0;
|
||||
|
||||
@@ -407,7 +407,8 @@ static int fts3SqlStmt(
|
||||
if( !zSql ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, NULL);
|
||||
rc = sqlite3_prepare_v3(p->db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
|
||||
&pStmt, NULL);
|
||||
sqlite3_free(zSql);
|
||||
assert( rc==SQLITE_OK || pStmt==0 );
|
||||
p->aStmt[eStmt] = pStmt;
|
||||
|
||||
@@ -67,9 +67,11 @@ void sqlite3Fts5BufferAppendBlob(
|
||||
const u8 *pData
|
||||
){
|
||||
assert_nc( *pRc || nData>=0 );
|
||||
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
|
||||
memcpy(&pBuf->p[pBuf->n], pData, nData);
|
||||
pBuf->n += nData;
|
||||
if( nData ){
|
||||
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
|
||||
memcpy(&pBuf->p[pBuf->n], pData, nData);
|
||||
pBuf->n += nData;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -246,8 +248,8 @@ void *sqlite3Fts5MallocZero(int *pRc, int nByte){
|
||||
void *pRet = 0;
|
||||
if( *pRc==SQLITE_OK ){
|
||||
pRet = sqlite3_malloc(nByte);
|
||||
if( pRet==0 && nByte>0 ){
|
||||
*pRc = SQLITE_NOMEM;
|
||||
if( pRet==0 ){
|
||||
if( nByte>0 ) *pRc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memset(pRet, 0, nByte);
|
||||
}
|
||||
|
||||
+39
-29
@@ -36,9 +36,10 @@ struct Fts5Hash {
|
||||
|
||||
/*
|
||||
** Each entry in the hash table is represented by an object of the
|
||||
** following type. Each object, its key (zKey[]) and its current data
|
||||
** are stored in a single memory allocation. The position list data
|
||||
** immediately follows the key data in memory.
|
||||
** following type. Each object, its key (a nul-terminated string) and
|
||||
** its current data are stored in a single memory allocation. The
|
||||
** key immediately follows the object in memory. The position list
|
||||
** data immediately follows the key data in memory.
|
||||
**
|
||||
** The data that follows the key is in a similar, but not identical format
|
||||
** to the doclist data stored in the database. It is:
|
||||
@@ -62,20 +63,20 @@ struct Fts5HashEntry {
|
||||
int nAlloc; /* Total size of allocation */
|
||||
int iSzPoslist; /* Offset of space for 4-byte poslist size */
|
||||
int nData; /* Total bytes of data (incl. structure) */
|
||||
int nKey; /* Length of zKey[] in bytes */
|
||||
int nKey; /* Length of key in bytes */
|
||||
u8 bDel; /* Set delete-flag @ iSzPoslist */
|
||||
u8 bContent; /* Set content-flag (detail=none mode) */
|
||||
i16 iCol; /* Column of last value written */
|
||||
int iPos; /* Position of last value written */
|
||||
i64 iRowid; /* Rowid of last value written */
|
||||
char zKey[8]; /* Nul-terminated entry key */
|
||||
};
|
||||
|
||||
/*
|
||||
** Size of Fts5HashEntry without the zKey[] array.
|
||||
** Eqivalent to:
|
||||
**
|
||||
** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
|
||||
*/
|
||||
#define FTS5_HASHENTRYSIZE (sizeof(Fts5HashEntry)-8)
|
||||
|
||||
#define fts5EntryKey(p) ( ((char *)(&(p)[1])) )
|
||||
|
||||
|
||||
/*
|
||||
@@ -170,10 +171,11 @@ static int fts5HashResize(Fts5Hash *pHash){
|
||||
|
||||
for(i=0; i<pHash->nSlot; i++){
|
||||
while( apOld[i] ){
|
||||
int iHash;
|
||||
unsigned int iHash;
|
||||
Fts5HashEntry *p = apOld[i];
|
||||
apOld[i] = p->pHashNext;
|
||||
iHash = fts5HashKey(nNew, (u8*)p->zKey, (int)strlen(p->zKey));
|
||||
iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p),
|
||||
(int)strlen(fts5EntryKey(p)));
|
||||
p->pHashNext = apNew[iHash];
|
||||
apNew[iHash] = p;
|
||||
}
|
||||
@@ -244,9 +246,10 @@ int sqlite3Fts5HashWrite(
|
||||
/* Attempt to locate an existing hash entry */
|
||||
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
|
||||
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
|
||||
if( p->zKey[0]==bByte
|
||||
char *zKey = fts5EntryKey(p);
|
||||
if( zKey[0]==bByte
|
||||
&& p->nKey==nToken
|
||||
&& memcmp(&p->zKey[1], pToken, nToken)==0
|
||||
&& memcmp(&zKey[1], pToken, nToken)==0
|
||||
){
|
||||
break;
|
||||
}
|
||||
@@ -255,7 +258,8 @@ int sqlite3Fts5HashWrite(
|
||||
/* If an existing hash entry cannot be found, create a new one. */
|
||||
if( p==0 ){
|
||||
/* Figure out how much space to allocate */
|
||||
int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64;
|
||||
char *zKey;
|
||||
int nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64;
|
||||
if( nByte<128 ) nByte = 128;
|
||||
|
||||
/* Grow the Fts5Hash.aSlot[] array if necessary. */
|
||||
@@ -268,14 +272,15 @@ int sqlite3Fts5HashWrite(
|
||||
/* Allocate new Fts5HashEntry and add it to the hash table. */
|
||||
p = (Fts5HashEntry*)sqlite3_malloc(nByte);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
memset(p, 0, FTS5_HASHENTRYSIZE);
|
||||
memset(p, 0, sizeof(Fts5HashEntry));
|
||||
p->nAlloc = nByte;
|
||||
p->zKey[0] = bByte;
|
||||
memcpy(&p->zKey[1], pToken, nToken);
|
||||
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) );
|
||||
zKey = fts5EntryKey(p);
|
||||
zKey[0] = bByte;
|
||||
memcpy(&zKey[1], pToken, nToken);
|
||||
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
|
||||
p->nKey = nToken;
|
||||
p->zKey[nToken+1] = '\0';
|
||||
p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE;
|
||||
zKey[nToken+1] = '\0';
|
||||
p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry);
|
||||
p->pHashNext = pHash->aSlot[iHash];
|
||||
pHash->aSlot[iHash] = p;
|
||||
pHash->nEntry++;
|
||||
@@ -393,9 +398,11 @@ static Fts5HashEntry *fts5HashEntryMerge(
|
||||
p1 = 0;
|
||||
}else{
|
||||
int i = 0;
|
||||
while( p1->zKey[i]==p2->zKey[i] ) i++;
|
||||
char *zKey1 = fts5EntryKey(p1);
|
||||
char *zKey2 = fts5EntryKey(p2);
|
||||
while( zKey1[i]==zKey2[i] ) i++;
|
||||
|
||||
if( ((u8)p1->zKey[i])>((u8)p2->zKey[i]) ){
|
||||
if( ((u8)zKey1[i])>((u8)zKey2[i]) ){
|
||||
/* p2 is smaller */
|
||||
*ppOut = p2;
|
||||
ppOut = &p2->pScanNext;
|
||||
@@ -438,7 +445,7 @@ static int fts5HashEntrySort(
|
||||
for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
|
||||
Fts5HashEntry *pIter;
|
||||
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
|
||||
if( pTerm==0 || 0==memcmp(pIter->zKey, pTerm, nTerm) ){
|
||||
if( pTerm==0 || 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm) ){
|
||||
Fts5HashEntry *pEntry = pIter;
|
||||
pEntry->pScanNext = 0;
|
||||
for(i=0; ap[i]; i++){
|
||||
@@ -471,16 +478,18 @@ int sqlite3Fts5HashQuery(
|
||||
int *pnDoclist /* OUT: Size of doclist in bytes */
|
||||
){
|
||||
unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
|
||||
char *zKey = 0;
|
||||
Fts5HashEntry *p;
|
||||
|
||||
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
|
||||
if( memcmp(p->zKey, pTerm, nTerm)==0 && p->zKey[nTerm]==0 ) break;
|
||||
zKey = fts5EntryKey(p);
|
||||
if( memcmp(zKey, pTerm, nTerm)==0 && zKey[nTerm]==0 ) break;
|
||||
}
|
||||
|
||||
if( p ){
|
||||
fts5HashAddPoslistSize(pHash, p);
|
||||
*ppDoclist = (const u8*)&p->zKey[nTerm+1];
|
||||
*pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
|
||||
*ppDoclist = (const u8*)&zKey[nTerm+1];
|
||||
*pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
|
||||
}else{
|
||||
*ppDoclist = 0;
|
||||
*pnDoclist = 0;
|
||||
@@ -513,11 +522,12 @@ void sqlite3Fts5HashScanEntry(
|
||||
){
|
||||
Fts5HashEntry *p;
|
||||
if( (p = pHash->pScan) ){
|
||||
int nTerm = (int)strlen(p->zKey);
|
||||
char *zKey = fts5EntryKey(p);
|
||||
int nTerm = (int)strlen(zKey);
|
||||
fts5HashAddPoslistSize(pHash, p);
|
||||
*pzTerm = p->zKey;
|
||||
*ppDoclist = (const u8*)&p->zKey[nTerm+1];
|
||||
*pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
|
||||
*pzTerm = zKey;
|
||||
*ppDoclist = (const u8*)&zKey[nTerm+1];
|
||||
*pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
|
||||
}else{
|
||||
*pzTerm = 0;
|
||||
*ppDoclist = 0;
|
||||
|
||||
+16
-9
@@ -728,7 +728,8 @@ static int fts5IndexPrepareStmt(
|
||||
){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( zSql ){
|
||||
p->rc = sqlite3_prepare_v2(p->pConfig->db, zSql, -1, ppStmt, 0);
|
||||
p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1,
|
||||
SQLITE_PREPARE_PERSISTENT, ppStmt, 0);
|
||||
}else{
|
||||
p->rc = SQLITE_NOMEM;
|
||||
}
|
||||
@@ -777,7 +778,8 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p->pDeleter, 0);
|
||||
rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
|
||||
SQLITE_PREPARE_PERSISTENT, &p->pDeleter, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -4192,6 +4194,7 @@ static void fts5IndexMergeLevel(
|
||||
int bOldest; /* True if the output segment is the oldest */
|
||||
int eDetail = p->pConfig->eDetail;
|
||||
const int flags = FTS5INDEX_QUERY_NOOUTPUT;
|
||||
int bTermWritten = 0; /* True if current term already output */
|
||||
|
||||
assert( iLvl<pStruct->nLevel );
|
||||
assert( pLvl->nMerge<=pLvl->nSeg );
|
||||
@@ -4245,18 +4248,22 @@ static void fts5IndexMergeLevel(
|
||||
int nTerm;
|
||||
const u8 *pTerm;
|
||||
|
||||
/* Check for key annihilation. */
|
||||
if( pSegIter->nPos==0 && (bOldest || pSegIter->bDel==0) ) continue;
|
||||
|
||||
pTerm = fts5MultiIterTerm(pIter, &nTerm);
|
||||
if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){
|
||||
if( pnRem && writer.nLeafWritten>nRem ){
|
||||
break;
|
||||
}
|
||||
fts5BufferSet(&p->rc, &term, nTerm, pTerm);
|
||||
bTermWritten =0;
|
||||
}
|
||||
|
||||
/* Check for key annihilation. */
|
||||
if( pSegIter->nPos==0 && (bOldest || pSegIter->bDel==0) ) continue;
|
||||
|
||||
if( p->rc==SQLITE_OK && bTermWritten==0 ){
|
||||
/* This is a new term. Append a term to the output segment. */
|
||||
fts5WriteAppendTerm(p, &writer, nTerm, pTerm);
|
||||
fts5BufferSet(&p->rc, &term, nTerm, pTerm);
|
||||
bTermWritten = 1;
|
||||
}
|
||||
|
||||
/* Append the rowid to the output */
|
||||
@@ -5088,7 +5095,7 @@ static void fts5SetupPrefixIter(
|
||||
if( pData ){
|
||||
pData->p = (u8*)&pData[1];
|
||||
pData->nn = pData->szLeaf = doclist.n;
|
||||
memcpy(pData->p, doclist.p, doclist.n);
|
||||
if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n);
|
||||
fts5MultiIterNew2(p, pData, bDesc, ppIter);
|
||||
}
|
||||
fts5BufferFree(&doclist);
|
||||
@@ -5327,7 +5334,7 @@ int sqlite3Fts5IndexQuery(
|
||||
|
||||
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
|
||||
int iIdx = 0; /* Index to search */
|
||||
memcpy(&buf.p[1], pToken, nToken);
|
||||
if( nToken ) memcpy(&buf.p[1], pToken, nToken);
|
||||
|
||||
/* Figure out which index to search and set iIdx accordingly. If this
|
||||
** is a prefix query for which there is no prefix index, set iIdx to
|
||||
@@ -5376,7 +5383,7 @@ int sqlite3Fts5IndexQuery(
|
||||
}
|
||||
|
||||
if( p->rc ){
|
||||
sqlite3Fts5IterClose(&pRet->base);
|
||||
sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
|
||||
pRet = 0;
|
||||
fts5CloseReader(p);
|
||||
}
|
||||
|
||||
@@ -883,7 +883,8 @@ static int fts5PrepareStatement(
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0);
|
||||
rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
|
||||
SQLITE_PREPARE_PERSISTENT, &pRet, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db));
|
||||
}
|
||||
@@ -1019,7 +1020,8 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){
|
||||
char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs);
|
||||
if( zSql ){
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pStmt, 0);
|
||||
rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
|
||||
SQLITE_PREPARE_PERSISTENT, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 );
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
@@ -136,7 +136,8 @@ static int fts5StorageGetStmt(
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
|
||||
rc = sqlite3_prepare_v3(pC->db, zSql, -1,
|
||||
SQLITE_PREPARE_PERSISTENT, &p->aStmt[eStmt], 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc!=SQLITE_OK && pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
|
||||
|
||||
@@ -182,7 +182,7 @@ static int fts5tokConnectMethod(
|
||||
Fts5tokTable *pTab = 0;
|
||||
int rc;
|
||||
char **azDequote = 0;
|
||||
int nDequote;
|
||||
int nDequote = 0;
|
||||
|
||||
rc = sqlite3_declare_vtab(db,
|
||||
"CREATE TABLE x(input HIDDEN, token, start, end, position)"
|
||||
|
||||
@@ -441,7 +441,7 @@ db func funk funk
|
||||
do_catchsql_test 16.2 {
|
||||
SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d'
|
||||
} {0 {{} -1e-06 {}}}
|
||||
# {1 {SQL logic error or missing database}}
|
||||
# {1 {SQL logic error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@@ -595,5 +595,3 @@ do_execsql_test 22.1 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -294,4 +294,3 @@ do_execsql_test 7.0 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -276,4 +276,3 @@ foreach {tn expr tclexpr} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -242,4 +242,3 @@ foreach {T create} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -309,4 +309,3 @@ foreach {tn q cnt} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -178,4 +178,3 @@ do_execsql_test 5.1 {
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -142,4 +142,3 @@ if {[detail_is_full]} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -167,4 +167,3 @@ do_execsql_test 1.8.2 {
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -55,4 +55,3 @@ do_execsql_test 1.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -66,4 +66,3 @@ do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -147,4 +147,3 @@ do_execsql_test 3.1 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ foreach {tn defn} {
|
||||
} {
|
||||
do_test 2.2.$tn {
|
||||
catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) }
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -297,4 +297,3 @@ do_catchsql_test 4.4.4 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -100,4 +100,3 @@ do_execsql_test 3.3 {
|
||||
} {1 2}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -342,4 +342,3 @@ foreach {tn expr} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -279,4 +279,3 @@ do_execsql_test 9.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -112,4 +112,3 @@ db eval {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -61,4 +61,3 @@ do_test 2.1...slow {
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -64,5 +64,3 @@ foreach_detail_mode $::testprefix {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -84,5 +84,3 @@ foreach_detail_mode $::testprefix {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ foreach {tn val} {
|
||||
} {
|
||||
do_catchsql_test 3.$tn {
|
||||
INSERT INTO t1(t1, rank) VALUES('rank', $val);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -169,33 +169,33 @@ do_execsql_test 9.0 {
|
||||
} {}
|
||||
do_catchsql_test 9.1.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('pgsz', -5);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.1.2 {
|
||||
INSERT INTO abc(abc, rank) VALUES('pgsz', 50000000);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.1.3 {
|
||||
INSERT INTO abc(abc, rank) VALUES('pgsz', 66.67);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
do_catchsql_test 9.2.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('automerge', -5);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.2.2 {
|
||||
INSERT INTO abc(abc, rank) VALUES('automerge', 50000000);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.2.3 {
|
||||
INSERT INTO abc(abc, rank) VALUES('automerge', 66.67);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_execsql_test 9.2.4 {
|
||||
INSERT INTO abc(abc, rank) VALUES('automerge', 1);
|
||||
} {}
|
||||
|
||||
do_catchsql_test 9.3.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('crisismerge', -5);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.3.2 {
|
||||
INSERT INTO abc(abc, rank) VALUES('crisismerge', 66.67);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_execsql_test 9.3.3 {
|
||||
INSERT INTO abc(abc, rank) VALUES('crisismerge', 1);
|
||||
} {}
|
||||
@@ -205,14 +205,14 @@ do_execsql_test 9.3.4 {
|
||||
|
||||
do_catchsql_test 9.4.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('nosuchoption', 1);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
do_catchsql_test 9.5.1 {
|
||||
INSERT INTO abc(abc, rank) VALUES('hashsize', 'not an integer');
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.5.2 {
|
||||
INSERT INTO abc(abc, rank) VALUES('hashsize', -500000);
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
do_catchsql_test 9.5.3 {
|
||||
INSERT INTO abc(abc, rank) VALUES('hashsize', 500000);
|
||||
} {0 {}}
|
||||
@@ -245,7 +245,7 @@ foreach {tn opt} {
|
||||
|
||||
do_catchsql_test 12.1 {
|
||||
INSERT INTO t1(t1, rank) VALUES('rank', NULL);;
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# errors in the 'usermerge' option
|
||||
@@ -260,8 +260,7 @@ foreach {tn val} {
|
||||
4 1
|
||||
} {
|
||||
set sql "INSERT INTO tt(tt, rank) VALUES('usermerge', $val)"
|
||||
do_catchsql_test 13.$tn $sql {1 {SQL logic error or missing database}}
|
||||
do_catchsql_test 13.$tn $sql {1 {SQL logic error}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -66,5 +66,3 @@ do_execsql_test 2.1 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -255,4 +255,3 @@ do_execsql_test 6.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -96,4 +96,3 @@ do_catchsql_test 3.1 {
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -269,4 +269,3 @@ do_catchsql_test 6.2 {
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -409,4 +409,3 @@ do_catchsql_test 9.2.2 {
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# 2017 May 12
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5delete
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<5000
|
||||
)
|
||||
INSERT INTO t1(rowid, x) SELECT i, (i/2)*2 FROM s;
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
execsql BEGIN
|
||||
for {set i 1} {$i<=5000} {incr i} {
|
||||
if {$i % 2} {
|
||||
execsql { INSERT INTO t1 VALUES($i) }
|
||||
} else {
|
||||
execsql { DELETE FROM t1 WHERE rowid = $i }
|
||||
}
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test 1.2 {
|
||||
execsql { INSERT INTO t1(t1, rank) VALUES('usermerge', 2); }
|
||||
for {set i 0} {$i < 5} {incr i} {
|
||||
execsql { INSERT INTO t1(t1, rank) VALUES('merge', 1) }
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
}
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
@@ -241,4 +241,3 @@ do_execsql_test 5.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -63,5 +63,3 @@ foreach_detail_mode $::testprefix {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -197,4 +197,3 @@ foreach v $vocab {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -44,4 +44,3 @@ do_execsql_test 1.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -81,6 +81,3 @@ do_execsql_test 3.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -351,4 +351,3 @@ do_faultsim_test 9.1 -faults oom-* -prep {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -137,4 +137,3 @@ do_faultsim_test 5.0 -faults oom-* -prep {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -110,4 +110,3 @@ do_faultsim_test 3.2 -faults oom-* -prep {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -395,4 +395,3 @@ do_faultsim_test 14.1 -faults oom-t* -prep {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -130,4 +130,3 @@ do_faultsim_test 3.3 -faults oom-t* -body {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -292,4 +292,3 @@ do_faultsim_test 6 -faults oom* -prep {
|
||||
db close
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -116,4 +116,3 @@ do_faultsim_test 2.2 -faults oom-* -body {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -82,4 +82,3 @@ do_faultsim_test 4 -faults oom-* -prep {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -153,4 +153,3 @@ do_faultsim_test 6 -faults oom-* -body {
|
||||
} ;# foreach_detail_mode...
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -61,4 +61,3 @@ do_faultsim_test 2 -faults oom* -prep {
|
||||
faultsim_test_result {0 {1 2}}
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -132,4 +132,3 @@ do_faultsim_test 4.2 -faults oom* -body {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -40,4 +40,3 @@ do_test 1.1 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -90,4 +90,3 @@ do_catchsql_test 4.1 {
|
||||
} {1 {fts5: syntax error near "`"}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -130,4 +130,3 @@ breakpoint
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -210,4 +210,3 @@ foreach {tn pgsz} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -70,4 +70,3 @@ do_execsql_test 1.6 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# 2014 June 17
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5leftjoin
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE vt USING fts5(x);
|
||||
INSERT INTO vt VALUES('abc');
|
||||
INSERT INTO vt VALUES('xyz');
|
||||
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY);
|
||||
INSERT INTO t1 VALUES(1), (2);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM t1 LEFT JOIN (
|
||||
SELECT rowid AS rrr, * FROM vt WHERE vt MATCH 'abc'
|
||||
) ON t1.a = rrr
|
||||
} {1 1 abc 2 {} {}}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT * FROM t1 LEFT JOIN vt ON (vt MATCH 'abc')
|
||||
} {1 abc 2 abc}
|
||||
|
||||
finish_test
|
||||
@@ -492,4 +492,3 @@ do_catchsql_test 14.2 {
|
||||
} {1 {unrecognized matchinfo flag: d}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -241,4 +241,3 @@ do_execsql_test 6.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -55,4 +55,3 @@ do_execsql_test 1.2 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -45,4 +45,3 @@ do_multiclient_test tn {
|
||||
};# do_multiclient_test
|
||||
};# foreach_detail_mode
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -68,4 +68,3 @@ do_near_test 1.25 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 4) } 0
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -178,4 +178,3 @@ do_execsql_test 4.3.1 {
|
||||
do_test 4.2.2 { fts5_level_segs ttt } {3}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -106,4 +106,3 @@ foreach {tn nStep} {
|
||||
do_test 2.$tn.6 { fts5_segcount t1 } 1
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -116,4 +116,3 @@ do_execsql_test 2.0 {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -64,4 +64,3 @@ do_eqp_test 1.5 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -11803,4 +11803,3 @@ foreach {in out} $test_vocab {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -67,4 +67,3 @@ foreach {in out} $test_vocab {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -341,5 +341,3 @@ foreach {tn create} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -79,5 +79,3 @@ for {set tn 1 ; set pgsz 64} {$tn<32} {incr tn; incr pgsz 16} {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ do_test 2.7 {
|
||||
execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db
|
||||
} {1 3 2}
|
||||
|
||||
db2 close
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# At one point there was a problem with queries such as:
|
||||
@@ -151,4 +152,3 @@ do_execsql_test 4.1 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -64,4 +64,3 @@ do_catchsql_test 2.2 {
|
||||
INSERT INTO nc(nc) VALUES('rebuild');
|
||||
} {1 {'rebuild' may not be used with a contentless fts5 table}}
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -149,4 +149,3 @@ do_test 4.3 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -216,4 +216,3 @@ do_execsql_test 6.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -370,4 +370,3 @@ do_execsql_test 17.6 {
|
||||
#db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -116,4 +116,3 @@ do_execsql_test 4.6 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -421,4 +421,3 @@ do_execsql_test 7.1.2 {
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -161,4 +161,3 @@ foreach {tn expr} {
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ do_catchsql_test 2.0 {
|
||||
do_catchsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5tokenize;
|
||||
SELECT * FROM t4;
|
||||
} {1 {SQL logic error or missing database}}
|
||||
} {1 {SQL logic error}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -302,4 +302,3 @@ do_test 9.5.2 { set ::flags } {query}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -59,4 +59,3 @@ do_execsql_test 2.1 "
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -126,4 +126,3 @@ do_test 1.5 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -76,4 +76,3 @@ do_execsql_test 3.2 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -117,5 +117,3 @@ do_execsql_test 2.2.integrity {
|
||||
|
||||
}
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
@@ -61,4 +61,3 @@ do_test 1.7 {
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#
|
||||
# This Makefile is designed for use with main.mk in the root directory of
|
||||
# this project. After including main.mk, the users makefile should contain:
|
||||
#
|
||||
# LSMDIR=$(TOP)/ext/lsm1/
|
||||
# include $(LSMDIR)/Makefile
|
||||
#
|
||||
# The most useful targets are [lsmtest] and [lsm.so].
|
||||
#
|
||||
|
||||
LSMOBJ = \
|
||||
lsm_ckpt.o \
|
||||
lsm_file.o \
|
||||
lsm_log.o \
|
||||
lsm_main.o \
|
||||
lsm_mem.o \
|
||||
lsm_mutex.o \
|
||||
lsm_shared.o \
|
||||
lsm_sorted.o \
|
||||
lsm_str.o \
|
||||
lsm_tree.o \
|
||||
lsm_unix.o \
|
||||
lsm_win32.o \
|
||||
lsm_varint.o \
|
||||
lsm_vtab.o
|
||||
|
||||
LSMHDR = \
|
||||
$(LSMDIR)/lsm.h \
|
||||
$(LSMDIR)/lsmInt.h
|
||||
|
||||
LSMTESTSRC = $(LSMDIR)/lsm-test/lsmtest1.c $(LSMDIR)/lsm-test/lsmtest2.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest3.c $(LSMDIR)/lsm-test/lsmtest4.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest5.c $(LSMDIR)/lsm-test/lsmtest6.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest7.c $(LSMDIR)/lsm-test/lsmtest8.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest9.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest_datasource.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest_func.c $(LSMDIR)/lsm-test/lsmtest_io.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest_main.c $(LSMDIR)/lsm-test/lsmtest_mem.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest_tdb.c $(LSMDIR)/lsm-test/lsmtest_tdb3.c \
|
||||
$(LSMDIR)/lsm-test/lsmtest_util.c $(LSMDIR)/lsm-test/lsmtest_win32.c
|
||||
|
||||
|
||||
# all: lsm.so
|
||||
|
||||
LSMOPTS = -DLSM_MUTEX_PTHREADS=1 -I$(LSMDIR)
|
||||
|
||||
lsm.so: $(LSMOBJ)
|
||||
$(TCCX) -shared -o lsm.so $(LSMOBJ)
|
||||
|
||||
%.o: $(LSMDIR)/%.c $(LSMHDR) sqlite3.h
|
||||
$(TCCX) $(LSMOPTS) -c $<
|
||||
|
||||
lsmtest$(EXE): $(LSMOBJ) $(LSMTESTSRC) $(LSMTESTHDR) sqlite3.o
|
||||
# $(TCPPX) -c $(TOP)/lsm-test/lsmtest_tdb2.cc
|
||||
$(TCCX) $(LSMOPTS) $(LSMTESTSRC) $(LSMOBJ) sqlite3.o -o lsmtest$(EXE) $(THREADLIB)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#
|
||||
# This Makefile is designed for use with Makefile.msc in the root directory
|
||||
# of this project. The Makefile.msc should contain:
|
||||
#
|
||||
# LSMDIR=$(TOP)\ext\lsm1
|
||||
# !INCLUDE $(LSMDIR)\Makefile.msc
|
||||
#
|
||||
# The most useful targets are [lsmtest.exe] and [lsm.dll].
|
||||
#
|
||||
|
||||
LSMOBJ = \
|
||||
lsm_ckpt.lo \
|
||||
lsm_file.lo \
|
||||
lsm_log.lo \
|
||||
lsm_main.lo \
|
||||
lsm_mem.lo \
|
||||
lsm_mutex.lo \
|
||||
lsm_shared.lo \
|
||||
lsm_sorted.lo \
|
||||
lsm_str.lo \
|
||||
lsm_tree.lo \
|
||||
lsm_unix.lo \
|
||||
lsm_win32.lo \
|
||||
lsm_varint.lo \
|
||||
lsm_vtab.lo
|
||||
|
||||
LSMHDR = \
|
||||
$(LSMDIR)\lsm.h \
|
||||
$(LSMDIR)\lsmInt.h
|
||||
|
||||
LSMTESTSRC = $(LSMDIR)\lsm-test\lsmtest1.c $(LSMDIR)\lsm-test\lsmtest2.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest3.c $(LSMDIR)\lsm-test\lsmtest4.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest5.c $(LSMDIR)\lsm-test\lsmtest6.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest7.c $(LSMDIR)\lsm-test\lsmtest8.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest9.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest_datasource.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest_func.c $(LSMDIR)\lsm-test\lsmtest_io.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest_main.c $(LSMDIR)\lsm-test\lsmtest_mem.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest_tdb.c $(LSMDIR)\lsm-test\lsmtest_tdb3.c \
|
||||
$(LSMDIR)\lsm-test\lsmtest_util.c $(LSMDIR)\lsm-test\lsmtest_win32.c
|
||||
|
||||
# all: lsm.dll lsmtest.exe
|
||||
|
||||
LSMOPTS = $(NO_WARN) -DLSM_MUTEX_WIN32=1 -I$(LSMDIR)
|
||||
|
||||
!IF $(DEBUG)>2
|
||||
LSMOPTS = $(LSMOPTS) -DLSM_DEBUG=1
|
||||
!ENDIF
|
||||
|
||||
!IF $(MEMDEBUG)!=0
|
||||
LSMOPTS = $(LSMOPTS) -DLSM_DEBUG_MEM=1
|
||||
!ENDIF
|
||||
|
||||
lsm_ckpt.lo: $(LSMDIR)\lsm_ckpt.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_ckpt.c
|
||||
|
||||
lsm_file.lo: $(LSMDIR)\lsm_file.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_file.c
|
||||
|
||||
lsm_log.lo: $(LSMDIR)\lsm_log.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_log.c
|
||||
|
||||
lsm_main.lo: $(LSMDIR)\lsm_main.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_main.c
|
||||
|
||||
lsm_mem.lo: $(LSMDIR)\lsm_mem.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_mem.c
|
||||
|
||||
lsm_mutex.lo: $(LSMDIR)\lsm_mutex.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_mutex.c
|
||||
|
||||
lsm_shared.lo: $(LSMDIR)\lsm_shared.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_shared.c
|
||||
|
||||
lsm_sorted.lo: $(LSMDIR)\lsm_sorted.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_sorted.c
|
||||
|
||||
lsm_str.lo: $(LSMDIR)\lsm_str.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_str.c
|
||||
|
||||
lsm_tree.lo: $(LSMDIR)\lsm_tree.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_tree.c
|
||||
|
||||
lsm_unix.lo: $(LSMDIR)\lsm_unix.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_unix.c
|
||||
|
||||
lsm_win32.lo: $(LSMDIR)\lsm_win32.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_win32.c
|
||||
|
||||
lsm_varint.lo: $(LSMDIR)\lsm_varint.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_varint.c
|
||||
|
||||
lsm_vtab.lo: $(LSMDIR)\lsm_vtab.c $(LSMHDR) $(SQLITE3H)
|
||||
$(LTCOMPILE) $(LSMOPTS) -c $(LSMDIR)\lsm_vtab.c
|
||||
|
||||
lsm.dll: $(LSMOBJ)
|
||||
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ $(LSMOBJ)
|
||||
|
||||
lsmtest.exe: $(LSMOBJ) $(LSMTESTSRC) $(LSMTESTHDR) $(LIBOBJ)
|
||||
$(LTLINK) $(LSMOPTS) $(LSMTESTSRC) /link $(LSMOBJ) $(LIBOBJ)
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
Organization of test case files:
|
||||
|
||||
lsmtest1.c: Data tests. Tests that perform many inserts and deletes on a
|
||||
database file, then verify that the contents of the database can
|
||||
be queried.
|
||||
|
||||
lsmtest2.c: Crash tests. Tests that attempt to verify that the database
|
||||
recovers correctly following an application or system crash.
|
||||
|
||||
lsmtest3.c: Rollback tests. Tests that focus on the explicit rollback of
|
||||
transactions and sub-transactions.
|
||||
|
||||
lsmtest4.c: Multi-client tests.
|
||||
|
||||
lsmtest5.c: Multi-client tests with a different thread for each client.
|
||||
|
||||
lsmtest6.c: OOM injection tests.
|
||||
|
||||
lsmtest7.c: API tests.
|
||||
|
||||
lsmtest8.c: Writer crash tests. Tests in this file attempt to verify that
|
||||
the system recovers and other clients proceed unaffected if
|
||||
a process fails in the middle of a write transaction.
|
||||
|
||||
The difference from lsmtest2.c is that this file tests
|
||||
live-recovery (recovery from a failure that occurs while other
|
||||
clients are still running) whereas lsmtest2.c tests recovery
|
||||
from a system or power failure.
|
||||
|
||||
lsmtest9.c: More data tests. These focus on testing that calling
|
||||
lsm_work(nMerge=1) to compact the database does not corrupt it.
|
||||
In other words, that databases containing block-redirects
|
||||
can be read and written.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
|
||||
#ifndef __WRAPPER_INT_H_
|
||||
#define __WRAPPER_INT_H_
|
||||
|
||||
#include "lsmtest_tdb.h"
|
||||
#include "sqlite3.h"
|
||||
#include "lsm.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# include "windows.h"
|
||||
# define gettimeofday win32GetTimeOfDay
|
||||
# define F_OK (0)
|
||||
# define sleep(sec) Sleep(1000 * (sec))
|
||||
# define usleep(usec) Sleep(((usec) + 999) / 1000)
|
||||
# ifdef _MSC_VER
|
||||
# include <io.h>
|
||||
# define snprintf _snprintf
|
||||
# define fsync(fd) FlushFileBuffers((HANDLE)_get_osfhandle((fd)))
|
||||
# define fdatasync(fd) FlushFileBuffers((HANDLE)_get_osfhandle((fd)))
|
||||
# define __va_copy(dst,src) ((dst) = (src))
|
||||
# define ftruncate(fd,sz) ((_chsize_s((fd), (sz))==0) ? 0 : -1)
|
||||
# else
|
||||
# error Unsupported C compiler for Windows.
|
||||
# endif
|
||||
int win32GetTimeOfDay(struct timeval *, void *);
|
||||
#endif
|
||||
|
||||
#ifndef _LSM_INT_H
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned char u8;
|
||||
typedef long long int i64;
|
||||
typedef unsigned long long int u64;
|
||||
#endif
|
||||
|
||||
|
||||
#define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0])))
|
||||
|
||||
#define MIN(x,y) ((x)<(y) ? (x) : (y))
|
||||
#define MAX(x,y) ((x)>(y) ? (x) : (y))
|
||||
|
||||
#define unused_parameter(x) (void)(x)
|
||||
|
||||
#define TESTDB_DEFAULT_PAGE_SIZE 4096
|
||||
#define TESTDB_DEFAULT_CACHE_SIZE 2048
|
||||
|
||||
#ifndef _O_BINARY
|
||||
# define _O_BINARY (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Ideally, these should be in wrapper.c. But they are here instead so that
|
||||
** they can be used by the C++ database wrappers in wrapper2.cc.
|
||||
*/
|
||||
typedef struct DatabaseMethods DatabaseMethods;
|
||||
struct TestDb {
|
||||
DatabaseMethods const *pMethods; /* Database methods */
|
||||
const char *zLibrary; /* Library name for tdb_open() */
|
||||
};
|
||||
struct DatabaseMethods {
|
||||
int (*xClose)(TestDb *);
|
||||
int (*xWrite)(TestDb *, void *, int , void *, int);
|
||||
int (*xDelete)(TestDb *, void *, int);
|
||||
int (*xDeleteRange)(TestDb *, void *, int, void *, int);
|
||||
int (*xFetch)(TestDb *, void *, int, void **, int *);
|
||||
int (*xScan)(TestDb *, void *, int, void *, int, void *, int,
|
||||
void (*)(void *, void *, int , void *, int)
|
||||
);
|
||||
int (*xBegin)(TestDb *, int);
|
||||
int (*xCommit)(TestDb *, int);
|
||||
int (*xRollback)(TestDb *, int);
|
||||
};
|
||||
|
||||
/*
|
||||
** Functions in wrapper2.cc (a C++ source file). wrapper2.cc contains the
|
||||
** wrapper for Kyoto Cabinet. Kyoto cabinet has a C API, but
|
||||
** the primary interface is the C++ API.
|
||||
*/
|
||||
int test_kc_open(const char*, const char *zFilename, int bClear, TestDb **ppDb);
|
||||
int test_kc_close(TestDb *);
|
||||
int test_kc_write(TestDb *, void *, int , void *, int);
|
||||
int test_kc_delete(TestDb *, void *, int);
|
||||
int test_kc_delete_range(TestDb *, void *, int, void *, int);
|
||||
int test_kc_fetch(TestDb *, void *, int, void **, int *);
|
||||
int test_kc_scan(TestDb *, void *, int, void *, int, void *, int,
|
||||
void (*)(void *, void *, int , void *, int)
|
||||
);
|
||||
|
||||
int test_mdb_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
int test_mdb_close(TestDb *);
|
||||
int test_mdb_write(TestDb *, void *, int , void *, int);
|
||||
int test_mdb_delete(TestDb *, void *, int);
|
||||
int test_mdb_fetch(TestDb *, void *, int, void **, int *);
|
||||
int test_mdb_scan(TestDb *, void *, int, void *, int, void *, int,
|
||||
void (*)(void *, void *, int , void *, int)
|
||||
);
|
||||
|
||||
/*
|
||||
** Functions in wrapper3.c. This file contains the tdb wrapper for lsm.
|
||||
** The wrapper for lsm is a bit more involved than the others, as it
|
||||
** includes code for a couple of different lsm configurations, and for
|
||||
** various types of fault injection and robustness testing.
|
||||
*/
|
||||
int test_lsm_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
int test_lsm_lomem_open(const char*, const char*, int bClear, TestDb **ppDb);
|
||||
int test_lsm_zip_open(const char*, const char*, int bClear, TestDb **ppDb);
|
||||
int test_lsm_small_open(const char*, const char*, int bClear, TestDb **ppDb);
|
||||
int test_lsm_mt2(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
int test_lsm_mt3(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
|
||||
int tdb_lsm_configure(lsm_db *, const char *);
|
||||
|
||||
/* Functions in lsmtest_tdb4.c */
|
||||
int test_bt_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
int test_fbt_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
int test_fbts_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
|
||||
|
||||
|
||||
/* Functions in testutil.c. */
|
||||
int testPrngInit(void);
|
||||
u32 testPrngValue(u32 iVal);
|
||||
void testPrngArray(u32 iVal, u32 *aOut, int nOut);
|
||||
void testPrngString(u32 iVal, char *aOut, int nOut);
|
||||
|
||||
void testErrorInit(int argc, char **);
|
||||
void testPrintError(const char *zFormat, ...);
|
||||
void testPrintUsage(const char *zArgs);
|
||||
void testPrintFUsage(const char *zFormat, ...);
|
||||
void testTimeInit(void);
|
||||
int testTimeGet(void);
|
||||
|
||||
/* Functions in testmem.c. */
|
||||
void testMallocInstall(lsm_env *pEnv);
|
||||
void testMallocUninstall(lsm_env *pEnv);
|
||||
void testMallocCheck(lsm_env *pEnv, int *, int *, FILE *);
|
||||
void testMallocOom(lsm_env *pEnv, int, int, void(*)(void*), void *);
|
||||
void testMallocOomEnable(lsm_env *pEnv, int);
|
||||
|
||||
/* lsmtest.c */
|
||||
TestDb *testOpen(const char *zSystem, int, int *pRc);
|
||||
void testReopen(TestDb **ppDb, int *pRc);
|
||||
void testClose(TestDb **ppDb);
|
||||
|
||||
void testFetch(TestDb *, void *, int, void *, int, int *);
|
||||
void testWrite(TestDb *, void *, int, void *, int, int *);
|
||||
void testDelete(TestDb *, void *, int, int *);
|
||||
void testDeleteRange(TestDb *, void *, int, void *, int, int *);
|
||||
void testWriteStr(TestDb *, const char *, const char *zVal, int *pRc);
|
||||
void testFetchStr(TestDb *, const char *, const char *, int *pRc);
|
||||
|
||||
void testBegin(TestDb *pDb, int iTrans, int *pRc);
|
||||
void testCommit(TestDb *pDb, int iTrans, int *pRc);
|
||||
|
||||
void test_failed(void);
|
||||
|
||||
char *testMallocPrintf(const char *zFormat, ...);
|
||||
char *testMallocVPrintf(const char *zFormat, va_list ap);
|
||||
int testGlobMatch(const char *zPattern, const char *zStr);
|
||||
|
||||
void testScanCompare(TestDb *, TestDb *, int, void *, int, void *, int, int *);
|
||||
void testFetchCompare(TestDb *, TestDb *, void *, int, int *);
|
||||
|
||||
void *testMalloc(int);
|
||||
void *testMallocCopy(void *pCopy, int nByte);
|
||||
void *testRealloc(void *, int);
|
||||
void testFree(void *);
|
||||
|
||||
/* lsmtest_bt.c */
|
||||
int do_bt(int nArg, char **azArg);
|
||||
|
||||
/* testio.c */
|
||||
int testVfsConfigureDb(TestDb *pDb);
|
||||
|
||||
/* testfunc.c */
|
||||
int do_show(int nArg, char **azArg);
|
||||
int do_work(int nArg, char **azArg);
|
||||
|
||||
/* testio.c */
|
||||
int do_io(int nArg, char **azArg);
|
||||
|
||||
/* lsmtest2.c */
|
||||
void do_crash_test(const char *zPattern, int *pRc);
|
||||
int do_rollback_test(int nArg, char **azArg);
|
||||
|
||||
/* test3.c */
|
||||
void test_rollback(const char *zSystem, const char *zPattern, int *pRc);
|
||||
|
||||
/* test4.c */
|
||||
void test_mc(const char *zSystem, const char *zPattern, int *pRc);
|
||||
|
||||
/* test5.c */
|
||||
void test_mt(const char *zSystem, const char *zPattern, int *pRc);
|
||||
|
||||
/* lsmtest6.c */
|
||||
void test_oom(const char *zPattern, int *pRc);
|
||||
void testDeleteLsmdb(const char *zFile);
|
||||
|
||||
void testSaveDb(const char *zFile, const char *zAuxExt);
|
||||
void testRestoreDb(const char *zFile, const char *zAuxExt);
|
||||
void testCopyLsmdb(const char *zFrom, const char *zTo);
|
||||
|
||||
/* lsmtest7.c */
|
||||
void test_api(const char *zPattern, int *pRc);
|
||||
|
||||
/* lsmtest8.c */
|
||||
void do_writer_crash_test(const char *zPattern, int *pRc);
|
||||
|
||||
/*************************************************************************
|
||||
** Interface to functionality in test_datasource.c.
|
||||
*/
|
||||
typedef struct Datasource Datasource;
|
||||
typedef struct DatasourceDefn DatasourceDefn;
|
||||
|
||||
struct DatasourceDefn {
|
||||
int eType; /* A TEST_DATASOURCE_* value */
|
||||
int nMinKey; /* Minimum key size */
|
||||
int nMaxKey; /* Maximum key size */
|
||||
int nMinVal; /* Minimum value size */
|
||||
int nMaxVal; /* Maximum value size */
|
||||
};
|
||||
|
||||
#define TEST_DATASOURCE_RANDOM 1
|
||||
#define TEST_DATASOURCE_SEQUENCE 2
|
||||
|
||||
char *testDatasourceName(const DatasourceDefn *);
|
||||
Datasource *testDatasourceNew(const DatasourceDefn *);
|
||||
void testDatasourceFree(Datasource *);
|
||||
void testDatasourceEntry(Datasource *, int, void **, int *, void **, int *);
|
||||
/* End of test_datasource.c interface.
|
||||
*************************************************************************/
|
||||
void testDatasourceFetch(
|
||||
TestDb *pDb, /* Database handle */
|
||||
Datasource *pData,
|
||||
int iKey,
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
);
|
||||
|
||||
void testWriteDatasource(TestDb *, Datasource *, int, int *);
|
||||
void testWriteDatasourceRange(TestDb *, Datasource *, int, int, int *);
|
||||
void testDeleteDatasource(TestDb *, Datasource *, int, int *);
|
||||
void testDeleteDatasourceRange(TestDb *, Datasource *, int, int, int *);
|
||||
|
||||
|
||||
/* test1.c */
|
||||
void test_data_1(const char *, const char *, int *pRc);
|
||||
void test_data_2(const char *, const char *, int *pRc);
|
||||
void test_data_3(const char *, const char *, int *pRc);
|
||||
void testDbContents(TestDb *, Datasource *, int, int, int, int, int, int *);
|
||||
void testCaseProgress(int, int, int, int *);
|
||||
int testCaseNDot(void);
|
||||
|
||||
void testCompareDb(Datasource *, int, int, TestDb *, TestDb *, int *);
|
||||
int testControlDb(TestDb **ppDb);
|
||||
|
||||
typedef struct CksumDb CksumDb;
|
||||
CksumDb *testCksumArrayNew(Datasource *, int, int, int);
|
||||
char *testCksumArrayGet(CksumDb *, int);
|
||||
void testCksumArrayFree(CksumDb *);
|
||||
void testCaseStart(int *pRc, char *zFmt, ...);
|
||||
void testCaseFinish(int rc);
|
||||
void testCaseSkip(void);
|
||||
int testCaseBegin(int *, const char *, const char *, ...);
|
||||
|
||||
#define TEST_CKSUM_BYTES 29
|
||||
int testCksumDatabase(TestDb *pDb, char *zOut);
|
||||
int testCountDatabase(TestDb *pDb);
|
||||
void testCompareInt(int, int, int *);
|
||||
void testCompareStr(const char *z1, const char *z2, int *pRc);
|
||||
|
||||
/* lsmtest9.c */
|
||||
void test_data_4(const char *, const char *, int *pRc);
|
||||
|
||||
|
||||
/*
|
||||
** Similar to the Tcl_GetIndexFromObjStruct() Tcl library function.
|
||||
*/
|
||||
#define testArgSelect(w,x,y,z) testArgSelectX(w,x,sizeof(w[0]),y,z)
|
||||
int testArgSelectX(void *, const char *, int, const char *, int *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* End of the 'extern "C"' block */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,654 @@
|
||||
|
||||
#include "lsmtest.h"
|
||||
|
||||
#define DATA_SEQUENTIAL TEST_DATASOURCE_SEQUENCE
|
||||
#define DATA_RANDOM TEST_DATASOURCE_RANDOM
|
||||
|
||||
typedef struct Datatest1 Datatest1;
|
||||
typedef struct Datatest2 Datatest2;
|
||||
|
||||
/*
|
||||
** An instance of the following structure contains parameters used to
|
||||
** customize the test function in this file. Test procedure:
|
||||
**
|
||||
** 1. Create a data-source based on the "datasource definition" vars.
|
||||
**
|
||||
** 2. Insert nRow key value pairs into the database.
|
||||
**
|
||||
** 3. Delete all keys from the database. Deletes are done in the same
|
||||
** order as the inserts.
|
||||
**
|
||||
** During steps 2 and 3 above, after each Datatest1.nVerify inserts or
|
||||
** deletes, the following:
|
||||
**
|
||||
** a. Run Datasource.nTest key lookups and check the results are as expected.
|
||||
**
|
||||
** b. If Datasource.bTestScan is true, run a handful (8) of range
|
||||
** queries (scanning forwards and backwards). Check that the results
|
||||
** are as expected.
|
||||
**
|
||||
** c. Close and reopen the database. Then run (a) and (b) again.
|
||||
*/
|
||||
struct Datatest1 {
|
||||
/* Datasource definition */
|
||||
DatasourceDefn defn;
|
||||
|
||||
/* Test procedure parameters */
|
||||
int nRow; /* Number of rows to insert then delete */
|
||||
int nVerify; /* How often to verify the db contents */
|
||||
int nTest; /* Number of keys to test (0==all) */
|
||||
int bTestScan; /* True to do scan tests */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following data structure is used to describe the
|
||||
** second type of test case in this file. The chief difference between
|
||||
** these tests and those described by Datatest1 is that these tests also
|
||||
** experiment with range-delete operations. Tests proceed as follows:
|
||||
**
|
||||
** 1. Open the datasource described by Datatest2.defn.
|
||||
**
|
||||
** 2. Open a connection on an empty database.
|
||||
**
|
||||
** 3. Do this Datatest2.nIter times:
|
||||
**
|
||||
** a) Insert Datatest2.nWrite key-value pairs from the datasource.
|
||||
**
|
||||
** b) Select two pseudo-random keys and use them as the start
|
||||
** and end points of a range-delete operation.
|
||||
**
|
||||
** c) Verify that the contents of the database are as expected (see
|
||||
** below for details).
|
||||
**
|
||||
** d) Close and then reopen the database handle.
|
||||
**
|
||||
** e) Verify that the contents of the database are still as expected.
|
||||
**
|
||||
** The inserts and range deletes are run twice - once on the database being
|
||||
** tested and once using a control system (sqlite3, kc etc. - something that
|
||||
** works). In order to verify that the contents of the db being tested are
|
||||
** correct, the test runs a bunch of scans and lookups on both the test and
|
||||
** control databases. If the results are the same, the test passes.
|
||||
*/
|
||||
struct Datatest2 {
|
||||
DatasourceDefn defn;
|
||||
int nRange;
|
||||
int nWrite; /* Number of writes per iteration */
|
||||
int nIter; /* Total number of iterations to run */
|
||||
};
|
||||
|
||||
/*
|
||||
** Generate a unique name for the test case pTest with database system
|
||||
** zSystem.
|
||||
*/
|
||||
static char *getName(const char *zSystem, int bRecover, Datatest1 *pTest){
|
||||
char *zRet;
|
||||
char *zData;
|
||||
zData = testDatasourceName(&pTest->defn);
|
||||
zRet = testMallocPrintf("data.%s.%s.rec=%d.%d.%d",
|
||||
zSystem, zData, bRecover, pTest->nRow, pTest->nVerify
|
||||
);
|
||||
testFree(zData);
|
||||
return zRet;
|
||||
}
|
||||
|
||||
int testControlDb(TestDb **ppDb){
|
||||
#ifdef HAVE_KYOTOCABINET
|
||||
return tdb_open("kyotocabinet", "tmp.db", 1, ppDb);
|
||||
#else
|
||||
return tdb_open("sqlite3", "", 1, ppDb);
|
||||
#endif
|
||||
}
|
||||
|
||||
void testDatasourceFetch(
|
||||
TestDb *pDb, /* Database handle */
|
||||
Datasource *pData,
|
||||
int iKey,
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
void *pKey; int nKey; /* Database key to query for */
|
||||
void *pVal; int nVal; /* Expected result of query */
|
||||
|
||||
testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal);
|
||||
testFetch(pDb, pKey, nKey, pVal, nVal, pRc);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called to test that the contents of database pDb
|
||||
** are as expected. In this case, expected is defined as containing
|
||||
** key-value pairs iFirst through iLast, inclusive, from data source
|
||||
** pData. In other words, a loop like the following could be used to
|
||||
** construct a database with identical contents from scratch.
|
||||
**
|
||||
** for(i=iFirst; i<=iLast; i++){
|
||||
** testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal);
|
||||
** // insert (pKey, nKey) -> (pVal, nVal) into database
|
||||
** }
|
||||
**
|
||||
** The key domain consists of keys 0 to (nRow-1), inclusive, from
|
||||
** data source pData. For both scan and lookup tests, keys are selected
|
||||
** pseudo-randomly from within this set.
|
||||
**
|
||||
** This function runs nLookupTest lookup tests and nScanTest scan tests.
|
||||
**
|
||||
** A lookup test consists of selecting a key from the domain and querying
|
||||
** pDb for it. The test fails if the presence of the key and, if present,
|
||||
** the associated value do not match the expectations defined above.
|
||||
**
|
||||
** A scan test involves selecting a key from the domain and running
|
||||
** the following queries:
|
||||
**
|
||||
** 1. Scan all keys equal to or greater than the key, in ascending order.
|
||||
** 2. Scan all keys equal to or smaller than the key, in descending order.
|
||||
**
|
||||
** Additionally, if nLookupTest is greater than zero, the following are
|
||||
** run once:
|
||||
**
|
||||
** 1. Scan all keys in the db, in ascending order.
|
||||
** 2. Scan all keys in the db, in descending order.
|
||||
**
|
||||
** As you would assume, the test fails if the returned values do not match
|
||||
** expectations.
|
||||
*/
|
||||
void testDbContents(
|
||||
TestDb *pDb, /* Database handle being tested */
|
||||
Datasource *pData, /* pDb contains data from here */
|
||||
int nRow, /* Size of key domain */
|
||||
int iFirst, /* Index of first key from pData in pDb */
|
||||
int iLast, /* Index of last key from pData in pDb */
|
||||
int nLookupTest, /* Number of lookup tests to run */
|
||||
int nScanTest, /* Number of scan tests to run */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
int j;
|
||||
int rc = *pRc;
|
||||
|
||||
if( rc==0 && nScanTest ){
|
||||
TestDb *pDb2 = 0;
|
||||
|
||||
/* Open a control db (i.e. one that we assume works) */
|
||||
rc = testControlDb(&pDb2);
|
||||
|
||||
for(j=iFirst; rc==0 && j<=iLast; j++){
|
||||
void *pKey; int nKey; /* Database key to insert */
|
||||
void *pVal; int nVal; /* Database value to insert */
|
||||
testDatasourceEntry(pData, j, &pKey, &nKey, &pVal, &nVal);
|
||||
rc = tdb_write(pDb2, pKey, nKey, pVal, nVal);
|
||||
}
|
||||
|
||||
if( rc==0 ){
|
||||
int iKey1;
|
||||
int iKey2;
|
||||
void *pKey1; int nKey1; /* Start key */
|
||||
void *pKey2; int nKey2; /* Final key */
|
||||
|
||||
iKey1 = testPrngValue((iFirst<<8) + (iLast<<16)) % nRow;
|
||||
iKey2 = testPrngValue((iLast<<8) + (iFirst<<16)) % nRow;
|
||||
testDatasourceEntry(pData, iKey1, &pKey2, &nKey1, 0, 0);
|
||||
pKey1 = testMalloc(nKey1+1);
|
||||
memcpy(pKey1, pKey2, nKey1+1);
|
||||
testDatasourceEntry(pData, iKey2, &pKey2, &nKey2, 0, 0);
|
||||
|
||||
testScanCompare(pDb2, pDb, 0, 0, 0, 0, 0, &rc);
|
||||
testScanCompare(pDb2, pDb, 0, 0, 0, pKey2, nKey2, &rc);
|
||||
testScanCompare(pDb2, pDb, 0, pKey1, nKey1, 0, 0, &rc);
|
||||
testScanCompare(pDb2, pDb, 0, pKey1, nKey1, pKey2, nKey2, &rc);
|
||||
testScanCompare(pDb2, pDb, 1, 0, 0, 0, 0, &rc);
|
||||
testScanCompare(pDb2, pDb, 1, 0, 0, pKey2, nKey2, &rc);
|
||||
testScanCompare(pDb2, pDb, 1, pKey1, nKey1, 0, 0, &rc);
|
||||
testScanCompare(pDb2, pDb, 1, pKey1, nKey1, pKey2, nKey2, &rc);
|
||||
testFree(pKey1);
|
||||
}
|
||||
tdb_close(pDb2);
|
||||
}
|
||||
|
||||
/* Test some lookups. */
|
||||
for(j=0; rc==0 && j<nLookupTest; j++){
|
||||
int iKey; /* Datasource key to test */
|
||||
void *pKey; int nKey; /* Database key to query for */
|
||||
void *pVal; int nVal; /* Expected result of query */
|
||||
|
||||
if( nLookupTest>=nRow ){
|
||||
iKey = j;
|
||||
}else{
|
||||
iKey = testPrngValue(j + (iFirst<<8) + (iLast<<16)) % nRow;
|
||||
}
|
||||
|
||||
testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal);
|
||||
if( iFirst>iKey || iKey>iLast ){
|
||||
pVal = 0;
|
||||
nVal = -1;
|
||||
}
|
||||
|
||||
testFetch(pDb, pKey, nKey, pVal, nVal, &rc);
|
||||
}
|
||||
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function should be called during long running test cases to output
|
||||
** the progress dots (...) to stdout.
|
||||
*/
|
||||
void testCaseProgress(int i, int n, int nDot, int *piDot){
|
||||
int iDot = *piDot;
|
||||
while( iDot < ( ((nDot*2+1) * i) / (n*2) ) ){
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
iDot++;
|
||||
}
|
||||
*piDot = iDot;
|
||||
}
|
||||
|
||||
int testCaseNDot(void){ return 20; }
|
||||
|
||||
#if 0
|
||||
static void printScanCb(
|
||||
void *pCtx, void *pKey, int nKey, void *pVal, int nVal
|
||||
){
|
||||
printf("%s\n", (char *)pKey);
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
void testReopenRecover(TestDb **ppDb, int *pRc){
|
||||
if( *pRc==0 ){
|
||||
const char *zLib = tdb_library_name(*ppDb);
|
||||
const char *zDflt = tdb_default_db(zLib);
|
||||
testCopyLsmdb(zDflt, "bak.db");
|
||||
testClose(ppDb);
|
||||
testCopyLsmdb("bak.db", zDflt);
|
||||
*pRc = tdb_open(zLib, 0, 0, ppDb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void doDataTest1(
|
||||
const char *zSystem, /* Database system to test */
|
||||
int bRecover,
|
||||
Datatest1 *p, /* Structure containing test parameters */
|
||||
int *pRc /* OUT: Error code */
|
||||
){
|
||||
int i;
|
||||
int iDot;
|
||||
int rc = LSM_OK;
|
||||
Datasource *pData;
|
||||
TestDb *pDb;
|
||||
|
||||
/* Start the test case, open a database and allocate the datasource. */
|
||||
pDb = testOpen(zSystem, 1, &rc);
|
||||
pData = testDatasourceNew(&p->defn);
|
||||
|
||||
i = 0;
|
||||
iDot = 0;
|
||||
while( rc==LSM_OK && i<p->nRow ){
|
||||
|
||||
/* Insert some data */
|
||||
testWriteDatasourceRange(pDb, pData, i, p->nVerify, &rc);
|
||||
i += p->nVerify;
|
||||
|
||||
/* Check that the db content is correct. */
|
||||
testDbContents(pDb, pData, p->nRow, 0, i-1, p->nTest, p->bTestScan, &rc);
|
||||
|
||||
if( bRecover ){
|
||||
testReopenRecover(&pDb, &rc);
|
||||
}else{
|
||||
testReopen(&pDb, &rc);
|
||||
}
|
||||
|
||||
/* Check that the db content is still correct. */
|
||||
testDbContents(pDb, pData, p->nRow, 0, i-1, p->nTest, p->bTestScan, &rc);
|
||||
|
||||
/* Update the progress dots... */
|
||||
testCaseProgress(i, p->nRow, testCaseNDot()/2, &iDot);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
iDot = 0;
|
||||
while( rc==LSM_OK && i<p->nRow ){
|
||||
|
||||
/* Delete some entries */
|
||||
testDeleteDatasourceRange(pDb, pData, i, p->nVerify, &rc);
|
||||
i += p->nVerify;
|
||||
|
||||
/* Check that the db content is correct. */
|
||||
testDbContents(pDb, pData, p->nRow, i, p->nRow-1,p->nTest,p->bTestScan,&rc);
|
||||
|
||||
/* Close and reopen the database. */
|
||||
if( bRecover ){
|
||||
testReopenRecover(&pDb, &rc);
|
||||
}else{
|
||||
testReopen(&pDb, &rc);
|
||||
}
|
||||
|
||||
/* Check that the db content is still correct. */
|
||||
testDbContents(pDb, pData, p->nRow, i, p->nRow-1,p->nTest,p->bTestScan,&rc);
|
||||
|
||||
/* Update the progress dots... */
|
||||
testCaseProgress(i, p->nRow, testCaseNDot()/2, &iDot);
|
||||
}
|
||||
|
||||
/* Free the datasource, close the database and finish the test case. */
|
||||
testDatasourceFree(pData);
|
||||
tdb_close(pDb);
|
||||
testCaseFinish(rc);
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
|
||||
void test_data_1(
|
||||
const char *zSystem, /* Database system name */
|
||||
const char *zPattern, /* Run test cases that match this pattern */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
Datatest1 aTest[] = {
|
||||
{ {DATA_RANDOM, 500,600, 1000,2000}, 1000, 100, 10, 0},
|
||||
{ {DATA_RANDOM, 20,25, 100,200}, 1000, 250, 1000, 1},
|
||||
{ {DATA_RANDOM, 8,10, 100,200}, 1000, 250, 1000, 1},
|
||||
{ {DATA_RANDOM, 8,10, 10,20}, 1000, 250, 1000, 1},
|
||||
{ {DATA_RANDOM, 8,10, 1000,2000}, 1000, 250, 1000, 1},
|
||||
{ {DATA_RANDOM, 8,100, 10000,20000}, 100, 25, 100, 1},
|
||||
{ {DATA_RANDOM, 80,100, 10,20}, 1000, 250, 1000, 1},
|
||||
{ {DATA_RANDOM, 5000,6000, 10,20}, 100, 25, 100, 1},
|
||||
{ {DATA_SEQUENTIAL, 5,10, 10,20}, 1000, 250, 1000, 1},
|
||||
{ {DATA_SEQUENTIAL, 5,10, 100,200}, 1000, 250, 1000, 1},
|
||||
{ {DATA_SEQUENTIAL, 5,10, 1000,2000}, 1000, 250, 1000, 1},
|
||||
{ {DATA_SEQUENTIAL, 5,100, 10000,20000}, 100, 25, 100, 1},
|
||||
{ {DATA_RANDOM, 10,10, 100,100}, 100000, 1000, 100, 0},
|
||||
{ {DATA_SEQUENTIAL, 10,10, 100,100}, 100000, 1000, 100, 0},
|
||||
};
|
||||
|
||||
int i;
|
||||
int bRecover;
|
||||
|
||||
for(bRecover=0; bRecover<2; bRecover++){
|
||||
if( bRecover==1 && memcmp(zSystem, "lsm", 3) ) break;
|
||||
for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){
|
||||
char *zName = getName(zSystem, bRecover, &aTest[i]);
|
||||
if( testCaseBegin(pRc, zPattern, "%s", zName) ){
|
||||
doDataTest1(zSystem, bRecover, &aTest[i], pRc);
|
||||
}
|
||||
testFree(zName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testCompareDb(
|
||||
Datasource *pData,
|
||||
int nData,
|
||||
int iSeed,
|
||||
TestDb *pControl,
|
||||
TestDb *pDb,
|
||||
int *pRc
|
||||
){
|
||||
int i;
|
||||
|
||||
static int nCall = 0;
|
||||
nCall++;
|
||||
|
||||
testScanCompare(pControl, pDb, 0, 0, 0, 0, 0, pRc);
|
||||
testScanCompare(pControl, pDb, 1, 0, 0, 0, 0, pRc);
|
||||
|
||||
if( *pRc==0 ){
|
||||
int iKey1;
|
||||
int iKey2;
|
||||
void *pKey1; int nKey1; /* Start key */
|
||||
void *pKey2; int nKey2; /* Final key */
|
||||
|
||||
iKey1 = testPrngValue(iSeed) % nData;
|
||||
iKey2 = testPrngValue(iSeed+1) % nData;
|
||||
testDatasourceEntry(pData, iKey1, &pKey2, &nKey1, 0, 0);
|
||||
pKey1 = testMalloc(nKey1+1);
|
||||
memcpy(pKey1, pKey2, nKey1+1);
|
||||
testDatasourceEntry(pData, iKey2, &pKey2, &nKey2, 0, 0);
|
||||
|
||||
testScanCompare(pControl, pDb, 0, 0, 0, pKey2, nKey2, pRc);
|
||||
testScanCompare(pControl, pDb, 0, pKey1, nKey1, 0, 0, pRc);
|
||||
testScanCompare(pControl, pDb, 0, pKey1, nKey1, pKey2, nKey2, pRc);
|
||||
testScanCompare(pControl, pDb, 1, 0, 0, pKey2, nKey2, pRc);
|
||||
testScanCompare(pControl, pDb, 1, pKey1, nKey1, 0, 0, pRc);
|
||||
testScanCompare(pControl, pDb, 1, pKey1, nKey1, pKey2, nKey2, pRc);
|
||||
testFree(pKey1);
|
||||
}
|
||||
|
||||
for(i=0; i<nData && *pRc==0; i++){
|
||||
void *pKey; int nKey;
|
||||
testDatasourceEntry(pData, i, &pKey, &nKey, 0, 0);
|
||||
testFetchCompare(pControl, pDb, pKey, nKey, pRc);
|
||||
}
|
||||
}
|
||||
|
||||
static void doDataTest2(
|
||||
const char *zSystem, /* Database system to test */
|
||||
int bRecover,
|
||||
Datatest2 *p, /* Structure containing test parameters */
|
||||
int *pRc /* OUT: Error code */
|
||||
){
|
||||
TestDb *pDb;
|
||||
TestDb *pControl;
|
||||
Datasource *pData;
|
||||
int i;
|
||||
int rc = LSM_OK;
|
||||
int iDot = 0;
|
||||
|
||||
/* Start the test case, open a database and allocate the datasource. */
|
||||
pDb = testOpen(zSystem, 1, &rc);
|
||||
pData = testDatasourceNew(&p->defn);
|
||||
rc = testControlDb(&pControl);
|
||||
|
||||
if( tdb_lsm(pDb) ){
|
||||
int nBuf = 32 * 1024 * 1024;
|
||||
lsm_config(tdb_lsm(pDb), LSM_CONFIG_AUTOFLUSH, &nBuf);
|
||||
}
|
||||
|
||||
for(i=0; rc==0 && i<p->nIter; i++){
|
||||
void *pKey1; int nKey1;
|
||||
void *pKey2; int nKey2;
|
||||
int ii;
|
||||
int nRange = MIN(p->nIter*p->nWrite, p->nRange);
|
||||
|
||||
for(ii=0; rc==0 && ii<p->nWrite; ii++){
|
||||
int iKey = (i*p->nWrite + ii) % p->nRange;
|
||||
testWriteDatasource(pControl, pData, iKey, &rc);
|
||||
testWriteDatasource(pDb, pData, iKey, &rc);
|
||||
}
|
||||
|
||||
testDatasourceEntry(pData, i+1000000, &pKey1, &nKey1, 0, 0);
|
||||
pKey1 = testMallocCopy(pKey1, nKey1);
|
||||
testDatasourceEntry(pData, i+2000000, &pKey2, &nKey2, 0, 0);
|
||||
|
||||
testDeleteRange(pDb, pKey1, nKey1, pKey2, nKey2, &rc);
|
||||
testDeleteRange(pControl, pKey1, nKey1, pKey2, nKey2, &rc);
|
||||
testFree(pKey1);
|
||||
|
||||
testCompareDb(pData, nRange, i, pControl, pDb, &rc);
|
||||
if( bRecover ){
|
||||
testReopenRecover(&pDb, &rc);
|
||||
}else{
|
||||
testReopen(&pDb, &rc);
|
||||
}
|
||||
testCompareDb(pData, nRange, i, pControl, pDb, &rc);
|
||||
|
||||
/* Update the progress dots... */
|
||||
testCaseProgress(i, p->nIter, testCaseNDot(), &iDot);
|
||||
}
|
||||
|
||||
testClose(&pDb);
|
||||
testClose(&pControl);
|
||||
testDatasourceFree(pData);
|
||||
testCaseFinish(rc);
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
static char *getName2(const char *zSystem, int bRecover, Datatest2 *pTest){
|
||||
char *zRet;
|
||||
char *zData;
|
||||
zData = testDatasourceName(&pTest->defn);
|
||||
zRet = testMallocPrintf("data2.%s.%s.rec=%d.%d.%d.%d",
|
||||
zSystem, zData, bRecover, pTest->nRange, pTest->nWrite, pTest->nIter
|
||||
);
|
||||
testFree(zData);
|
||||
return zRet;
|
||||
}
|
||||
|
||||
void test_data_2(
|
||||
const char *zSystem, /* Database system name */
|
||||
const char *zPattern, /* Run test cases that match this pattern */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
Datatest2 aTest[] = {
|
||||
/* defn, nRange, nWrite, nIter */
|
||||
{ {DATA_RANDOM, 20,25, 100,200}, 10000, 10, 50 },
|
||||
{ {DATA_RANDOM, 20,25, 100,200}, 10000, 200, 50 },
|
||||
{ {DATA_RANDOM, 20,25, 100,200}, 100, 10, 1000 },
|
||||
{ {DATA_RANDOM, 20,25, 100,200}, 100, 200, 50 },
|
||||
};
|
||||
|
||||
int i;
|
||||
int bRecover;
|
||||
|
||||
for(bRecover=0; bRecover<2; bRecover++){
|
||||
if( bRecover==1 && memcmp(zSystem, "lsm", 3) ) break;
|
||||
for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){
|
||||
char *zName = getName2(zSystem, bRecover, &aTest[i]);
|
||||
if( testCaseBegin(pRc, zPattern, "%s", zName) ){
|
||||
doDataTest2(zSystem, bRecover, &aTest[i], pRc);
|
||||
}
|
||||
testFree(zName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
** Test case data3.*
|
||||
*/
|
||||
|
||||
typedef struct Datatest3 Datatest3;
|
||||
struct Datatest3 {
|
||||
int nRange; /* Keys are between 1 and this value, incl. */
|
||||
int nIter; /* Number of iterations */
|
||||
int nWrite; /* Number of writes per iteration */
|
||||
int nDelete; /* Number of deletes per iteration */
|
||||
|
||||
int nValMin; /* Minimum value size for writes */
|
||||
int nValMax; /* Maximum value size for writes */
|
||||
};
|
||||
|
||||
void testPutU32(u8 *aBuf, u32 iVal){
|
||||
aBuf[0] = (iVal >> 24) & 0xFF;
|
||||
aBuf[1] = (iVal >> 16) & 0xFF;
|
||||
aBuf[2] = (iVal >> 8) & 0xFF;
|
||||
aBuf[3] = (iVal >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
void dt3PutKey(u8 *aBuf, int iKey){
|
||||
assert( iKey<100000 && iKey>=0 );
|
||||
sprintf((char *)aBuf, "%.5d", iKey);
|
||||
}
|
||||
|
||||
static void doDataTest3(
|
||||
const char *zSystem, /* Database system to test */
|
||||
Datatest3 *p, /* Structure containing test parameters */
|
||||
int *pRc /* OUT: Error code */
|
||||
){
|
||||
int iDot = 0;
|
||||
int rc = *pRc;
|
||||
TestDb *pDb;
|
||||
u8 *abPresent; /* Array of boolean */
|
||||
char *aVal; /* Buffer to hold values */
|
||||
int i;
|
||||
u32 iSeq = 10; /* prng counter */
|
||||
|
||||
abPresent = (u8 *)testMalloc(p->nRange+1);
|
||||
aVal = (char *)testMalloc(p->nValMax+1);
|
||||
pDb = testOpen(zSystem, 1, &rc);
|
||||
|
||||
for(i=0; i<p->nIter && rc==0; i++){
|
||||
int ii;
|
||||
|
||||
testCaseProgress(i, p->nIter, testCaseNDot(), &iDot);
|
||||
|
||||
/* Perform nWrite inserts */
|
||||
for(ii=0; ii<p->nWrite; ii++){
|
||||
u8 aKey[6];
|
||||
u32 iKey;
|
||||
int nVal;
|
||||
|
||||
iKey = (testPrngValue(iSeq++) % p->nRange) + 1;
|
||||
nVal = (testPrngValue(iSeq++) % (p->nValMax - p->nValMin)) + p->nValMin;
|
||||
testPrngString(testPrngValue(iSeq++), aVal, nVal);
|
||||
dt3PutKey(aKey, iKey);
|
||||
|
||||
testWrite(pDb, aKey, sizeof(aKey)-1, aVal, nVal, &rc);
|
||||
abPresent[iKey] = 1;
|
||||
}
|
||||
|
||||
/* Perform nDelete deletes */
|
||||
for(ii=0; ii<p->nDelete; ii++){
|
||||
u8 aKey1[6];
|
||||
u8 aKey2[6];
|
||||
u32 iKey;
|
||||
|
||||
iKey = (testPrngValue(iSeq++) % p->nRange) + 1;
|
||||
dt3PutKey(aKey1, iKey-1);
|
||||
dt3PutKey(aKey2, iKey+1);
|
||||
|
||||
testDeleteRange(pDb, aKey1, sizeof(aKey1)-1, aKey2, sizeof(aKey2)-1, &rc);
|
||||
abPresent[iKey] = 0;
|
||||
}
|
||||
|
||||
testReopen(&pDb, &rc);
|
||||
|
||||
for(ii=1; rc==0 && ii<=p->nRange; ii++){
|
||||
int nDbVal;
|
||||
void *pDbVal;
|
||||
u8 aKey[6];
|
||||
int dbrc;
|
||||
|
||||
dt3PutKey(aKey, ii);
|
||||
dbrc = tdb_fetch(pDb, aKey, sizeof(aKey)-1, &pDbVal, &nDbVal);
|
||||
testCompareInt(0, dbrc, &rc);
|
||||
|
||||
if( abPresent[ii] ){
|
||||
testCompareInt(1, (nDbVal>0), &rc);
|
||||
}else{
|
||||
testCompareInt(1, (nDbVal<0), &rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testClose(&pDb);
|
||||
testCaseFinish(rc);
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
static char *getName3(const char *zSystem, Datatest3 *p){
|
||||
return testMallocPrintf("data3.%s.%d.%d.%d.%d.(%d..%d)",
|
||||
zSystem, p->nRange, p->nIter, p->nWrite, p->nDelete,
|
||||
p->nValMin, p->nValMax
|
||||
);
|
||||
}
|
||||
|
||||
void test_data_3(
|
||||
const char *zSystem, /* Database system name */
|
||||
const char *zPattern, /* Run test cases that match this pattern */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
Datatest3 aTest[] = {
|
||||
/* nRange, nIter, nWrite, nDelete, nValMin, nValMax */
|
||||
{ 100, 1000, 5, 5, 50, 100 },
|
||||
{ 100, 1000, 2, 2, 5, 10 },
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){
|
||||
char *zName = getName3(zSystem, &aTest[i]);
|
||||
if( testCaseBegin(pRc, zPattern, "%s", zName) ){
|
||||
doDataTest3(zSystem, &aTest[i], pRc);
|
||||
}
|
||||
testFree(zName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
|
||||
/*
|
||||
** This file contains tests related to recovery following application
|
||||
** and system crashes (power failures) while writing to the database.
|
||||
*/
|
||||
|
||||
#include "lsmtest.h"
|
||||
|
||||
/*
|
||||
** Structure used by testCksumDatabase() to accumulate checksum values in.
|
||||
*/
|
||||
typedef struct Cksum Cksum;
|
||||
struct Cksum {
|
||||
int nRow;
|
||||
int cksum1;
|
||||
int cksum2;
|
||||
};
|
||||
|
||||
/*
|
||||
** tdb_scan() callback used by testCksumDatabase()
|
||||
*/
|
||||
static void scanCksumDb(
|
||||
void *pCtx,
|
||||
void *pKey, int nKey,
|
||||
void *pVal, int nVal
|
||||
){
|
||||
Cksum *p = (Cksum *)pCtx;
|
||||
int i;
|
||||
|
||||
p->nRow++;
|
||||
for(i=0; i<nKey; i++){
|
||||
p->cksum1 += ((u8 *)pKey)[i];
|
||||
p->cksum2 += p->cksum1;
|
||||
}
|
||||
for(i=0; i<nVal; i++){
|
||||
p->cksum1 += ((u8 *)pVal)[i];
|
||||
p->cksum2 += p->cksum1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** tdb_scan() callback used by testCountDatabase()
|
||||
*/
|
||||
static void scanCountDb(
|
||||
void *pCtx,
|
||||
void *pKey, int nKey,
|
||||
void *pVal, int nVal
|
||||
){
|
||||
Cksum *p = (Cksum *)pCtx;
|
||||
p->nRow++;
|
||||
|
||||
unused_parameter(pKey);
|
||||
unused_parameter(nKey);
|
||||
unused_parameter(pVal);
|
||||
unused_parameter(nVal);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Iterate through the entire contents of database pDb. Write a checksum
|
||||
** string based on the db contents into buffer zOut before returning. A
|
||||
** checksum string is at most 29 (TEST_CKSUM_BYTES) bytes in size:
|
||||
**
|
||||
** * 32-bit integer (10 bytes)
|
||||
** * 1 space (1 byte)
|
||||
** * 32-bit hex (8 bytes)
|
||||
** * 1 space (1 byte)
|
||||
** * 32-bit hex (8 bytes)
|
||||
** * nul-terminator (1 byte)
|
||||
**
|
||||
** The number of entries in the database is returned.
|
||||
*/
|
||||
int testCksumDatabase(
|
||||
TestDb *pDb, /* Database handle */
|
||||
char *zOut /* Buffer to write checksum to */
|
||||
){
|
||||
Cksum cksum;
|
||||
memset(&cksum, 0, sizeof(Cksum));
|
||||
tdb_scan(pDb, (void *)&cksum, 0, 0, 0, 0, 0, scanCksumDb);
|
||||
sprintf(zOut, "%d %x %x",
|
||||
cksum.nRow, (u32)cksum.cksum1, (u32)cksum.cksum2
|
||||
);
|
||||
assert( strlen(zOut)<TEST_CKSUM_BYTES );
|
||||
return cksum.nRow;
|
||||
}
|
||||
|
||||
int testCountDatabase(TestDb *pDb){
|
||||
Cksum cksum;
|
||||
memset(&cksum, 0, sizeof(Cksum));
|
||||
tdb_scan(pDb, (void *)&cksum, 0, 0, 0, 0, 0, scanCountDb);
|
||||
return cksum.nRow;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is not 0 when it is called.
|
||||
**
|
||||
** Otherwise, the two nul-terminated strings z1 and z1 are compared. If
|
||||
** they are the same, the function returns without doing anything. Otherwise,
|
||||
** an error message is printed, *pRc is set to 1 and the test_failed()
|
||||
** function called.
|
||||
*/
|
||||
void testCompareStr(const char *z1, const char *z2, int *pRc){
|
||||
if( *pRc==0 ){
|
||||
if( strcmp(z1, z2) ){
|
||||
testPrintError("testCompareStr: \"%s\" != \"%s\"\n", z1, z2);
|
||||
*pRc = 1;
|
||||
test_failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is not 0 when it is called.
|
||||
**
|
||||
** Otherwise, the two integers i1 and i2 are compared. If they are equal,
|
||||
** the function returns without doing anything. Otherwise, an error message
|
||||
** is printed, *pRc is set to 1 and the test_failed() function called.
|
||||
*/
|
||||
void testCompareInt(int i1, int i2, int *pRc){
|
||||
if( *pRc==0 && i1!=i2 ){
|
||||
testPrintError("testCompareInt: %d != %d\n", i1, i2);
|
||||
*pRc = 1;
|
||||
test_failed();
|
||||
}
|
||||
}
|
||||
|
||||
void testCaseStart(int *pRc, char *zFmt, ...){
|
||||
va_list ap;
|
||||
va_start(ap, zFmt);
|
||||
vprintf(zFmt, ap);
|
||||
printf(" ...");
|
||||
va_end(ap);
|
||||
*pRc = 0;
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is non-zero when it is called. Zero
|
||||
** is returned in this case.
|
||||
**
|
||||
** Otherwise, the zFmt (a printf style format string) and following arguments
|
||||
** are used to create a test case name. If zPattern is NULL or a glob pattern
|
||||
** that matches the test case name, 1 is returned and the test case started.
|
||||
** Otherwise, zero is returned and the test case does not start.
|
||||
*/
|
||||
int testCaseBegin(int *pRc, const char *zPattern, const char *zFmt, ...){
|
||||
int res = 0;
|
||||
if( *pRc==0 ){
|
||||
char *zTest;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, zFmt);
|
||||
zTest = testMallocVPrintf(zFmt, ap);
|
||||
va_end(ap);
|
||||
if( zPattern==0 || testGlobMatch(zPattern, zTest) ){
|
||||
printf("%-50s ...", zTest);
|
||||
res = 1;
|
||||
}
|
||||
testFree(zTest);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void testCaseFinish(int rc){
|
||||
if( rc==0 ){
|
||||
printf("Ok\n");
|
||||
}else{
|
||||
printf("FAILED\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void testCaseSkip(){
|
||||
printf("Skipped\n");
|
||||
}
|
||||
|
||||
void testSetupSavedLsmdb(
|
||||
const char *zCfg,
|
||||
const char *zFile,
|
||||
Datasource *pData,
|
||||
int nRow,
|
||||
int *pRc
|
||||
){
|
||||
if( *pRc==0 ){
|
||||
int rc;
|
||||
TestDb *pDb;
|
||||
rc = tdb_lsm_open(zCfg, zFile, 1, &pDb);
|
||||
if( rc==0 ){
|
||||
testWriteDatasourceRange(pDb, pData, 0, nRow, &rc);
|
||||
testClose(&pDb);
|
||||
if( rc==0 ) testSaveDb(zFile, "log");
|
||||
}
|
||||
*pRc = rc;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is a no-op if *pRc is non-zero when it is called.
|
||||
**
|
||||
** Open the LSM database identified by zFile and compute its checksum
|
||||
** (a string, as returned by testCksumDatabase()). If the checksum is
|
||||
** identical to zExpect1 or, if it is not NULL, zExpect2, the test passes.
|
||||
** Otherwise, print an error message and set *pRc to 1.
|
||||
*/
|
||||
static void testCompareCksumLsmdb(
|
||||
const char *zFile, /* Path to LSM database */
|
||||
int bCompress, /* True if db is compressed */
|
||||
const char *zExpect1, /* Expected checksum 1 */
|
||||
const char *zExpect2, /* Expected checksum 2 (or NULL) */
|
||||
int *pRc /* IN/OUT: Test case error code */
|
||||
){
|
||||
if( *pRc==0 ){
|
||||
char zCksum[TEST_CKSUM_BYTES];
|
||||
TestDb *pDb;
|
||||
|
||||
*pRc = tdb_lsm_open((bCompress?"compression=1 mmap=0":""), zFile, 0, &pDb);
|
||||
testCksumDatabase(pDb, zCksum);
|
||||
testClose(&pDb);
|
||||
|
||||
if( *pRc==0 ){
|
||||
int r1 = 0;
|
||||
int r2 = -1;
|
||||
|
||||
r1 = strcmp(zCksum, zExpect1);
|
||||
if( zExpect2 ) r2 = strcmp(zCksum, zExpect2);
|
||||
if( r1 && r2 ){
|
||||
if( zExpect2 ){
|
||||
testPrintError("testCompareCksumLsmdb: \"%s\" != (\"%s\" OR \"%s\")",
|
||||
zCksum, zExpect1, zExpect2
|
||||
);
|
||||
}else{
|
||||
testPrintError("testCompareCksumLsmdb: \"%s\" != \"%s\"",
|
||||
zCksum, zExpect1
|
||||
);
|
||||
}
|
||||
*pRc = 1;
|
||||
test_failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
static void testCompareCksumBtdb(
|
||||
const char *zFile, /* Path to LSM database */
|
||||
const char *zExpect1, /* Expected checksum 1 */
|
||||
const char *zExpect2, /* Expected checksum 2 (or NULL) */
|
||||
int *pRc /* IN/OUT: Test case error code */
|
||||
){
|
||||
if( *pRc==0 ){
|
||||
char zCksum[TEST_CKSUM_BYTES];
|
||||
TestDb *pDb;
|
||||
|
||||
*pRc = tdb_open("bt", zFile, 0, &pDb);
|
||||
testCksumDatabase(pDb, zCksum);
|
||||
testClose(&pDb);
|
||||
|
||||
if( *pRc==0 ){
|
||||
int r1 = 0;
|
||||
int r2 = -1;
|
||||
|
||||
r1 = strcmp(zCksum, zExpect1);
|
||||
if( zExpect2 ) r2 = strcmp(zCksum, zExpect2);
|
||||
if( r1 && r2 ){
|
||||
if( zExpect2 ){
|
||||
testPrintError("testCompareCksumLsmdb: \"%s\" != (\"%s\" OR \"%s\")",
|
||||
zCksum, zExpect1, zExpect2
|
||||
);
|
||||
}else{
|
||||
testPrintError("testCompareCksumLsmdb: \"%s\" != \"%s\"",
|
||||
zCksum, zExpect1
|
||||
);
|
||||
}
|
||||
*pRc = 1;
|
||||
test_failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* not used */
|
||||
|
||||
/* Above this point are reusable test routines. Not clear that they
|
||||
** should really be in this file.
|
||||
*************************************************************************/
|
||||
|
||||
/*
|
||||
** This test verifies that if a system crash occurs while doing merge work
|
||||
** on the db, no data is lost.
|
||||
*/
|
||||
static void crash_test1(int bCompress, int *pRc){
|
||||
const char *DBNAME = "testdb.lsm";
|
||||
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 200, 200};
|
||||
|
||||
const int nRow = 5000; /* Database size */
|
||||
const int nIter = 200; /* Number of test iterations */
|
||||
const int nWork = 20; /* Maximum lsm_work() calls per iteration */
|
||||
const int nPage = 15; /* Pages per lsm_work call */
|
||||
|
||||
int i;
|
||||
int iDot = 0;
|
||||
Datasource *pData;
|
||||
CksumDb *pCksumDb;
|
||||
TestDb *pDb;
|
||||
char *zCfg;
|
||||
|
||||
const char *azConfig[2] = {
|
||||
"page_size=1024 block_size=65536 autoflush=16384 safety=2 mmap=0",
|
||||
"page_size=1024 block_size=65536 autoflush=16384 safety=2 "
|
||||
" compression=1 mmap=0"
|
||||
};
|
||||
assert( bCompress==0 || bCompress==1 );
|
||||
|
||||
/* Allocate datasource. And calculate the expected checksums. */
|
||||
pData = testDatasourceNew(&defn);
|
||||
pCksumDb = testCksumArrayNew(pData, nRow, nRow, 1);
|
||||
|
||||
/* Setup and save the initial database. */
|
||||
|
||||
zCfg = testMallocPrintf("%s automerge=7", azConfig[bCompress]);
|
||||
testSetupSavedLsmdb(zCfg, DBNAME, pData, 5000, pRc);
|
||||
testFree(zCfg);
|
||||
|
||||
for(i=0; i<nIter && *pRc==0; i++){
|
||||
int iWork;
|
||||
int testrc = 0;
|
||||
|
||||
testCaseProgress(i, nIter, testCaseNDot(), &iDot);
|
||||
|
||||
/* Restore and open the database. */
|
||||
testRestoreDb(DBNAME, "log");
|
||||
testrc = tdb_lsm_open(azConfig[bCompress], DBNAME, 0, &pDb);
|
||||
assert( testrc==0 );
|
||||
|
||||
/* Call lsm_work() on the db */
|
||||
tdb_lsm_prepare_sync_crash(pDb, 1 + (i%(nWork*2)));
|
||||
for(iWork=0; testrc==0 && iWork<nWork; iWork++){
|
||||
int nWrite = 0;
|
||||
lsm_db *db = tdb_lsm(pDb);
|
||||
testrc = lsm_work(db, 0, nPage, &nWrite);
|
||||
/* assert( testrc!=0 || nWrite>0 ); */
|
||||
if( testrc==0 ) testrc = lsm_checkpoint(db, 0);
|
||||
}
|
||||
tdb_close(pDb);
|
||||
|
||||
/* Check that the database content is still correct */
|
||||
testCompareCksumLsmdb(DBNAME,
|
||||
bCompress, testCksumArrayGet(pCksumDb, nRow), 0, pRc);
|
||||
}
|
||||
|
||||
testCksumArrayFree(pCksumDb);
|
||||
testDatasourceFree(pData);
|
||||
}
|
||||
|
||||
/*
|
||||
** This test verifies that if a system crash occurs while committing a
|
||||
** transaction to the log file, no earlier transactions are lost or damaged.
|
||||
*/
|
||||
static void crash_test2(int bCompress, int *pRc){
|
||||
const char *DBNAME = "testdb.lsm";
|
||||
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 1000, 1000};
|
||||
|
||||
const int nIter = 200;
|
||||
const int nInsert = 20;
|
||||
|
||||
int i;
|
||||
int iDot = 0;
|
||||
Datasource *pData;
|
||||
CksumDb *pCksumDb;
|
||||
TestDb *pDb;
|
||||
|
||||
/* Allocate datasource. And calculate the expected checksums. */
|
||||
pData = testDatasourceNew(&defn);
|
||||
pCksumDb = testCksumArrayNew(pData, 100, 100+nInsert, 1);
|
||||
|
||||
/* Setup and save the initial database. */
|
||||
testSetupSavedLsmdb("", DBNAME, pData, 100, pRc);
|
||||
|
||||
for(i=0; i<nIter && *pRc==0; i++){
|
||||
int iIns;
|
||||
int testrc = 0;
|
||||
|
||||
testCaseProgress(i, nIter, testCaseNDot(), &iDot);
|
||||
|
||||
/* Restore and open the database. */
|
||||
testRestoreDb(DBNAME, "log");
|
||||
testrc = tdb_lsm_open("safety=2", DBNAME, 0, &pDb);
|
||||
assert( testrc==0 );
|
||||
|
||||
/* Insert nInsert records into the database. Crash midway through. */
|
||||
tdb_lsm_prepare_sync_crash(pDb, 1 + (i%(nInsert+2)));
|
||||
for(iIns=0; iIns<nInsert; iIns++){
|
||||
void *pKey; int nKey;
|
||||
void *pVal; int nVal;
|
||||
|
||||
testDatasourceEntry(pData, 100+iIns, &pKey, &nKey, &pVal, &nVal);
|
||||
testrc = tdb_write(pDb, pKey, nKey, pVal, nVal);
|
||||
if( testrc ) break;
|
||||
}
|
||||
tdb_close(pDb);
|
||||
|
||||
/* Check that no data was lost when the system crashed. */
|
||||
testCompareCksumLsmdb(DBNAME, bCompress,
|
||||
testCksumArrayGet(pCksumDb, 100 + iIns),
|
||||
testCksumArrayGet(pCksumDb, 100 + iIns + 1),
|
||||
pRc
|
||||
);
|
||||
}
|
||||
|
||||
testDatasourceFree(pData);
|
||||
testCksumArrayFree(pCksumDb);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This test verifies that if a system crash occurs when checkpointing
|
||||
** the database, data is not lost (assuming that any writes not synced
|
||||
** to the db have been synced into the log file).
|
||||
*/
|
||||
static void crash_test3(int bCompress, int *pRc){
|
||||
const char *DBNAME = "testdb.lsm";
|
||||
const int nIter = 100;
|
||||
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 1000, 1000};
|
||||
|
||||
int i;
|
||||
int iDot = 0;
|
||||
Datasource *pData;
|
||||
CksumDb *pCksumDb;
|
||||
TestDb *pDb;
|
||||
|
||||
/* Allocate datasource. And calculate the expected checksums. */
|
||||
pData = testDatasourceNew(&defn);
|
||||
pCksumDb = testCksumArrayNew(pData, 110, 150, 10);
|
||||
|
||||
/* Setup and save the initial database. */
|
||||
testSetupSavedLsmdb("", DBNAME, pData, 100, pRc);
|
||||
|
||||
for(i=0; i<nIter && *pRc==0; i++){
|
||||
int iOpen;
|
||||
testCaseProgress(i, nIter, testCaseNDot(), &iDot);
|
||||
testRestoreDb(DBNAME, "log");
|
||||
|
||||
for(iOpen=0; iOpen<5; iOpen++){
|
||||
/* Open the database. Insert 10 more records. */
|
||||
pDb = testOpen("lsm", 0, pRc);
|
||||
testWriteDatasourceRange(pDb, pData, 100+iOpen*10, 10, pRc);
|
||||
|
||||
/* Schedule a crash simulation then close the db. */
|
||||
tdb_lsm_prepare_sync_crash(pDb, 1 + (i%2));
|
||||
tdb_close(pDb);
|
||||
|
||||
/* Open the database and check that the crash did not cause any
|
||||
** data loss. */
|
||||
testCompareCksumLsmdb(DBNAME, bCompress,
|
||||
testCksumArrayGet(pCksumDb, 110 + iOpen*10), 0,
|
||||
pRc
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
testDatasourceFree(pData);
|
||||
testCksumArrayFree(pCksumDb);
|
||||
}
|
||||
|
||||
void do_crash_test(const char *zPattern, int *pRc){
|
||||
struct Test {
|
||||
const char *zTest;
|
||||
void (*x)(int, int *);
|
||||
int bCompress;
|
||||
} aTest [] = {
|
||||
{ "crash.lsm.1", crash_test1, 0 },
|
||||
#ifdef HAVE_ZLIB
|
||||
{ "crash.lsm_zip.1", crash_test1, 1 },
|
||||
#endif
|
||||
{ "crash.lsm.2", crash_test2, 0 },
|
||||
{ "crash.lsm.3", crash_test3, 0 },
|
||||
};
|
||||
int i;
|
||||
|
||||
for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){
|
||||
struct Test *p = &aTest[i];
|
||||
if( testCaseBegin(pRc, zPattern, "%s", p->zTest) ){
|
||||
p->x(p->bCompress, pRc);
|
||||
testCaseFinish(*pRc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
|
||||
|
||||
/*
|
||||
** This file contains tests related to the explicit rollback of database
|
||||
** transactions and sub-transactions.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Repeat 2000 times (until the db contains 100,000 entries):
|
||||
**
|
||||
** 1. Open a transaction and insert 500 rows, opening a nested
|
||||
** sub-transaction each 100 rows.
|
||||
**
|
||||
** 2. Roll back to each sub-transaction savepoint. Check the database
|
||||
** checksum looks Ok.
|
||||
**
|
||||
** 3. Every second iteration, roll back the main transaction. Check the
|
||||
** db checksum is correct. Every other iteration, commit the main
|
||||
** transaction (increasing the size of the db by 100 rows).
|
||||
*/
|
||||
|
||||
|
||||
#include "lsmtest.h"
|
||||
|
||||
struct CksumDb {
|
||||
int nFirst;
|
||||
int nLast;
|
||||
int nStep;
|
||||
char **azCksum;
|
||||
};
|
||||
|
||||
CksumDb *testCksumArrayNew(
|
||||
Datasource *pData,
|
||||
int nFirst,
|
||||
int nLast,
|
||||
int nStep
|
||||
){
|
||||
TestDb *pDb;
|
||||
CksumDb *pRet;
|
||||
int i;
|
||||
int nEntry;
|
||||
int rc = 0;
|
||||
|
||||
assert( nLast>=nFirst && ((nLast-nFirst)%nStep)==0 );
|
||||
|
||||
pRet = malloc(sizeof(CksumDb));
|
||||
memset(pRet, 0, sizeof(CksumDb));
|
||||
pRet->nFirst = nFirst;
|
||||
pRet->nLast = nLast;
|
||||
pRet->nStep = nStep;
|
||||
nEntry = 1 + ((nLast - nFirst) / nStep);
|
||||
|
||||
/* Allocate space so that azCksum is an array of nEntry pointers to
|
||||
** buffers each TEST_CKSUM_BYTES in size. */
|
||||
pRet->azCksum = (char **)malloc(nEntry * (sizeof(char *) + TEST_CKSUM_BYTES));
|
||||
for(i=0; i<nEntry; i++){
|
||||
char *pStart = (char *)(&pRet->azCksum[nEntry]);
|
||||
pRet->azCksum[i] = &pStart[i * TEST_CKSUM_BYTES];
|
||||
}
|
||||
|
||||
tdb_open("lsm", "tempdb.lsm", 1, &pDb);
|
||||
testWriteDatasourceRange(pDb, pData, 0, nFirst, &rc);
|
||||
for(i=0; i<nEntry; i++){
|
||||
testCksumDatabase(pDb, pRet->azCksum[i]);
|
||||
if( i==nEntry ) break;
|
||||
testWriteDatasourceRange(pDb, pData, nFirst+i*nStep, nStep, &rc);
|
||||
}
|
||||
|
||||
tdb_close(pDb);
|
||||
|
||||
return pRet;
|
||||
}
|
||||
|
||||
char *testCksumArrayGet(CksumDb *p, int nRow){
|
||||
int i;
|
||||
assert( nRow>=p->nFirst );
|
||||
assert( nRow<=p->nLast );
|
||||
assert( ((nRow-p->nFirst) % p->nStep)==0 );
|
||||
|
||||
i = (nRow - p->nFirst) / p->nStep;
|
||||
return p->azCksum[i];
|
||||
}
|
||||
|
||||
void testCksumArrayFree(CksumDb *p){
|
||||
free(p->azCksum);
|
||||
memset(p, 0x55, sizeof(*p));
|
||||
free(p);
|
||||
}
|
||||
|
||||
/* End of CksumDb code.
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
** Test utility function. Write key-value pair $i from datasource pData
|
||||
** into database pDb.
|
||||
*/
|
||||
void testWriteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){
|
||||
void *pKey; int nKey;
|
||||
void *pVal; int nVal;
|
||||
testDatasourceEntry(pData, i, &pKey, &nKey, &pVal, &nVal);
|
||||
testWrite(pDb, pKey, nKey, pVal, nVal, pRc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Test utility function. Delete datasource pData key $i from database pDb.
|
||||
*/
|
||||
void testDeleteDatasource(TestDb *pDb, Datasource *pData, int i, int *pRc){
|
||||
void *pKey; int nKey;
|
||||
testDatasourceEntry(pData, i, &pKey, &nKey, 0, 0);
|
||||
testDelete(pDb, pKey, nKey, pRc);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function inserts nWrite key/value pairs into database pDb - the
|
||||
** nWrite key value pairs starting at iFirst from data source pData.
|
||||
*/
|
||||
void testWriteDatasourceRange(
|
||||
TestDb *pDb, /* Database to write to */
|
||||
Datasource *pData, /* Data source to read values from */
|
||||
int iFirst, /* Index of first key/value pair */
|
||||
int nWrite, /* Number of key/value pairs to write */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<nWrite; i++){
|
||||
testWriteDatasource(pDb, pData, iFirst+i, pRc);
|
||||
}
|
||||
}
|
||||
|
||||
void testDeleteDatasourceRange(
|
||||
TestDb *pDb, /* Database to write to */
|
||||
Datasource *pData, /* Data source to read keys from */
|
||||
int iFirst, /* Index of first key */
|
||||
int nWrite, /* Number of keys to delete */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<nWrite; i++){
|
||||
testDeleteDatasource(pDb, pData, iFirst+i, pRc);
|
||||
}
|
||||
}
|
||||
|
||||
static char *getName(const char *zSystem){
|
||||
char *zRet;
|
||||
zRet = testMallocPrintf("rollback.%s", zSystem);
|
||||
return zRet;
|
||||
}
|
||||
|
||||
static int rollback_test_1(
|
||||
const char *zSystem,
|
||||
Datasource *pData
|
||||
){
|
||||
const int nRepeat = 100;
|
||||
|
||||
TestDb *pDb;
|
||||
int rc;
|
||||
int i;
|
||||
CksumDb *pCksum;
|
||||
char *zName;
|
||||
|
||||
zName = getName(zSystem);
|
||||
testCaseStart(&rc, zName);
|
||||
testFree(zName);
|
||||
|
||||
pCksum = testCksumArrayNew(pData, 0, nRepeat*100, 100);
|
||||
pDb = 0;
|
||||
rc = tdb_open(zSystem, 0, 1, &pDb);
|
||||
if( pDb && tdb_transaction_support(pDb)==0 ){
|
||||
testCaseSkip();
|
||||
goto skip_rollback_test;
|
||||
}
|
||||
|
||||
for(i=0; i<nRepeat && rc==0; i++){
|
||||
char zCksum[TEST_CKSUM_BYTES];
|
||||
int nCurrent = (((i+1)/2) * 100);
|
||||
int nDbRow;
|
||||
int iTrans;
|
||||
|
||||
/* Check that the database is the expected size. */
|
||||
nDbRow = testCountDatabase(pDb);
|
||||
testCompareInt(nCurrent, nDbRow, &rc);
|
||||
|
||||
for(iTrans=2; iTrans<=6 && rc==0; iTrans++){
|
||||
tdb_begin(pDb, iTrans);
|
||||
testWriteDatasourceRange(pDb, pData, nCurrent, 100, &rc);
|
||||
nCurrent += 100;
|
||||
}
|
||||
|
||||
testCksumDatabase(pDb, zCksum);
|
||||
testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
|
||||
|
||||
for(iTrans=6; iTrans>2 && rc==0; iTrans--){
|
||||
tdb_rollback(pDb, iTrans);
|
||||
nCurrent -= 100;
|
||||
testCksumDatabase(pDb, zCksum);
|
||||
testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
|
||||
}
|
||||
|
||||
if( i%2 ){
|
||||
tdb_rollback(pDb, 0);
|
||||
nCurrent -= 100;
|
||||
testCksumDatabase(pDb, zCksum);
|
||||
testCompareStr(zCksum, testCksumArrayGet(pCksum, nCurrent), &rc);
|
||||
}else{
|
||||
tdb_commit(pDb, 0);
|
||||
}
|
||||
}
|
||||
testCaseFinish(rc);
|
||||
|
||||
skip_rollback_test:
|
||||
tdb_close(pDb);
|
||||
testCksumArrayFree(pCksum);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void test_rollback(
|
||||
const char *zSystem,
|
||||
const char *zPattern,
|
||||
int *pRc
|
||||
){
|
||||
if( *pRc==0 ){
|
||||
int bRun = 1;
|
||||
|
||||
if( zPattern ){
|
||||
char *zName = getName(zSystem);
|
||||
bRun = testGlobMatch(zPattern, zName);
|
||||
testFree(zName);
|
||||
}
|
||||
|
||||
if( bRun ){
|
||||
DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 50, 100 };
|
||||
Datasource *pData = testDatasourceNew(&defn);
|
||||
*pRc = rollback_test_1(zSystem, pData);
|
||||
testDatasourceFree(pData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
/*
|
||||
** This file contains test cases involving multiple database clients.
|
||||
*/
|
||||
|
||||
#include "lsmtest.h"
|
||||
|
||||
/*
|
||||
** The following code implements test cases "mc1.*".
|
||||
**
|
||||
** This test case uses one writer and $nReader readers. All connections
|
||||
** are driven by a single thread. All connections are opened at the start
|
||||
** of the test and remain open until the test is finished.
|
||||
**
|
||||
** The test consists of $nStep steps. Each step the following is performed:
|
||||
**
|
||||
** 1. The writer inserts $nWriteStep records into the db.
|
||||
**
|
||||
** 2. The writer checks that the contents of the db are as expected.
|
||||
**
|
||||
** 3. Each reader that currently has an open read transaction also checks
|
||||
** that the contents of the db are as expected (according to the snapshot
|
||||
** the read transaction is reading - see below).
|
||||
**
|
||||
** After step 1, reader 1 opens a read transaction. After step 2, reader
|
||||
** 2 opens a read transaction, and so on. At step ($nReader+1), reader 1
|
||||
** closes the current read transaction and opens a new one. And so on.
|
||||
** The result is that at step N (for N > $nReader), there exists a reader
|
||||
** with an open read transaction reading the snapshot committed following
|
||||
** steps (N-$nReader-1) to N.
|
||||
*/
|
||||
typedef struct Mctest Mctest;
|
||||
struct Mctest {
|
||||
DatasourceDefn defn; /* Datasource to use */
|
||||
int nStep; /* Total number of steps in test */
|
||||
int nWriteStep; /* Number of rows to insert each step */
|
||||
int nReader; /* Number of read connections */
|
||||
};
|
||||
static void do_mc_test(
|
||||
const char *zSystem, /* Database system to test */
|
||||
Mctest *pTest,
|
||||
int *pRc /* IN/OUT: return code */
|
||||
){
|
||||
const int nDomain = pTest->nStep * pTest->nWriteStep;
|
||||
Datasource *pData; /* Source of data */
|
||||
TestDb *pDb; /* First database connection (writer) */
|
||||
int iReader; /* Used to iterate through aReader */
|
||||
int iStep; /* Current step in test */
|
||||
int iDot = 0; /* Current step in test */
|
||||
|
||||
/* Array of reader connections */
|
||||
struct Reader {
|
||||
TestDb *pDb; /* Connection handle */
|
||||
int iLast; /* Current snapshot contains keys 0..iLast */
|
||||
} *aReader;
|
||||
|
||||
/* Create a data source */
|
||||
pData = testDatasourceNew(&pTest->defn);
|
||||
|
||||
/* Open the writer connection */
|
||||
pDb = testOpen(zSystem, 1, pRc);
|
||||
|
||||
/* Allocate aReader */
|
||||
aReader = (struct Reader *)testMalloc(sizeof(aReader[0]) * pTest->nReader);
|
||||
for(iReader=0; iReader<pTest->nReader; iReader++){
|
||||
aReader[iReader].pDb = testOpen(zSystem, 0, pRc);
|
||||
}
|
||||
|
||||
for(iStep=0; iStep<pTest->nStep; iStep++){
|
||||
int iLast;
|
||||
int iBegin; /* Start read trans using aReader[iBegin] */
|
||||
|
||||
/* Insert nWriteStep more records into the database */
|
||||
int iFirst = iStep*pTest->nWriteStep;
|
||||
testWriteDatasourceRange(pDb, pData, iFirst, pTest->nWriteStep, pRc);
|
||||
|
||||
/* Check that the db is Ok according to the writer */
|
||||
iLast = (iStep+1) * pTest->nWriteStep - 1;
|
||||
testDbContents(pDb, pData, nDomain, 0, iLast, iLast, 1, pRc);
|
||||
|
||||
/* Have reader (iStep % nReader) open a read transaction here. */
|
||||
iBegin = (iStep % pTest->nReader);
|
||||
if( iBegin<iStep ) tdb_commit(aReader[iBegin].pDb, 0);
|
||||
tdb_begin(aReader[iBegin].pDb, 1);
|
||||
aReader[iBegin].iLast = iLast;
|
||||
|
||||
/* Check that the db is Ok for each open reader */
|
||||
for(iReader=0; iReader<pTest->nReader && aReader[iReader].iLast; iReader++){
|
||||
iLast = aReader[iReader].iLast;
|
||||
testDbContents(
|
||||
aReader[iReader].pDb, pData, nDomain, 0, iLast, iLast, 1, pRc
|
||||
);
|
||||
}
|
||||
|
||||
/* Report progress */
|
||||
testCaseProgress(iStep, pTest->nStep, testCaseNDot(), &iDot);
|
||||
}
|
||||
|
||||
/* Close all readers */
|
||||
for(iReader=0; iReader<pTest->nReader; iReader++){
|
||||
testClose(&aReader[iReader].pDb);
|
||||
}
|
||||
testFree(aReader);
|
||||
|
||||
/* Close the writer-connection and free the datasource */
|
||||
testClose(&pDb);
|
||||
testDatasourceFree(pData);
|
||||
}
|
||||
|
||||
|
||||
void test_mc(
|
||||
const char *zSystem, /* Database system name */
|
||||
const char *zPattern, /* Run test cases that match this pattern */
|
||||
int *pRc /* IN/OUT: Error code */
|
||||
){
|
||||
int i;
|
||||
Mctest aTest[] = {
|
||||
{ { TEST_DATASOURCE_RANDOM, 10,10, 100,100 }, 100, 10, 5 },
|
||||
};
|
||||
|
||||
for(i=0; i<ArraySize(aTest); i++){
|
||||
if( testCaseBegin(pRc, zPattern, "mc1.%s.%d", zSystem, i) ){
|
||||
do_mc_test(zSystem, &aTest[i], pRc);
|
||||
testCaseFinish(*pRc);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user