1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Merge latest trunk changes with this branch.

FossilOrigin-Name: 2719cf5c5bbe8e31d18368d54d968af3878ad2e15f0666e18d7b567d7439c451
This commit is contained in:
dan
2017-10-11 20:26:07 +00:00
399 changed files with 56477 additions and 4892 deletions

View File

@ -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 \ 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 \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
random.lo resolve.lo rowset.lo rtree.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 \ table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
update.lo util.lo vacuum.lo \ update.lo util.lo vacuum.lo \
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.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.h \
$(TOP)/ext/rbu/sqlite3rbu.c $(TOP)/ext/rbu/sqlite3rbu.c
SRC += \ SRC += \
$(TOP)/ext/misc/json1.c $(TOP)/ext/misc/json1.c \
$(TOP)/ext/misc/stmt.c
# Generated source code files # Generated source code files
# #
@ -429,6 +430,7 @@ TESTSRC += \
$(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_mi.c \
$(TOP)/ext/fts5/fts5_test_tok.c \ $(TOP)/ext/fts5/fts5_test_tok.c \
$(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/mmapwarm.c \
$(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/nextchar.c \
$(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/regexp.c \
@ -436,6 +438,7 @@ TESTSRC += \
$(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/series.c \
$(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/spellfix.c \
$(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/unionvtab.c \
$(TOP)/ext/misc/wholenumber.c $(TOP)/ext/misc/wholenumber.c
# Source code to the library files needed by the test fixture # Source code to the library files needed by the test fixture
@ -485,7 +488,8 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/async/sqlite3async.c \ $(TOP)/ext/async/sqlite3async.c \
$(TOP)/ext/session/sqlite3session.c $(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/misc/stmt.c
# Header files used by all library source files. # Header files used by all library source files.
# #
@ -566,6 +570,7 @@ SHELL_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS4
# SHELL_OPT += -DSQLITE_ENABLE_FTS5 # SHELL_OPT += -DSQLITE_ENABLE_FTS5
SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS SHELL_OPT += -DSQLITE_ENABLE_EXPLAIN_COMMENTS
SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
SHELL_OPT += -DSQLITE_ENABLE_STMTVTAB
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1 FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5 -DSQLITE_OSS_FUZZ
FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000 FUZZCHECK_OPT += -DSQLITE_MAX_MEMORY=50000000
@ -691,6 +696,11 @@ lemon$(BEXE): $(TOP)/tool/lemon.c $(TOP)/tool/lempar.c
$(BCC) -o $@ $(TOP)/tool/lemon.c $(BCC) -o $@ $(TOP)/tool/lemon.c
cp $(TOP)/tool/lempar.c . cp $(TOP)/tool/lempar.c .
# Rules to build the program that generates the source-id
#
mksourceid$(BEXE): $(TOP)/tool/mksourceid.c
$(BCC) -o $@ $(TOP)/tool/mksourceid.c
# Rules to build individual *.o files from generated *.c files. This # Rules to build individual *.o files from generated *.c files. This
# applies to: # applies to:
# #
@ -956,7 +966,7 @@ parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/tool/addopcodes.tcl
mv parse.h parse.h.temp mv parse.h parse.h.temp
$(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h $(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h
sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
keywordhash.h: $(TOP)/tool/mkkeywordhash.c keywordhash.h: $(TOP)/tool/mkkeywordhash.c
@ -1036,6 +1046,9 @@ sqlite3session.lo: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR)
json1.lo: $(TOP)/ext/misc/json1.c json1.lo: $(TOP)/ext/misc/json1.c
$(LTCOMPILE) -DSQLITE_CORE -c $(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 things
# #
FTS5_SRC = \ FTS5_SRC = \
@ -1085,6 +1098,7 @@ TESTFIXTURE_FLAGS += -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
TESTFIXTURE_FLAGS += -DBUILD_sqlite TESTFIXTURE_FLAGS += -DBUILD_sqlite
TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS += -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS += -DSQLITE_DEFAULT_PAGE_SIZE=1024
TESTFIXTURE_FLAGS += -DSQLITE_ENABLE_STMTVTAB
TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la
TESTFIXTURE_SRC1 = sqlite3.c TESTFIXTURE_SRC1 = sqlite3.c

View File

@ -1294,7 +1294,8 @@ SRC07 = \
$(TOP)\ext\rtree\rtree.c \ $(TOP)\ext\rtree\rtree.c \
$(TOP)\ext\session\sqlite3session.c \ $(TOP)\ext\session\sqlite3session.c \
$(TOP)\ext\rbu\sqlite3rbu.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. # Extension header files, part 1.
# #
@ -1412,6 +1413,7 @@ TESTEXT = \
$(TOP)\ext\fts5\fts5_test_mi.c \ $(TOP)\ext\fts5\fts5_test_mi.c \
$(TOP)\ext\fts5\fts5_test_tok.c \ $(TOP)\ext\fts5\fts5_test_tok.c \
$(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\mmapwarm.c \
$(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\nextchar.c \
$(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\percentile.c \
$(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\regexp.c \
@ -1419,6 +1421,7 @@ TESTEXT = \
$(TOP)\ext\misc\series.c \ $(TOP)\ext\misc\series.c \
$(TOP)\ext\misc\spellfix.c \ $(TOP)\ext\misc\spellfix.c \
$(TOP)\ext\misc\totype.c \ $(TOP)\ext\misc\totype.c \
$(TOP)\ext\misc\unionvtab.c \
$(TOP)\ext\misc\wholenumber.c $(TOP)\ext\misc\wholenumber.c
# Source code to the library files needed by the test fixture # Source code to the library files needed by the test fixture
@ -1507,7 +1510,7 @@ FUZZDATA = \
# when the shell is not being dynamically linked. # when the shell is not being dynamically linked.
# #
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 !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 !ENDIF
# <<mark>> # <<mark>>
@ -1670,6 +1673,12 @@ lemon.exe: $(TOP)\tool\lemon.c lempar.c
$(BCC) $(NO_WARN) -Daccess=_access \ $(BCC) $(NO_WARN) -Daccess=_access \
-Fe$@ $(TOP)\tool\lemon.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS) -Fe$@ $(TOP)\tool\lemon.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS)
# <<mark>>
# Rules to build the source-id generator tool
#
mksourceid.exe: $(TOP)\tool\mksourceid.c
$(BCC) $(NO_WARN) -Fe$@ $(TOP)\tool\mksourceid.c /link $(LDFLAGS) $(NLTLINKOPTS) $(NLTLIBPATHS)
# Rules to build individual *.lo files from generated *.c files. This # Rules to build individual *.lo files from generated *.c files. This
# applies to: # applies to:
# #
@ -1948,7 +1957,7 @@ parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl
move parse.h parse.h.temp move parse.h parse.h.temp
$(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h $(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h
$(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION $(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION
$(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS) $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS)
sqlite3ext.h: .target_source sqlite3ext.h: .target_source
@ -2091,6 +2100,7 @@ TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE=""
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN) TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN)
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_DEFAULT_PAGE_SIZE=1024
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_ENABLE_STMTVTAB
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS) TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) $(TEST_CCONV_OPTS)
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2)
@ -2247,6 +2257,9 @@ rbu.exe: $(TOP)\ext\rbu\rbu.c $(TOP)\ext\rbu\sqlite3rbu.c $(SQLITE3C) $(SQLITE3H
$(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \ $(LTLINK) $(NO_WARN) -DSQLITE_ENABLE_RBU \
$(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(TOP)\ext\rbu\rbu.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
LSMDIR=$(TOP)\ext\lsm1
!INCLUDE $(LSMDIR)\Makefile.msc
moreclean: clean moreclean: clean
del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL del /Q $(SQLITE3C) $(SQLITE3H) 2>NUL
# <</mark>> # <</mark>>
@ -2259,13 +2272,14 @@ clean:
del /Q sqlite3.c sqlite3.h 2>NUL del /Q sqlite3.c sqlite3.h 2>NUL
del /Q opcodes.c opcodes.h 2>NUL del /Q opcodes.c opcodes.h 2>NUL
del /Q lemon.* lempar.c parse.* 2>NUL del /Q lemon.* lempar.c parse.* 2>NUL
del /Q mkkeywordhash.* keywordhash.h 2>NUL del /Q mksourceid.* mkkeywordhash.* keywordhash.h 2>NUL
del /Q notasharedlib.* 2>NUL del /Q notasharedlib.* 2>NUL
-rmdir /Q/S .deps 2>NUL -rmdir /Q/S .deps 2>NUL
-rmdir /Q/S .libs 2>NUL -rmdir /Q/S .libs 2>NUL
-rmdir /Q/S tsrc 2>NUL -rmdir /Q/S tsrc 2>NUL
del /Q .target_source 2>NUL del /Q .target_source 2>NUL
del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 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 testloadext.dll 2>NUL
del /Q testfixture.exe test.db 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 del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL

View File

@ -34,7 +34,9 @@ archives as follows:
If you do want to use Fossil to check out the source tree, If you do want to use Fossil to check out the source tree,
first install Fossil version 2.0 or later. first install Fossil version 2.0 or later.
(Source tarballs and precompiled binaries available (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: Then run commands like this:
mkdir ~/sqlite mkdir ~/sqlite
@ -106,19 +108,22 @@ The makefiles also require AWK.
## Source Code Tour ## Source Code Tour
Most of the core source files are in the **src/** subdirectory. But Most of the core source files are in the **src/** subdirectory. The
src/ also contains files used to build the "testfixture" test harness; **src/** folder also contains files used to build the "testfixture" test
those file all begin with "test". And src/ contains the "shell.c" file harness. The names of the source files used by "testfixture" all begin
which is the main program for the "sqlite3.exe" command-line shell and with "test".
the "tclsqlite.c" file which implements the bindings to SQLite from the The **src/** also contains the "shell.c" file
Tcl programming language. (Historical note: SQLite began as a Tcl 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.) extension and only later escaped to the wild as an independent library.)
Test scripts and programs are found in the **test/** subdirectory. Test scripts and programs are found in the **test/** subdirectory.
There are other test suites for SQLite (see Addtional test code is found in other source repositories.
[How SQLite Is Tested](http://www.sqlite.org/testing.html)) See [How SQLite Is Tested](http://www.sqlite.org/testing.html) for
but those other test suites are additional information.
in separate source repositories.
The **ext/** subdirectory contains code for extensions. The The **ext/** subdirectory contains code for extensions. The
Full-text search engine is in **ext/fts3**. The R-Tree engine is in 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 The SQLite interface is defined by the **sqlite3.h** header file, which is
generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The generated from src/sqlite.h.in, ./manifest.uuid, and ./VERSION. The
[Tcl script](http://www.tcl.tk) at tool/mksqlite3h.tcl does the conversion. [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 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 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 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 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 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 by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code
for lemon is at tool/lemon.c. Lemon uses a for lemon is at tool/lemon.c. Lemon uses the tool/lempar.c file as a
template for generating its parser. A generic template is in tool/lempar.c, template for generating its parser.
but SQLite uses a slightly modified template found in src/lempar.c.
Lemon also generates the **parse.h** header file, at the same time it Lemon also generates the **parse.h** header file, at the same time it
generates parse.c. But the parse.h header file is 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 the numeric codes used by the parse.c parser. The keywordhash.h file is
generated by a C-language program at tool mkkeywordhash.c. 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 ### The Amalgamation
All of the individual C source code and header files (both manually-edited 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 tool/mksqlite3c.tcl script is run to copy them all together in just the
right order while resolving internal "#include" references. 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 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, 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 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 for details. Other documents that are useful in
(helping to understand how SQLite works include the (helping to understand how SQLite works include the
[file format](http://www.sqlite.org/fileformat2.html) description, [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 prepared statements, the description of
[how transactions work](http://www.sqlite.org/atomiccommit.html), and [how transactions work](http://www.sqlite.org/atomiccommit.html), and
the [overview of the query planner](http://www.sqlite.org/optoverview.html). 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 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: Key files:
@ -227,7 +239,7 @@ Key files:
* **sqliteInt.h** - this header file defines many of the data objects * **sqliteInt.h** - this header file defines many of the data objects
used internally by SQLite. 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 to parse SQL statements, and the actions that are taken at each step
in the parsing process. 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 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. 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. describes its purpose and role within the larger system.
## Contacts ## Contacts
The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/) 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://www2.sqlite.org/](http://www2.sqlite.org) and
[http://www3.sqlite.org/](http://www3.sqlite.org). [http://www3.sqlite.org/](http://www3.sqlite.org).

View File

@ -1 +1 @@
3.19.0 3.21.0

View File

@ -927,7 +927,7 @@ LIBRESOBJS =
# when the shell is not being dynamically linked. # when the shell is not being dynamically linked.
# #
!IF $(DYNAMIC_SHELL)==0 && $(FOR_WIN10)==0 !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 !ENDIF

84
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # 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.21.0.
# #
# #
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='sqlite' PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite' PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.19.0' PACKAGE_VERSION='3.21.0'
PACKAGE_STRING='sqlite 3.19.0' PACKAGE_STRING='sqlite 3.21.0'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='' PACKAGE_URL=''
@ -909,6 +909,7 @@ enable_fts3
enable_fts4 enable_fts4
enable_fts5 enable_fts5
enable_json1 enable_json1
enable_update_limit
enable_rtree enable_rtree
enable_session enable_session
enable_gcov enable_gcov
@ -1463,7 +1464,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures sqlite 3.19.0 to adapt to many kinds of systems. \`configure' configures sqlite 3.21.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1528,7 +1529,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.19.0:";; short | recursive ) echo "Configuration of sqlite 3.21.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1560,6 +1561,7 @@ Optional Features:
--enable-fts4 Enable the FTS4 extension --enable-fts4 Enable the FTS4 extension
--enable-fts5 Enable the FTS5 extension --enable-fts5 Enable the FTS5 extension
--enable-json1 Enable the JSON1 extension --enable-json1 Enable the JSON1 extension
--enable-update-limit Enable the UPDATE/DELETE LIMIT clause
--enable-rtree Enable the RTREE extension --enable-rtree Enable the RTREE extension
--enable-session Enable the SESSION extension --enable-session Enable the SESSION extension
--enable-gcov Enable coverage testing using gcov --enable-gcov Enable coverage testing using gcov
@ -1652,7 +1654,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
sqlite configure 3.19.0 sqlite configure 3.21.0
generated by GNU Autoconf 2.69 generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc. Copyright (C) 2012 Free Software Foundation, Inc.
@ -2071,7 +2073,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. 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.21.0, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@ $ $0 $@
@ -3929,13 +3931,13 @@ if ${lt_cv_nm_interface+:} false; then :
else else
lt_cv_nm_interface="BSD nm" lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:3932: $ac_compile\"" >&5) (eval echo "\"\$as_me:3934: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err) (eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5 cat conftest.err >&5
(eval echo "\"\$as_me:3935: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval echo "\"\$as_me:3937: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5 cat conftest.err >&5
(eval echo "\"\$as_me:3938: output\"" >&5) (eval echo "\"\$as_me:3940: output\"" >&5)
cat conftest.out >&5 cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin" lt_cv_nm_interface="MS dumpbin"
@ -5141,7 +5143,7 @@ ia64-*-hpux*)
;; ;;
*-*-irix6*) *-*-irix6*)
# Find out which ABI we are using. # Find out which ABI we are using.
echo '#line 5144 "configure"' > conftest.$ac_ext echo '#line 5146 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5 (eval $ac_compile) 2>&5
ac_status=$? ac_status=$?
@ -6666,11 +6668,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:6669: $lt_compile\"" >&5) (eval echo "\"\$as_me:6671: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "$as_me:6673: \$? = $ac_status" >&5 echo "$as_me:6675: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -7005,11 +7007,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7008: $lt_compile\"" >&5) (eval echo "\"\$as_me:7010: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err) (eval "$lt_compile" 2>conftest.err)
ac_status=$? ac_status=$?
cat conftest.err >&5 cat conftest.err >&5
echo "$as_me:7012: \$? = $ac_status" >&5 echo "$as_me:7014: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output. # So say no if there are warnings other than the usual output.
@ -7110,11 +7112,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7113: $lt_compile\"" >&5) (eval echo "\"\$as_me:7115: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "$as_me:7117: \$? = $ac_status" >&5 echo "$as_me:7119: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
@ -7165,11 +7167,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'` -e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7168: $lt_compile\"" >&5) (eval echo "\"\$as_me:7170: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err) (eval "$lt_compile" 2>out/conftest.err)
ac_status=$? ac_status=$?
cat out/conftest.err >&5 cat out/conftest.err >&5
echo "$as_me:7172: \$? = $ac_status" >&5 echo "$as_me:7174: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext if (exit $ac_status) && test -s out/conftest2.$ac_objext
then then
# The compiler can only warn and ignore the option if not recognized # The compiler can only warn and ignore the option if not recognized
@ -9545,7 +9547,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF cat > conftest.$ac_ext <<_LT_EOF
#line 9548 "configure" #line 9550 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -9641,7 +9643,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF cat > conftest.$ac_ext <<_LT_EOF
#line 9644 "configure" #line 9646 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -10302,7 +10304,7 @@ USE_AMALGAMATION=1
# if not, then we fall back to plain tclsh. # if not, then we fall back to plain tclsh.
# TODO: try other versions before falling back? # TODO: try other versions before falling back?
# #
for ac_prog in tclsh8.6 tclsh8.5 tclsh for ac_prog in tclsh8.7 tclsh8.6 tclsh8.5 tclsh
do do
# Extract the first word of "$ac_prog", so it can be a program name with args. # Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2 set dummy $ac_prog; ac_word=$2
@ -11356,7 +11358,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS5" >&5
$as_echo_n "checking whether to support MEMSYS5... " >&6; } $as_echo_n "checking whether to support MEMSYS5... " >&6; }
if test "${enable_memsys5}" = "yes"; then 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 "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; } $as_echo "yes" >&6; }
else else
@ -11373,7 +11375,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to support MEMSYS3" >&5
$as_echo_n "checking whether to support MEMSYS3... " >&6; } $as_echo_n "checking whether to support MEMSYS3... " >&6; }
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then 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 "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; } $as_echo "yes" >&6; }
else else
@ -11391,7 +11393,7 @@ else
fi fi
if test "${enable_fts3}" = "yes" ; then 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 fi
# Check whether --enable-fts4 was given. # Check whether --enable-fts4 was given.
if test "${enable_fts4+set}" = set; then : if test "${enable_fts4+set}" = set; then :
@ -11401,7 +11403,7 @@ else
fi fi
if test "${enable_fts4}" = "yes" ; then 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 "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
$as_echo_n "checking for library containing log... " >&6; } $as_echo_n "checking for library containing log... " >&6; }
if ${ac_cv_search_log+:} false; then : if ${ac_cv_search_log+:} false; then :
@ -11467,7 +11469,7 @@ else
fi fi
if test "${enable_fts5}" = "yes" ; then 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 "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
$as_echo_n "checking for library containing log... " >&6; } $as_echo_n "checking for library containing log... " >&6; }
if ${ac_cv_search_log+:} false; then : if ${ac_cv_search_log+:} false; then :
@ -11536,7 +11538,21 @@ else
fi fi
if test "${enable_json1}" = "yes" ; then 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
#########
# See whether we should enable the LIMIT clause on UPDATE and DELETE
# statements.
# Check whether --enable-update-limit was given.
if test "${enable_update_limit+set}" = set; then :
enableval=$enable_update_limit; enable_udlimit=yes
else
enable_udlimit=no
fi
if test "${enable_udlimit}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
fi fi
######### #########
@ -11549,7 +11565,7 @@ else
fi fi
if test "${enable_rtree}" = "yes" ; then 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 fi
######### #########
@ -11562,12 +11578,12 @@ else
fi fi
if test "${enable_session}" = "yes" ; then if test "${enable_session}" = "yes" ; then
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION" 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_PREUPDATE_HOOK"
fi 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 for option in $CFLAGS $CPPFLAGS
do do
case $option in case $option in
@ -12151,7 +12167,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by sqlite $as_me 3.19.0, which was This file was extended by sqlite $as_me 3.21.0, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -12217,7 +12233,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
sqlite config.status 3.19.0 sqlite config.status 3.21.0
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -120,7 +120,7 @@ USE_AMALGAMATION=1
# if not, then we fall back to plain tclsh. # if not, then we fall back to plain tclsh.
# TODO: try other versions before falling back? # TODO: try other versions before falling back?
# #
AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.6 tclsh8.5 tclsh], none) AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.7 tclsh8.6 tclsh8.5 tclsh], none)
if test "$TCLSH_CMD" = "none"; then if test "$TCLSH_CMD" = "none"; then
# If we can't find a local tclsh, then building the amalgamation will fail. # If we can't find a local tclsh, then building the amalgamation will fail.
# We act as though --disable-amalgamation has been used. # We act as though --disable-amalgamation has been used.
@ -596,7 +596,7 @@ AC_ARG_ENABLE(memsys5,
[enable_memsys5=yes],[enable_memsys5=no]) [enable_memsys5=yes],[enable_memsys5=no])
AC_MSG_CHECKING([whether to support MEMSYS5]) AC_MSG_CHECKING([whether to support MEMSYS5])
if test "${enable_memsys5}" = "yes"; then 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]) AC_MSG_RESULT([yes])
else else
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
@ -606,7 +606,7 @@ AC_ARG_ENABLE(memsys3,
[enable_memsys3=yes],[enable_memsys3=no]) [enable_memsys3=yes],[enable_memsys3=no])
AC_MSG_CHECKING([whether to support MEMSYS3]) AC_MSG_CHECKING([whether to support MEMSYS3])
if test "${enable_memsys3}" = "yes" -a "${enable_memsys5}" = "no"; then 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]) AC_MSG_RESULT([yes])
else else
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
@ -618,20 +618,20 @@ AC_ARG_ENABLE(fts3, AC_HELP_STRING([--enable-fts3],
[Enable the FTS3 extension]), [Enable the FTS3 extension]),
[enable_fts3=yes],[enable_fts3=no]) [enable_fts3=yes],[enable_fts3=no])
if test "${enable_fts3}" = "yes" ; then 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 fi
AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4], AC_ARG_ENABLE(fts4, AC_HELP_STRING([--enable-fts4],
[Enable the FTS4 extension]), [Enable the FTS4 extension]),
[enable_fts4=yes],[enable_fts4=no]) [enable_fts4=yes],[enable_fts4=no])
if test "${enable_fts4}" = "yes" ; then 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]) AC_SEARCH_LIBS([log],[m])
fi fi
AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5], AC_ARG_ENABLE(fts5, AC_HELP_STRING([--enable-fts5],
[Enable the FTS5 extension]), [Enable the FTS5 extension]),
[enable_fts5=yes],[enable_fts5=no]) [enable_fts5=yes],[enable_fts5=no])
if test "${enable_fts5}" = "yes" ; then 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]) AC_SEARCH_LIBS([log],[m])
fi fi
@ -641,7 +641,17 @@ AC_ARG_ENABLE(json1, AC_HELP_STRING([--enable-json1],
[Enable the JSON1 extension]), [Enable the JSON1 extension]),
[enable_json1=yes],[enable_json1=no]) [enable_json1=yes],[enable_json1=no])
if test "${enable_json1}" = "yes" ; then 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
#########
# See whether we should enable the LIMIT clause on UPDATE and DELETE
# statements.
AC_ARG_ENABLE(update-limit, AC_HELP_STRING([--enable-update-limit],
[Enable the UPDATE/DELETE LIMIT clause]),
[enable_udlimit=yes],[enable_udlimit=no])
if test "${enable_udlimit}" = "yes" ; then
OPT_FEATURE_FLAGS="${OPT_FEATURE_FLAGS} -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT"
fi fi
######### #########
@ -650,7 +660,7 @@ AC_ARG_ENABLE(rtree, AC_HELP_STRING([--enable-rtree],
[Enable the RTREE extension]), [Enable the RTREE extension]),
[enable_rtree=yes],[enable_rtree=no]) [enable_rtree=yes],[enable_rtree=no])
if test "${enable_rtree}" = "yes" ; then 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 fi
######### #########
@ -659,12 +669,12 @@ AC_ARG_ENABLE(session, AC_HELP_STRING([--enable-session],
[Enable the SESSION extension]), [Enable the SESSION extension]),
[enable_session=yes],[enable_session=no]) [enable_session=yes],[enable_session=no])
if test "${enable_session}" = "yes" ; then if test "${enable_session}" = "yes" ; then
OPT_FEATURE_FLAGS="$(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_SESSION" 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_PREUPDATE_HOOK"
fi 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 for option in $CFLAGS $CPPFLAGS
do do
case $option in case $option in

View File

@ -2,12 +2,12 @@
<head> <head>
<title>The Lemon Parser Generator</title> <title>The Lemon Parser Generator</title>
</head> </head>
<body bgcolor=white> <body bgcolor='white'>
<h1 align=center>The Lemon Parser Generator</h1> <h1 align='center'>The Lemon Parser Generator</h1>
<p>Lemon is an LALR(1) parser generator for C. <p>Lemon is an LALR(1) parser generator for C.
It does the same job as "bison" and "yacc". It does the same job as "bison" and "yacc".
But lemon is not a bison or yacc clone. Lemon But Lemon is not a bison or yacc clone. Lemon
uses a different grammar syntax which is designed to uses a different grammar syntax which is designed to
reduce the number of coding errors. Lemon also uses a reduce the number of coding errors. Lemon also uses a
parsing engine that is faster than yacc and parsing engine that is faster than yacc and
@ -16,13 +16,33 @@ bison and which is both reentrant and threadsafe.
has also been updated so that it too can generate a has also been updated so that it too can generate a
reentrant and threadsafe parser.) reentrant and threadsafe parser.)
Lemon also implements features that can be used Lemon also implements features that can be used
to eliminate resource leaks, making is suitable for use to eliminate resource leaks, making it suitable for use
in long-running programs such as graphical user interfaces in long-running programs such as graphical user interfaces
or embedded controllers.</p> or embedded controllers.</p>
<p>This document is an introduction to the Lemon <p>This document is an introduction to the Lemon
parser generator.</p> 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 &rarr; Robust and secure
<li>The "lemon.exe" command line tool itself &rarr; Not so much
</ul>
<h2>Theory of Operation</h2> <h2>Theory of Operation</h2>
<p>The main goal of Lemon is to translate a context free grammar (CFG) <p>The main goal of Lemon is to translate a context free grammar (CFG)
@ -38,8 +58,8 @@ Lemon comes with a default parser template which works fine for most
applications. But the user is free to substitute a different parser applications. But the user is free to substitute a different parser
template if desired.</p> template if desired.</p>
<p>Depending on command-line options, Lemon will generate between <p>Depending on command-line options, Lemon will generate up to
one and three files of outputs. three output files.
<ul> <ul>
<li>C code to implement the parser. <li>C code to implement the parser.
<li>A header file defining an integer ID for each terminal symbol. <li>A header file defining an integer ID for each terminal symbol.
@ -70,17 +90,20 @@ the states used by the parser automaton.</p>
You can obtain a list of the available command-line options together You can obtain a list of the available command-line options together
with a brief explanation of what each does by typing with a brief explanation of what each does by typing
<pre> <pre>
lemon -? lemon "-?"
</pre> </pre>
As of this writing, the following command-line options are supported: As of this writing, the following command-line options are supported:
<ul> <ul>
<li><b>-b</b> <li><b>-b</b>
Show only the basis for each parser state in the report file. Show only the basis for each parser state in the report file.
<li><b>-c</b> <li><b>-c</b>
Do not compress the generated action tables. Do not compress the generated action tables. The parser will be a
little larger and slower, but it will detect syntax errors sooner.
<li><b>-D<i>name</i></b> <li><b>-D<i>name</i></b>
Define C preprocessor macro <i>name</i>. This macro is useable by Define C preprocessor macro <i>name</i>. This macro is usable by
"%ifdef" lines in the grammar file. "<tt><a href='#pifdef'>%ifdef</a></tt>" and
"<tt><a href='#pifdef'>%ifndef</a></tt>" lines
in the grammar file.
<li><b>-g</b> <li><b>-g</b>
Do not generate a parser. Instead write the input grammar to standard Do not generate a parser. Instead write the input grammar to standard
output with all comments, actions, and other extraneous text removed. output with all comments, actions, and other extraneous text removed.
@ -88,9 +111,9 @@ output with all comments, actions, and other extraneous text removed.
Omit "#line" directives in the generated parser C code. Omit "#line" directives in the generated parser C code.
<li><b>-m</b> <li><b>-m</b>
Cause the output C source code to be compatible with the "makeheaders" Cause the output C source code to be compatible with the "makeheaders"
program. program.
<li><b>-p</b> <li><b>-p</b>
Display all conflicts that are resolved by Display all conflicts that are resolved by
<a href='#precrules'>precedence rules</a>. <a href='#precrules'>precedence rules</a>.
<li><b>-q</b> <li><b>-q</b>
Suppress generation of the report file. Suppress generation of the report file.
@ -145,7 +168,7 @@ once for each token:
</pre> </pre>
The first argument to the Parse() routine is the pointer returned by The first argument to the Parse() routine is the pointer returned by
ParseAlloc(). ParseAlloc().
The second argument is a small positive integer that tells the parse the The second argument is a small positive integer that tells the parser the
type of the next token in the data stream. type of the next token in the data stream.
There is one token type for each terminal symbol in the grammar. There is one token type for each terminal symbol in the grammar.
The gram.h file generated by Lemon contains #define statements that The gram.h file generated by Lemon contains #define statements that
@ -153,7 +176,7 @@ map symbolic terminal symbol names into appropriate integer values.
A value of 0 for the second argument is a special flag to the A value of 0 for the second argument is a special flag to the
parser to indicate that the end of input has been reached. parser to indicate that the end of input has been reached.
The third argument is the value of the given token. By default, The third argument is the value of the given token. By default,
the type of the third argument is integer, but the grammar will the type of the third argument is "void*", but the grammar will
usually redefine this type to be some kind of structure. usually redefine this type to be some kind of structure.
Typically the second argument will be a broad category of tokens Typically the second argument will be a broad category of tokens
such as "identifier" or "number" and the third argument will such as "identifier" or "number" and the third argument will
@ -161,7 +184,7 @@ be the name of the identifier or the value of the number.</p>
<p>The Parse() function may have either three or four arguments, <p>The Parse() function may have either three or four arguments,
depending on the grammar. If the grammar specification file requests depending on the grammar. If the grammar specification file requests
it (via the <a href='#extraarg'><tt>extra_argument</tt> directive</a>), it (via the <tt><a href='#extraarg'>%extra_argument</a></tt> directive),
the Parse() function will have a fourth parameter that can be the Parse() function will have a fourth parameter that can be
of any type chosen by the programmer. The parser doesn't do anything of any type chosen by the programmer. The parser doesn't do anything
with this argument except to pass it through to action routines. with this argument except to pass it through to action routines.
@ -171,20 +194,20 @@ to the action routines without having to use global variables.</p>
<p>A typical use of a Lemon parser might look something like the <p>A typical use of a Lemon parser might look something like the
following: following:
<pre> <pre>
01 ParseTree *ParseFile(const char *zFilename){ 1 ParseTree *ParseFile(const char *zFilename){
02 Tokenizer *pTokenizer; 2 Tokenizer *pTokenizer;
03 void *pParser; 3 void *pParser;
04 Token sToken; 4 Token sToken;
05 int hTokenId; 5 int hTokenId;
06 ParserState sState; 6 ParserState sState;
07 7
08 pTokenizer = TokenizerCreate(zFilename); 8 pTokenizer = TokenizerCreate(zFilename);
09 pParser = ParseAlloc( malloc ); 9 pParser = ParseAlloc( malloc );
10 InitParserState(&sState); 10 InitParserState(&amp;sState);
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){ 11 while( GetNextToken(pTokenizer, &amp;hTokenId, &amp;sToken) ){
12 Parse(pParser, hTokenId, sToken, &sState); 12 Parse(pParser, hTokenId, sToken, &amp;sState);
13 } 13 }
14 Parse(pParser, 0, sToken, &sState); 14 Parse(pParser, 0, sToken, &amp;sState);
15 ParseFree(pParser, free ); 15 ParseFree(pParser, free );
16 TokenizerFree(pTokenizer); 16 TokenizerFree(pTokenizer);
17 return sState.treeRoot; 17 return sState.treeRoot;
@ -197,10 +220,10 @@ simple.)
We assume the existence of some kind of tokenizer which is created We assume the existence of some kind of tokenizer which is created
using TokenizerCreate() on line 8 and deleted by TokenizerFree() using TokenizerCreate() on line 8 and deleted by TokenizerFree()
on line 16. The GetNextToken() function on line 11 retrieves the on line 16. The GetNextToken() function on line 11 retrieves the
next token from the input file and puts its type in the next token from the input file and puts its type in the
integer variable hTokenId. The sToken variable is assumed to be integer variable hTokenId. The sToken variable is assumed to be
some kind of structure that contains details about each token, some kind of structure that contains details about each token,
such as its complete text, what line it occurs on, etc. </p> such as its complete text, what line it occurs on, etc.</p>
<p>This example also assumes the existence of structure of type <p>This example also assumes the existence of structure of type
ParserState that holds state information about a particular parse. ParserState that holds state information about a particular parse.
@ -217,7 +240,7 @@ tree.</p>
<pre> <pre>
ParseFile(){ ParseFile(){
pParser = ParseAlloc( malloc ); pParser = ParseAlloc( malloc );
while( GetNextToken(pTokenizer,&hTokenId, &sToken) ){ while( GetNextToken(pTokenizer,&amp;hTokenId, &amp;sToken) ){
Parse(pParser, hTokenId, sToken); Parse(pParser, hTokenId, sToken);
} }
Parse(pParser, 0, sToken); Parse(pParser, 0, sToken);
@ -277,25 +300,25 @@ specifies additional information Lemon requires to do its job.
Most of the work in using Lemon is in writing an appropriate Most of the work in using Lemon is in writing an appropriate
grammar file.</p> grammar file.</p>
<p>The grammar file for lemon is, for the most part, free format. <p>The grammar file for Lemon is, for the most part, free format.
It does not have sections or divisions like yacc or bison. Any It does not have sections or divisions like yacc or bison. Any
declaration can occur at any point in the file. declaration can occur at any point in the file.
Lemon ignores whitespace (except where it is needed to separate Lemon ignores whitespace (except where it is needed to separate
tokens) and it honors the same commenting conventions as C and C++.</p> tokens), and it honors the same commenting conventions as C and C++.</p>
<h3>Terminals and Nonterminals</h3> <h3>Terminals and Nonterminals</h3>
<p>A terminal symbol (token) is any string of alphanumeric <p>A terminal symbol (token) is any string of alphanumeric
and/or underscore characters and/or underscore characters
that begins with an upper case letter. that begins with an uppercase letter.
A terminal can contain lowercase letters after the first character, A terminal can contain lowercase letters after the first character,
but the usual convention is to make terminals all upper case. but the usual convention is to make terminals all uppercase.
A nonterminal, on the other hand, is any string of alphanumeric A nonterminal, on the other hand, is any string of alphanumeric
and underscore characters than begins with a lower case letter. and underscore characters than begins with a lowercase letter.
Again, the usual convention is to make nonterminals use all lower Again, the usual convention is to make nonterminals use all lowercase
case letters.</p> letters.</p>
<p>In Lemon, terminal and nonterminal symbols do not need to <p>In Lemon, terminal and nonterminal symbols do not need to
be declared or identified in a separate section of the grammar file. be declared or identified in a separate section of the grammar file.
Lemon is able to generate a list of all terminals and nonterminals Lemon is able to generate a list of all terminals and nonterminals
by examining the grammar rules, and it can always distinguish a by examining the grammar rules, and it can always distinguish a
@ -319,7 +342,8 @@ The list of terminals and nonterminals on the right-hand side of the
rule can be empty. rule can be empty.
Rules can occur in any order, except that the left-hand side of the Rules can occur in any order, except that the left-hand side of the
first rule is assumed to be the start symbol for the grammar (unless first rule is assumed to be the start symbol for the grammar (unless
specified otherwise using the <tt>%start</tt> directive described below.) specified otherwise using the <tt><a href='#start_symbol'>%start_symbol</a></tt>
directive described below.)
A typical sequence of grammar rules might look something like this: A typical sequence of grammar rules might look something like this:
<pre> <pre>
expr ::= expr PLUS expr. expr ::= expr PLUS expr.
@ -362,7 +386,7 @@ names to each symbol in a grammar rule and then using those symbolic
names in the action. names in the action.
In yacc or bison, one would write this: In yacc or bison, one would write this:
<pre> <pre>
expr -> expr PLUS expr { $$ = $1 + $3; }; expr -&gt; expr PLUS expr { $$ = $1 + $3; };
</pre> </pre>
But in Lemon, the same rule becomes the following: But in Lemon, the same rule becomes the following:
<pre> <pre>
@ -402,14 +426,14 @@ of the shift, and a reduce-reduce conflict is resolved by reducing
whichever rule comes first in the grammar file.</p> whichever rule comes first in the grammar file.</p>
<p>Just like in <p>Just like in
yacc and bison, Lemon allows a measure of control yacc and bison, Lemon allows a measure of control
over the resolution of paring conflicts using precedence rules. over the resolution of parsing conflicts using precedence rules.
A precedence value can be assigned to any terminal symbol A precedence value can be assigned to any terminal symbol
using the using the
<a href='#pleft'>%left</a>, <tt><a href='#pleft'>%left</a></tt>,
<a href='#pright'>%right</a> or <tt><a href='#pright'>%right</a></tt> or
<a href='#pnonassoc'>%nonassoc</a> directives. Terminal symbols <tt><a href='#pnonassoc'>%nonassoc</a></tt> directives. Terminal symbols
mentioned in earlier directives have a lower precedence that mentioned in earlier directives have a lower precedence than
terminal symbols mentioned in later directives. For example:</p> terminal symbols mentioned in later directives. For example:</p>
<p><pre> <p><pre>
@ -485,29 +509,29 @@ as follows:
<li> If the precedence of the token to be shifted is greater than <li> If the precedence of the token to be shifted is greater than
the precedence of the rule to reduce, then resolve in favor the precedence of the rule to reduce, then resolve in favor
of the shift. No parsing conflict is reported. of the shift. No parsing conflict is reported.
<li> If the precedence of the token it be shifted is less than the <li> If the precedence of the token to be shifted is less than the
precedence of the rule to reduce, then resolve in favor of the precedence of the rule to reduce, then resolve in favor of the
reduce action. No parsing conflict is reported. reduce action. No parsing conflict is reported.
<li> If the precedences are the same and the shift token is <li> If the precedences are the same and the shift token is
right-associative, then resolve in favor of the shift. right-associative, then resolve in favor of the shift.
No parsing conflict is reported. No parsing conflict is reported.
<li> If the precedences are the same the shift token is <li> If the precedences are the same and the shift token is
left-associative, then resolve in favor of the reduce. left-associative, then resolve in favor of the reduce.
No parsing conflict is reported. No parsing conflict is reported.
<li> Otherwise, resolve the conflict by doing the shift and <li> Otherwise, resolve the conflict by doing the shift, and
report the parsing conflict. report a parsing conflict.
</ul> </ul>
Reduce-reduce conflicts are resolved this way: Reduce-reduce conflicts are resolved this way:
<ul> <ul>
<li> If either reduce rule <li> If either reduce rule
lacks precedence information, then resolve in favor of the lacks precedence information, then resolve in favor of the
rule that appears first in the grammar and report a parsing rule that appears first in the grammar, and report a parsing
conflict. conflict.
<li> If both rules have precedence and the precedence is different <li> If both rules have precedence and the precedence is different,
then resolve the dispute in favor of the rule with the highest then resolve the dispute in favor of the rule with the highest
precedence and do not report a conflict. precedence, and do not report a conflict.
<li> Otherwise, resolve the conflict by reducing by the rule that <li> Otherwise, resolve the conflict by reducing by the rule that
appears first in the grammar and report a parsing conflict. appears first in the grammar, and report a parsing conflict.
</ul> </ul>
<h3>Special Directives</h3> <h3>Special Directives</h3>
@ -516,40 +540,40 @@ Reduce-reduce conflicts are resolved this way:
directives. We've described all the grammar rules, so now we'll directives. We've described all the grammar rules, so now we'll
talk about the special directives.</p> talk about the special directives.</p>
<p>Directives in lemon can occur in any order. You can put them before <p>Directives in Lemon can occur in any order. You can put them before
the grammar rules, or after the grammar rules, or in the mist of the the grammar rules, or after the grammar rules, or in the midst of the
grammar rules. It doesn't matter. The relative order of grammar rules. It doesn't matter. The relative order of
directives used to assign precedence to terminals is important, but directives used to assign precedence to terminals is important, but
other than that, the order of directives in Lemon is arbitrary.</p> other than that, the order of directives in Lemon is arbitrary.</p>
<p>Lemon supports the following special directives: <p>Lemon supports the following special directives:
<ul> <ul>
<li><tt>%code</tt> <li><tt><a href='#pcode'>%code</a></tt>
<li><tt>%default_destructor</tt> <li><tt><a href='#default_destructor'>%default_destructor</a></tt>
<li><tt>%default_type</tt> <li><tt><a href='#default_type'>%default_type</a></tt>
<li><tt>%destructor</tt> <li><tt><a href='#destructor'>%destructor</a></tt>
<li><tt>%endif</tt> <li><tt><a href='#pifdef'>%endif</a></tt>
<li><tt>%extra_argument</tt> <li><tt><a href='#extraarg'>%extra_argument</a></tt>
<li><tt>%fallback</tt> <li><tt><a href='#pfallback'>%fallback</a></tt>
<li><tt>%ifdef</tt> <li><tt><a href='#pifdef'>%ifdef</a></tt>
<li><tt>%ifndef</tt> <li><tt><a href='#pifdef'>%ifndef</a></tt>
<li><tt>%include</tt> <li><tt><a href='#pinclude'>%include</a></tt>
<li><tt>%left</tt> <li><tt><a href='#pleft'>%left</a></tt>
<li><tt>%name</tt> <li><tt><a href='#pname'>%name</a></tt>
<li><tt>%nonassoc</tt> <li><tt><a href='#pnonassoc'>%nonassoc</a></tt>
<li><tt>%parse_accept</tt> <li><tt><a href='#parse_accept'>%parse_accept</a></tt>
<li><tt>%parse_failure </tt> <li><tt><a href='#parse_failure'>%parse_failure</a></tt>
<li><tt>%right</tt> <li><tt><a href='#pright'>%right</a></tt>
<li><tt>%stack_overflow</tt> <li><tt><a href='#stack_overflow'>%stack_overflow</a></tt>
<li><tt>%stack_size</tt> <li><tt><a href='#stack_size'>%stack_size</a></tt>
<li><tt>%start_symbol</tt> <li><tt><a href='#start_symbol'>%start_symbol</a></tt>
<li><tt>%syntax_error</tt> <li><tt><a href='#syntax_error'>%syntax_error</a></tt>
<li><tt>%token_class</tt> <li><tt><a href='#token_class'>%token_class</a></tt>
<li><tt>%token_destructor</tt> <li><tt><a href='#token_destructor'>%token_destructor</a></tt>
<li><tt>%token_prefix</tt> <li><tt><a href='#token_prefix'>%token_prefix</a></tt>
<li><tt>%token_type</tt> <li><tt><a href='#token_type'>%token_type</a></tt>
<li><tt>%type</tt> <li><tt><a href='#ptype'>%type</a></tt>
<li><tt>%wildcard</tt> <li><tt><a href='#pwildcard'>%wildcard</a></tt>
</ul> </ul>
Each of these directives will be described separately in the Each of these directives will be described separately in the
following sections:</p> following sections:</p>
@ -557,43 +581,42 @@ following sections:</p>
<a name='pcode'></a> <a name='pcode'></a>
<h4>The <tt>%code</tt> directive</h4> <h4>The <tt>%code</tt> directive</h4>
<p>The %code directive is used to specify addition C code that <p>The <tt>%code</tt> directive is used to specify additional C code that
is added to the end of the main output file. This is similar to is added to the end of the main output file. This is similar to
the <a href='#pinclude'>%include</a> directive except that %include the <tt><a href='#pinclude'>%include</a></tt> directive except that
is inserted at the beginning of the main output file.</p> <tt>%include</tt> is inserted at the beginning of the main output file.</p>
<p>%code is typically used to include some action routines or perhaps <p><tt>%code</tt> is typically used to include some action routines or perhaps
a tokenizer or even the "main()" function a tokenizer or even the "main()" function
as part of the output file.</p> as part of the output file.</p>
<a name='default_destructor'></a> <a name='default_destructor'></a>
<h4>The <tt>%default_destructor</tt> directive</h4> <h4>The <tt>%default_destructor</tt> directive</h4>
<p>The %default_destructor directive specifies a destructor to <p>The <tt>%default_destructor</tt> directive specifies a destructor to
use for non-terminals that do not have their own destructor use for non-terminals that do not have their own destructor
specified by a separate %destructor directive. See the documentation specified by a separate <tt>%destructor</tt> directive. See the documentation
on the <a name='#destructor'>%destructor</a> directive below for on the <tt><a name='#destructor'>%destructor</a></tt> directive below for
additional information.</p> additional information.</p>
<p>In some grammers, many different non-terminal symbols have the <p>In some grammars, many different non-terminal symbols have the
same datatype and hence the same destructor. This directive is same data type and hence the same destructor. This directive is
a convenience way to specify the same destructor for all those a convenient way to specify the same destructor for all those
non-terminals using a single statement.</p> non-terminals using a single statement.</p>
<a name='default_type'></a> <a name='default_type'></a>
<h4>The <tt>%default_type</tt> directive</h4> <h4>The <tt>%default_type</tt> directive</h4>
<p>The %default_type directive specifies the datatype of non-terminal <p>The <tt>%default_type</tt> directive specifies the data type of non-terminal
symbols that do no have their own datatype defined using a separate symbols that do not have their own data type defined using a separate
<a href='#ptype'>%type</a> directive. <tt><a href='#ptype'>%type</a></tt> directive.</p>
</p>
<a name='destructor'></a> <a name='destructor'></a>
<h4>The <tt>%destructor</tt> directive</h4> <h4>The <tt>%destructor</tt> directive</h4>
<p>The %destructor directive is used to specify a destructor for <p>The <tt>%destructor</tt> directive is used to specify a destructor for
a non-terminal symbol. a non-terminal symbol.
(See also the <a href='#token_destructor'>%token_destructor</a> (See also the <tt><a href='#token_destructor'>%token_destructor</a></tt>
directive which is used to specify a destructor for terminal symbols.)</p> directive which is used to specify a destructor for terminal symbols.)</p>
<p>A non-terminal's destructor is called to dispose of the <p>A non-terminal's destructor is called to dispose of the
@ -615,7 +638,7 @@ or other resources held by that non-terminal.</p>
%destructor nt { free($$); } %destructor nt { free($$); }
nt(A) ::= ID NUM. { A = malloc( 100 ); } nt(A) ::= ID NUM. { A = malloc( 100 ); }
</pre> </pre>
This example is a bit contrived but it serves to illustrate how This example is a bit contrived, but it serves to illustrate how
destructors work. The example shows a non-terminal named destructors work. The example shows a non-terminal named
"nt" that holds values of type "void*". When the rule for "nt" that holds values of type "void*". When the rule for
an "nt" reduces, it sets the value of the non-terminal to an "nt" reduces, it sets the value of the non-terminal to
@ -631,17 +654,17 @@ stack, unless the non-terminal is used in a C-code action. If
the non-terminal is used by C-code, then it is assumed that the the non-terminal is used by C-code, then it is assumed that the
C-code will take care of destroying it. C-code will take care of destroying it.
More commonly, the value is used to build some More commonly, the value is used to build some
larger structure and we don't want to destroy it, which is why larger structure, and we don't want to destroy it, which is why
the destructor is not called in this circumstance.</p> the destructor is not called in this circumstance.</p>
<p>Destructors help avoid memory leaks by automatically freeing <p>Destructors help avoid memory leaks by automatically freeing
allocated objects when they go out of scope. allocated objects when they go out of scope.
To do the same using yacc or bison is much more difficult.</p> To do the same using yacc or bison is much more difficult.</p>
<a name="extraarg"></a> <a name='extraarg'></a>
<h4>The <tt>%extra_argument</tt> directive</h4> <h4>The <tt>%extra_argument</tt> directive</h4>
The %extra_argument directive instructs Lemon to add a 4th parameter The <tt>%extra_argument</tt> directive instructs Lemon to add a 4th parameter
to the parameter list of the Parse() function it generates. Lemon to the parameter list of the Parse() function it generates. Lemon
doesn't do anything itself with this extra argument, but it does doesn't do anything itself with this extra argument, but it does
make the argument available to C-code action routines, destructors, make the argument available to C-code action routines, destructors,
@ -659,61 +682,64 @@ in the most recent call to Parse().</p>
<a name='pfallback'></a> <a name='pfallback'></a>
<h4>The <tt>%fallback</tt> directive</h4> <h4>The <tt>%fallback</tt> directive</h4>
<p>The %fallback directive specifies an alternative meaning for one <p>The <tt>%fallback</tt> directive specifies an alternative meaning for one
or more tokens. The alternative meaning is tried if the original token or more tokens. The alternative meaning is tried if the original token
would have generated a syntax error. would have generated a syntax error.</p>
<p>The %fallback directive was added to support robust parsing of SQL <p>The <tt>%fallback</tt> directive was added to support robust parsing of SQL
syntax in <a href="https://www.sqlite.org/">SQLite</a>. syntax in <a href='https://www.sqlite.org/'>SQLite</a>.
The SQL language contains a large assortment of keywords, each of which The SQL language contains a large assortment of keywords, each of which
appears as a different token to the language parser. SQL contains so appears as a different token to the language parser. SQL contains so
many keywords, that it can be difficult for programmers to keep up with many keywords that it can be difficult for programmers to keep up with
them all. Programmers will, therefore, sometimes mistakenly use an them all. Programmers will, therefore, sometimes mistakenly use an
obscure language keyword for an identifier. The %fallback directive obscure language keyword for an identifier. The <tt>%fallback</tt> directive
provides a mechanism to tell the parser: "If you are unable to parse provides a mechanism to tell the parser: "If you are unable to parse
this keyword, try treating it as an identifier instead." this keyword, try treating it as an identifier instead."</p>
<p>The syntax of %fallback is as follows: <p>The syntax of <tt>%fallback</tt> is as follows:
<blockquote> <blockquote>
<tt>%fallback</tt> <i>ID</i> <i>TOKEN...</i> <b>.</b> <tt>%fallback</tt> <i>ID</i> <i>TOKEN...</i> <b>.</b>
</blockquote> </blockquote></p>
<p>In words, the %fallback directive is followed by a list of token names <p>In words, the <tt>%fallback</tt> directive is followed by a list of token
terminated by a period. The first token name is the fallback token - the names terminated by a period.
The first token name is the fallback token &mdash; the
token to which all the other tokens fall back to. The second and subsequent token to which all the other tokens fall back to. The second and subsequent
arguments are tokens which fall back to the token identified by the first arguments are tokens which fall back to the token identified by the first
argument. argument.</p>
<a name='pifdef'></a> <a name='pifdef'></a>
<h4>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives.</h4> <h4>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives</h4>
<p>The %ifdef, %ifndef, and %endif directives are similar to <p>The <tt>%ifdef</tt>, <tt>%ifndef</tt>, and <tt>%endif</tt> directives
#ifdef, #ifndef, and #endif in the C-preprocessor, just not as general. are similar to #ifdef, #ifndef, and #endif in the C-preprocessor,
just not as general.
Each of these directives must begin at the left margin. No whitespace Each of these directives must begin at the left margin. No whitespace
is allowed between the "%" and the directive name. is allowed between the "%" and the directive name.</p>
<p>Grammar text in between "%ifdef MACRO" and the next nested "%endif" is <p>Grammar text in between "<tt>%ifdef MACRO</tt>" and the next nested
"<tt>%endif</tt>" is
ignored unless the "-DMACRO" command-line option is used. Grammar text ignored unless the "-DMACRO" command-line option is used. Grammar text
betwen "%ifndef MACRO" and the next nested "%endif" is included except when betwen "<tt>%ifndef MACRO</tt>" and the next nested "<tt>%endif</tt>" is
the "-DMACRO" command-line option is used. included except when the "-DMACRO" command-line option is used.</p>
<p>Note that the argument to %ifdef and %ifndef must be a single <p>Note that the argument to <tt>%ifdef</tt> and <tt>%ifndef</tt> must
preprocessor symbol name, not a general expression. There is no "%else" be a single preprocessor symbol name, not a general expression.
directive. There is no "<tt>%else</tt>" directive.</p>
<a name='pinclude'></a> <a name='pinclude'></a>
<h4>The <tt>%include</tt> directive</h4> <h4>The <tt>%include</tt> directive</h4>
<p>The %include directive specifies C code that is included at the <p>The <tt>%include</tt> directive specifies C code that is included at the
top of the generated parser. You can include any text you want -- top of the generated parser. You can include any text you want &mdash;
the Lemon parser generator copies it blindly. If you have multiple the Lemon parser generator copies it blindly. If you have multiple
%include directives in your grammar file, their values are concatenated <tt>%include</tt> directives in your grammar file, their values are concatenated
so that all %include code ultimately appears near the top of the so that all <tt>%include</tt> code ultimately appears near the top of the
generated parser, in the same order as it appeared in the grammer.</p> generated parser, in the same order as it appeared in the grammar.</p>
<p>The %include directive is very handy for getting some extra #include <p>The <tt>%include</tt> directive is very handy for getting some extra #include
preprocessor statements at the beginning of the generated parser. preprocessor statements at the beginning of the generated parser.
For example:</p> For example:</p>
@ -722,17 +748,19 @@ For example:</p>
</pre></p> </pre></p>
<p>This might be needed, for example, if some of the C actions in the <p>This might be needed, for example, if some of the C actions in the
grammar call functions that are prototyed in unistd.h.</p> grammar call functions that are prototyped in unistd.h.</p>
<a name='pleft'></a> <a name='pleft'></a>
<h4>The <tt>%left</tt> directive</h4> <h4>The <tt>%left</tt> directive</h4>
The %left directive is used (along with the <a href='#pright'>%right</a> and The <tt>%left</tt> directive is used (along with the
<a href='#pnonassoc'>%nonassoc</a> directives) to declare precedences of <tt><a href='#pright'>%right</a></tt> and
terminal symbols. Every terminal symbol whose name appears after <tt><a href='#pnonassoc'>%nonassoc</a></tt> directives) to declare
a %left directive but before the next period (".") is precedences of terminal symbols.
Every terminal symbol whose name appears after
a <tt>%left</tt> directive but before the next period (".") is
given the same left-associative precedence value. Subsequent given the same left-associative precedence value. Subsequent
%left directives have higher precedence. For example:</p> <tt>%left</tt> directives have higher precedence. For example:</p>
<p><pre> <p><pre>
%left AND. %left AND.
@ -743,20 +771,21 @@ given the same left-associative precedence value. Subsequent
%right EXP NOT. %right EXP NOT.
</pre></p> </pre></p>
<p>Note the period that terminates each %left, %right or %nonassoc <p>Note the period that terminates each <tt>%left</tt>,
<tt>%right</tt> or <tt>%nonassoc</tt>
directive.</p> directive.</p>
<p>LALR(1) grammars can get into a situation where they require <p>LALR(1) grammars can get into a situation where they require
a large amount of stack space if you make heavy use or right-associative a large amount of stack space if you make heavy use or right-associative
operators. For this reason, it is recommended that you use %left operators. For this reason, it is recommended that you use <tt>%left</tt>
rather than %right whenever possible.</p> rather than <tt>%right</tt> whenever possible.</p>
<a name='pname'></a> <a name='pname'></a>
<h4>The <tt>%name</tt> directive</h4> <h4>The <tt>%name</tt> directive</h4>
<p>By default, the functions generated by Lemon all begin with the <p>By default, the functions generated by Lemon all begin with the
five-character string "Parse". You can change this string to something five-character string "Parse". You can change this string to something
different using the %name directive. For instance:</p> different using the <tt>%name</tt> directive. For instance:</p>
<p><pre> <p><pre>
%name Abcde %name Abcde
@ -770,22 +799,22 @@ functions named
<li> AbcdeTrace(), and <li> AbcdeTrace(), and
<li> Abcde(). <li> Abcde().
</ul> </ul>
The %name directive allows you to generator two or more different The <tt>%name</tt> directive allows you to generate two or more different
parsers and link them all into the same executable. parsers and link them all into the same executable.</p>
</p>
<a name='pnonassoc'></a> <a name='pnonassoc'></a>
<h4>The <tt>%nonassoc</tt> directive</h4> <h4>The <tt>%nonassoc</tt> directive</h4>
<p>This directive is used to assign non-associative precedence to <p>This directive is used to assign non-associative precedence to
one or more terminal symbols. See the section on one or more terminal symbols. See the section on
<a href='#precrules'>precedence rules</a> <a href='#precrules'>precedence rules</a>
or on the <a href='#pleft'>%left</a> directive for additional information.</p> or on the <tt><a href='#pleft'>%left</a></tt> directive
for additional information.</p>
<a name='parse_accept'></a> <a name='parse_accept'></a>
<h4>The <tt>%parse_accept</tt> directive</h4> <h4>The <tt>%parse_accept</tt> directive</h4>
<p>The %parse_accept directive specifies a block of C code that is <p>The <tt>%parse_accept</tt> directive specifies a block of C code that is
executed whenever the parser accepts its input string. To "accept" executed whenever the parser accepts its input string. To "accept"
an input string means that the parser was able to process all tokens an input string means that the parser was able to process all tokens
without error.</p> without error.</p>
@ -801,7 +830,7 @@ without error.</p>
<a name='parse_failure'></a> <a name='parse_failure'></a>
<h4>The <tt>%parse_failure</tt> directive</h4> <h4>The <tt>%parse_failure</tt> directive</h4>
<p>The %parse_failure directive specifies a block of C code that <p>The <tt>%parse_failure</tt> directive specifies a block of C code that
is executed whenever the parser fails complete. This code is not is executed whenever the parser fails complete. This code is not
executed until the parser has tried and failed to resolve an input executed until the parser has tried and failed to resolve an input
error using is usual error recovery strategy. The routine is error using is usual error recovery strategy. The routine is
@ -817,14 +846,14 @@ only invoked when parsing is unable to continue.</p>
<h4>The <tt>%right</tt> directive</h4> <h4>The <tt>%right</tt> directive</h4>
<p>This directive is used to assign right-associative precedence to <p>This directive is used to assign right-associative precedence to
one or more terminal symbols. See the section on one or more terminal symbols. See the section on
<a href='#precrules'>precedence rules</a> <a href='#precrules'>precedence rules</a>
or on the <a href='#pleft'>%left</a> directive for additional information.</p> or on the <a href='#pleft'>%left</a> directive for additional information.</p>
<a name='stack_overflow'></a> <a name='stack_overflow'></a>
<h4>The <tt>%stack_overflow</tt> directive</h4> <h4>The <tt>%stack_overflow</tt> directive</h4>
<p>The %stack_overflow directive specifies a block of C code that <p>The <tt>%stack_overflow</tt> directive specifies a block of C code that
is executed if the parser's internal stack ever overflows. Typically is executed if the parser's internal stack ever overflows. Typically
this just prints an error message. After a stack overflow, the parser this just prints an error message. After a stack overflow, the parser
will be unable to continue and must be reset.</p> will be unable to continue and must be reset.</p>
@ -837,7 +866,7 @@ will be unable to continue and must be reset.</p>
<p>You can help prevent parser stack overflows by avoiding the use <p>You can help prevent parser stack overflows by avoiding the use
of right recursion and right-precedence operators in your grammar. of right recursion and right-precedence operators in your grammar.
Use left recursion and and left-precedence operators instead, to Use left recursion and and left-precedence operators instead to
encourage rules to reduce sooner and keep the stack size down. encourage rules to reduce sooner and keep the stack size down.
For example, do rules like this: For example, do rules like this:
<pre> <pre>
@ -848,7 +877,7 @@ Not like this:
<pre> <pre>
list ::= element list. // right-recursion. Bad! list ::= element list. // right-recursion. Bad!
list ::= . list ::= .
</pre> </pre></p>
<a name='stack_size'></a> <a name='stack_size'></a>
<h4>The <tt>%stack_size</tt> directive</h4> <h4>The <tt>%stack_size</tt> directive</h4>
@ -856,7 +885,7 @@ Not like this:
<p>If stack overflow is a problem and you can't resolve the trouble <p>If stack overflow is a problem and you can't resolve the trouble
by using left-recursion, then you might want to increase the size by using left-recursion, then you might want to increase the size
of the parser's stack using this directive. Put an positive integer of the parser's stack using this directive. Put an positive integer
after the %stack_size directive and Lemon will generate a parse after the <tt>%stack_size</tt> directive and Lemon will generate a parse
with a stack of the requested size. The default value is 100.</p> with a stack of the requested size. The default value is 100.</p>
<p><pre> <p><pre>
@ -866,25 +895,40 @@ with a stack of the requested size. The default value is 100.</p>
<a name='start_symbol'></a> <a name='start_symbol'></a>
<h4>The <tt>%start_symbol</tt> directive</h4> <h4>The <tt>%start_symbol</tt> directive</h4>
<p>By default, the start-symbol for the grammar that Lemon generates <p>By default, the start symbol for the grammar that Lemon generates
is the first non-terminal that appears in the grammar file. But you is the first non-terminal that appears in the grammar file. But you
can choose a different start-symbol using the %start_symbol directive.</p> can choose a different start symbol using the
<tt>%start_symbol</tt> directive.</p>
<p><pre> <p><pre>
%start_symbol prog %start_symbol prog
</pre></p> </pre></p>
<a name='syntax_error'></a>
<h4>The <tt>%syntax_error</tt> directive</h4>
<p>See <a href='#error_processing'>Error Processing</a>.</p>
<a name='token_class'></a>
<h4>The <tt>%token_class</tt> directive</h4>
<p>Undocumented. Appears to be related to the MULTITERMINAL concept.
<a href='http://sqlite.org/src/fdiff?v1=796930d5fc2036c7&v2=624b24c5dc048e09&sbs=0'>Implementation</a>.</p>
<a name='token_destructor'></a> <a name='token_destructor'></a>
<h4>The <tt>%token_destructor</tt> directive</h4> <h4>The <tt>%token_destructor</tt> directive</h4>
<p>The %destructor directive assigns a destructor to a non-terminal <p>The <tt>%destructor</tt> directive assigns a destructor to a non-terminal
symbol. (See the description of the %destructor directive above.) symbol. (See the description of the
This directive does the same thing for all terminal symbols.</p> <tt><a href='%destructor'>%destructor</a></tt> directive above.)
The <tt>%token_destructor</tt> directive does the same thing
for all terminal symbols.</p>
<p>Unlike non-terminal symbols which may each have a different data type <p>Unlike non-terminal symbols which may each have a different data type
for their values, terminals all use the same data type (defined by for their values, terminals all use the same data type (defined by
the %token_type directive) and so they use a common destructor. Other the <tt><a href='#token_type'>%token_type</a></tt> directive)
than that, the token destructor works just like the non-terminal and so they use a common destructor.
Other than that, the token destructor works just like the non-terminal
destructors.</p> destructors.</p>
<a name='token_prefix'></a> <a name='token_prefix'></a>
@ -893,8 +937,9 @@ destructors.</p>
<p>Lemon generates #defines that assign small integer constants <p>Lemon generates #defines that assign small integer constants
to each terminal symbol in the grammar. If desired, Lemon will to each terminal symbol in the grammar. If desired, Lemon will
add a prefix specified by this directive add a prefix specified by this directive
to each of the #defines it generates. to each of the #defines it generates.</p>
So if the default output of Lemon looked like this:
<p>So if the default output of Lemon looked like this:
<pre> <pre>
#define AND 1 #define AND 1
#define MINUS 2 #define MINUS 2
@ -911,7 +956,7 @@ to cause Lemon to produce these symbols instead:
#define TOKEN_MINUS 2 #define TOKEN_MINUS 2
#define TOKEN_OR 3 #define TOKEN_OR 3
#define TOKEN_PLUS 4 #define TOKEN_PLUS 4
</pre> </pre></p>
<a name='token_type'></a><a name='ptype'></a> <a name='token_type'></a><a name='ptype'></a>
<h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4> <h4>The <tt>%token_type</tt> and <tt>%type</tt> directives</h4>
@ -932,7 +977,7 @@ token structure. Like this:</p>
is "void*".</p> is "void*".</p>
<p>Non-terminal symbols can each have their own data types. Typically <p>Non-terminal symbols can each have their own data types. Typically
the data type of a non-terminal is a pointer to the root of a parse-tree the data type of a non-terminal is a pointer to the root of a parse tree
structure that contains all information about that non-terminal. structure that contains all information about that non-terminal.
For example:</p> For example:</p>
@ -953,14 +998,15 @@ and able to pay that price, fine. You just need to know.</p>
<a name='pwildcard'></a> <a name='pwildcard'></a>
<h4>The <tt>%wildcard</tt> directive</h4> <h4>The <tt>%wildcard</tt> directive</h4>
<p>The %wildcard directive is followed by a single token name and a <p>The <tt>%wildcard</tt> directive is followed by a single token name and a
period. This directive specifies that the identified token should period. This directive specifies that the identified token should
match any input token. match any input token.</p>
<p>When the generated parser has the choice of matching an input against <p>When the generated parser has the choice of matching an input against
the wildcard token and some other token, the other token is always used. the wildcard token and some other token, the other token is always used.
The wildcard token is only matched if there are no other alternatives. The wildcard token is only matched if there are no alternatives.</p>
<a name='error_processing'></a>
<h3>Error Processing</h3> <h3>Error Processing</h3>
<p>After extensive experimentation over several years, it has been <p>After extensive experimentation over several years, it has been
@ -968,19 +1014,20 @@ discovered that the error recovery strategy used by yacc is about
as good as it gets. And so that is what Lemon uses.</p> as good as it gets. And so that is what Lemon uses.</p>
<p>When a Lemon-generated parser encounters a syntax error, it <p>When a Lemon-generated parser encounters a syntax error, it
first invokes the code specified by the %syntax_error directive, if first invokes the code specified by the <tt>%syntax_error</tt> directive, if
any. It then enters its error recovery strategy. The error recovery any. It then enters its error recovery strategy. The error recovery
strategy is to begin popping the parsers stack until it enters a strategy is to begin popping the parsers stack until it enters a
state where it is permitted to shift a special non-terminal symbol state where it is permitted to shift a special non-terminal symbol
named "error". It then shifts this non-terminal and continues named "error". It then shifts this non-terminal and continues
parsing. But the %syntax_error routine will not be called again parsing. The <tt>%syntax_error</tt> routine will not be called again
until at least three new tokens have been successfully shifted.</p> until at least three new tokens have been successfully shifted.</p>
<p>If the parser pops its stack until the stack is empty, and it still <p>If the parser pops its stack until the stack is empty, and it still
is unable to shift the error symbol, then the %parse_failed routine is unable to shift the error symbol, then the
<tt><a href='#parse_failure'>%parse_failure</a></tt> routine
is invoked and the parser resets itself to its start state, ready is invoked and the parser resets itself to its start state, ready
to begin parsing a new file. This is what will happen at the very to begin parsing a new file. This is what will happen at the very
first syntax error, of course, if there are no instances of the first syntax error, of course, if there are no instances of the
"error" non-terminal in your grammar.</p> "error" non-terminal in your grammar.</p>
</body> </body>

View File

@ -1707,6 +1707,19 @@ static void fts3CursorFinalizeStmt(Fts3Cursor *pCsr){
sqlite3_finalize(pCsr->pStmt); 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 ** Close the cursor. For additional information see the documentation
** on the xClose method of the virtual table interface. ** 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){ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
fts3CursorFinalizeStmt(pCsr); fts3ClearCursor(pCsr);
sqlite3Fts3ExprFree(pCsr->pExpr);
sqlite3Fts3FreeDeferredTokens(pCsr);
sqlite3_free(pCsr->aDoclist);
sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_free(pCsr); sqlite3_free(pCsr);
return SQLITE_OK; return SQLITE_OK;
@ -1744,7 +1753,7 @@ static int fts3CursorSeekStmt(Fts3Cursor *pCsr){
}else{ }else{
zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist); zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
if( !zSql ) return SQLITE_NOMEM; 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); sqlite3_free(zSql);
} }
if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1; if( rc==SQLITE_OK ) pCsr->bSeekStmt = 1;
@ -3219,11 +3228,7 @@ static int fts3FilterMethod(
assert( iIdx==nVal ); assert( iIdx==nVal );
/* In case the cursor has been used before, clear it now. */ /* In case the cursor has been used before, clear it now. */
fts3CursorFinalizeStmt(pCsr); fts3ClearCursor(pCsr);
sqlite3_free(pCsr->aDoclist);
sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
/* Set the lower and upper bounds on docids to return */ /* Set the lower and upper bounds on docids to return */
pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64); pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
@ -3281,7 +3286,7 @@ static int fts3FilterMethod(
); );
} }
if( zSql ){ 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); sqlite3_free(zSql);
}else{ }else{
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
@ -3302,7 +3307,12 @@ static int fts3FilterMethod(
** routine to find out if it has reached the end of a result set. ** routine to find out if it has reached the end of a result set.
*/ */
static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){ 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 ){ switch( iCol-p->nColumn ){
case 0: case 0:
/* The special 'table-name' column */ /* The special 'table-name' column */
sqlite3_result_blob(pCtx, &pCsr, sizeof(Fts3Cursor*), SQLITE_TRANSIENT); sqlite3_result_pointer(pCtx, pCsr, "fts3cursor", 0);
sqlite3_result_subtype(pCtx, SQLITE_BLOB);
break; break;
case 1: case 1:
@ -3562,9 +3571,10 @@ static int fts3FunctionArg(
sqlite3_value *pVal, /* argv[0] passed to function */ sqlite3_value *pVal, /* argv[0] passed to function */
Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */
){ ){
int rc = SQLITE_OK; int rc;
if( sqlite3_value_subtype(pVal)==SQLITE_BLOB ){ *ppCsr = (Fts3Cursor*)sqlite3_value_pointer(pVal, "fts3cursor");
*ppCsr = *(Fts3Cursor**)sqlite3_value_blob(pVal); if( (*ppCsr)!=0 ){
rc = SQLITE_OK;
}else{ }else{
char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc);
sqlite3_result_error(pContext, zErr, -1); sqlite3_result_error(pContext, zErr, -1);

View File

@ -407,7 +407,8 @@ static int fts3SqlStmt(
if( !zSql ){ if( !zSql ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
}else{ }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); sqlite3_free(zSql);
assert( rc==SQLITE_OK || pStmt==0 ); assert( rc==SQLITE_OK || pStmt==0 );
p->aStmt[eStmt] = pStmt; p->aStmt[eStmt] = pStmt;

View File

@ -67,9 +67,11 @@ void sqlite3Fts5BufferAppendBlob(
const u8 *pData const u8 *pData
){ ){
assert_nc( *pRc || nData>=0 ); assert_nc( *pRc || nData>=0 );
if( fts5BufferGrow(pRc, pBuf, nData) ) return; if( nData ){
memcpy(&pBuf->p[pBuf->n], pData, nData); if( fts5BufferGrow(pRc, pBuf, nData) ) return;
pBuf->n += nData; memcpy(&pBuf->p[pBuf->n], pData, nData);
pBuf->n += nData;
}
} }
/* /*
@ -246,8 +248,8 @@ void *sqlite3Fts5MallocZero(int *pRc, int nByte){
void *pRet = 0; void *pRet = 0;
if( *pRc==SQLITE_OK ){ if( *pRc==SQLITE_OK ){
pRet = sqlite3_malloc(nByte); pRet = sqlite3_malloc(nByte);
if( pRet==0 && nByte>0 ){ if( pRet==0 ){
*pRc = SQLITE_NOMEM; if( nByte>0 ) *pRc = SQLITE_NOMEM;
}else{ }else{
memset(pRet, 0, nByte); memset(pRet, 0, nByte);
} }

View File

@ -36,9 +36,10 @@ struct Fts5Hash {
/* /*
** Each entry in the hash table is represented by an object of the ** Each entry in the hash table is represented by an object of the
** following type. Each object, its key (zKey[]) and its current data ** following type. Each object, its key (a nul-terminated string) and
** are stored in a single memory allocation. The position list data ** its current data are stored in a single memory allocation. The
** immediately follows the key data in memory. ** 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 ** The data that follows the key is in a similar, but not identical format
** to the doclist data stored in the database. It is: ** to the doclist data stored in the database. It is:
@ -62,20 +63,20 @@ struct Fts5HashEntry {
int nAlloc; /* Total size of allocation */ int nAlloc; /* Total size of allocation */
int iSzPoslist; /* Offset of space for 4-byte poslist size */ int iSzPoslist; /* Offset of space for 4-byte poslist size */
int nData; /* Total bytes of data (incl. structure) */ 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 bDel; /* Set delete-flag @ iSzPoslist */
u8 bContent; /* Set content-flag (detail=none mode) */ u8 bContent; /* Set content-flag (detail=none mode) */
i16 iCol; /* Column of last value written */ i16 iCol; /* Column of last value written */
int iPos; /* Position of last value written */ int iPos; /* Position of last value written */
i64 iRowid; /* Rowid 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++){ for(i=0; i<pHash->nSlot; i++){
while( apOld[i] ){ while( apOld[i] ){
int iHash; unsigned int iHash;
Fts5HashEntry *p = apOld[i]; Fts5HashEntry *p = apOld[i];
apOld[i] = p->pHashNext; 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]; p->pHashNext = apNew[iHash];
apNew[iHash] = p; apNew[iHash] = p;
} }
@ -244,9 +246,10 @@ int sqlite3Fts5HashWrite(
/* Attempt to locate an existing hash entry */ /* Attempt to locate an existing hash entry */
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ 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 && p->nKey==nToken
&& memcmp(&p->zKey[1], pToken, nToken)==0 && memcmp(&zKey[1], pToken, nToken)==0
){ ){
break; break;
} }
@ -255,7 +258,8 @@ int sqlite3Fts5HashWrite(
/* If an existing hash entry cannot be found, create a new one. */ /* If an existing hash entry cannot be found, create a new one. */
if( p==0 ){ if( p==0 ){
/* Figure out how much space to allocate */ /* 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; if( nByte<128 ) nByte = 128;
/* Grow the Fts5Hash.aSlot[] array if necessary. */ /* Grow the Fts5Hash.aSlot[] array if necessary. */
@ -268,14 +272,15 @@ int sqlite3Fts5HashWrite(
/* Allocate new Fts5HashEntry and add it to the hash table. */ /* Allocate new Fts5HashEntry and add it to the hash table. */
p = (Fts5HashEntry*)sqlite3_malloc(nByte); p = (Fts5HashEntry*)sqlite3_malloc(nByte);
if( !p ) return SQLITE_NOMEM; if( !p ) return SQLITE_NOMEM;
memset(p, 0, FTS5_HASHENTRYSIZE); memset(p, 0, sizeof(Fts5HashEntry));
p->nAlloc = nByte; p->nAlloc = nByte;
p->zKey[0] = bByte; zKey = fts5EntryKey(p);
memcpy(&p->zKey[1], pToken, nToken); zKey[0] = bByte;
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) ); memcpy(&zKey[1], pToken, nToken);
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
p->nKey = nToken; p->nKey = nToken;
p->zKey[nToken+1] = '\0'; zKey[nToken+1] = '\0';
p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE; p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry);
p->pHashNext = pHash->aSlot[iHash]; p->pHashNext = pHash->aSlot[iHash];
pHash->aSlot[iHash] = p; pHash->aSlot[iHash] = p;
pHash->nEntry++; pHash->nEntry++;
@ -393,9 +398,11 @@ static Fts5HashEntry *fts5HashEntryMerge(
p1 = 0; p1 = 0;
}else{ }else{
int i = 0; 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 */ /* p2 is smaller */
*ppOut = p2; *ppOut = p2;
ppOut = &p2->pScanNext; ppOut = &p2->pScanNext;
@ -438,7 +445,7 @@ static int fts5HashEntrySort(
for(iSlot=0; iSlot<pHash->nSlot; iSlot++){ for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
Fts5HashEntry *pIter; Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ 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; Fts5HashEntry *pEntry = pIter;
pEntry->pScanNext = 0; pEntry->pScanNext = 0;
for(i=0; ap[i]; i++){ for(i=0; ap[i]; i++){
@ -471,16 +478,18 @@ int sqlite3Fts5HashQuery(
int *pnDoclist /* OUT: Size of doclist in bytes */ int *pnDoclist /* OUT: Size of doclist in bytes */
){ ){
unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm); unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
char *zKey = 0;
Fts5HashEntry *p; Fts5HashEntry *p;
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ 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 ){ if( p ){
fts5HashAddPoslistSize(pHash, p); fts5HashAddPoslistSize(pHash, p);
*ppDoclist = (const u8*)&p->zKey[nTerm+1]; *ppDoclist = (const u8*)&zKey[nTerm+1];
*pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
}else{ }else{
*ppDoclist = 0; *ppDoclist = 0;
*pnDoclist = 0; *pnDoclist = 0;
@ -513,11 +522,12 @@ void sqlite3Fts5HashScanEntry(
){ ){
Fts5HashEntry *p; Fts5HashEntry *p;
if( (p = pHash->pScan) ){ if( (p = pHash->pScan) ){
int nTerm = (int)strlen(p->zKey); char *zKey = fts5EntryKey(p);
int nTerm = (int)strlen(zKey);
fts5HashAddPoslistSize(pHash, p); fts5HashAddPoslistSize(pHash, p);
*pzTerm = p->zKey; *pzTerm = zKey;
*ppDoclist = (const u8*)&p->zKey[nTerm+1]; *ppDoclist = (const u8*)&zKey[nTerm+1];
*pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
}else{ }else{
*pzTerm = 0; *pzTerm = 0;
*ppDoclist = 0; *ppDoclist = 0;

View File

@ -728,7 +728,8 @@ static int fts5IndexPrepareStmt(
){ ){
if( p->rc==SQLITE_OK ){ if( p->rc==SQLITE_OK ){
if( zSql ){ 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{ }else{
p->rc = SQLITE_NOMEM; p->rc = SQLITE_NOMEM;
} }
@ -777,7 +778,8 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){
if( zSql==0 ){ if( zSql==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
}else{ }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); sqlite3_free(zSql);
} }
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@ -5093,7 +5095,7 @@ static void fts5SetupPrefixIter(
if( pData ){ if( pData ){
pData->p = (u8*)&pData[1]; pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = doclist.n; 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); fts5MultiIterNew2(p, pData, bDesc, ppIter);
} }
fts5BufferFree(&doclist); fts5BufferFree(&doclist);
@ -5332,7 +5334,7 @@ int sqlite3Fts5IndexQuery(
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */ 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 /* 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 ** is a prefix query for which there is no prefix index, set iIdx to
@ -5381,7 +5383,7 @@ int sqlite3Fts5IndexQuery(
} }
if( p->rc ){ if( p->rc ){
sqlite3Fts5IterClose(&pRet->base); sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
pRet = 0; pRet = 0;
fts5CloseReader(p); fts5CloseReader(p);
} }

View File

@ -883,7 +883,8 @@ static int fts5PrepareStatement(
if( zSql==0 ){ if( zSql==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
}else{ }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 ){ if( rc!=SQLITE_OK ){
*pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); *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); char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs);
if( zSql ){ if( zSql ){
sqlite3_stmt *pStmt = 0; 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); sqlite3_free(zSql);
assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 ); assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
@ -2607,15 +2609,14 @@ static void fts5ModuleDestroy(void *pCtx){
static void fts5Fts5Func( static void fts5Fts5Func(
sqlite3_context *pCtx, /* Function call context */ sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */ int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */ sqlite3_value **apArg /* Function arguments */
){ ){
Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx);
char buf[8]; fts5_api **ppApi;
UNUSED_PARAM2(nArg, apUnused); UNUSED_PARAM(nArg);
assert( nArg==0 ); assert( nArg==1 );
assert( sizeof(buf)>=sizeof(pGlobal) ); ppApi = (fts5_api**)sqlite3_value_pointer(apArg[0], "fts5_api_ptr");
memcpy(buf, (void*)&pGlobal, sizeof(pGlobal)); if( ppApi ) *ppApi = &pGlobal->api;
sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT);
} }
/* /*
@ -2680,7 +2681,7 @@ static int fts5Init(sqlite3 *db){
if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db); if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3_create_function( rc = sqlite3_create_function(
db, "fts5", 0, SQLITE_UTF8, p, fts5Fts5Func, 0, 0 db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
); );
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){

View File

@ -136,7 +136,8 @@ static int fts5StorageGetStmt(
if( zSql==0 ){ if( zSql==0 ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
}else{ }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); sqlite3_free(zSql);
if( rc!=SQLITE_OK && pzErrMsg ){ if( rc!=SQLITE_OK && pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));

View File

@ -99,16 +99,13 @@ static int SQLITE_TCLAPI f5tDbAndApi(
sqlite3_stmt *pStmt = 0; sqlite3_stmt *pStmt = 0;
fts5_api *pApi = 0; fts5_api *pApi = 0;
rc = sqlite3_prepare_v2(db, "SELECT fts5()", -1, &pStmt, 0); rc = sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);
return TCL_ERROR; return TCL_ERROR;
} }
sqlite3_bind_pointer(pStmt, 1, (void*)&pApi, "fts5_api_ptr", 0);
if( SQLITE_ROW==sqlite3_step(pStmt) ){ sqlite3_step(pStmt);
const void *pPtr = sqlite3_column_blob(pStmt, 0);
memcpy((void*)&pApi, pPtr, sizeof(pApi));
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0);

View File

@ -73,13 +73,10 @@ static int fts5_api_from_db(sqlite3 *db, fts5_api **ppApi){
int rc; int rc;
*ppApi = 0; *ppApi = 0;
rc = sqlite3_prepare(db, "SELECT fts5()", -1, &pStmt, 0); rc = sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pStmt) sqlite3_bind_pointer(pStmt, 1, (void*)ppApi, "fts5_api_ptr", 0);
&& sizeof(fts5_api*)==sqlite3_column_bytes(pStmt, 0) (void)sqlite3_step(pStmt);
){
memcpy(ppApi, sqlite3_column_blob(pStmt, 0), sizeof(fts5_api*));
}
rc = sqlite3_finalize(pStmt); rc = sqlite3_finalize(pStmt);
} }
@ -422,4 +419,3 @@ int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db){
} }
#endif /* SQLITE_ENABLE_FTS5 */ #endif /* SQLITE_ENABLE_FTS5 */

View File

@ -182,7 +182,7 @@ static int fts5tokConnectMethod(
Fts5tokTable *pTab = 0; Fts5tokTable *pTab = 0;
int rc; int rc;
char **azDequote = 0; char **azDequote = 0;
int nDequote; int nDequote = 0;
rc = sqlite3_declare_vtab(db, rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(input HIDDEN, token, start, end, position)" "CREATE TABLE x(input HIDDEN, token, start, end, position)"

View File

@ -29,6 +29,11 @@
** the number of fts5 rows that contain at least one instance of term ** the number of fts5 rows that contain at least one instance of term
** $term. Field $cnt is set to the total number of instances of term ** $term. Field $cnt is set to the total number of instances of term
** $term in the database. ** $term in the database.
**
** instance:
** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>));
**
** One row for each term instance in the database.
*/ */
@ -44,7 +49,7 @@ struct Fts5VocabTable {
char *zFts5Db; /* Db containing fts5 table */ char *zFts5Db; /* Db containing fts5 table */
sqlite3 *db; /* Database handle */ sqlite3 *db; /* Database handle */
Fts5Global *pGlobal; /* FTS5 global object for this database */ Fts5Global *pGlobal; /* FTS5 global object for this database */
int eType; /* FTS5_VOCAB_COL or ROW */ int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */
}; };
struct Fts5VocabCursor { struct Fts5VocabCursor {
@ -64,16 +69,22 @@ struct Fts5VocabCursor {
i64 *aCnt; i64 *aCnt;
i64 *aDoc; i64 *aDoc;
/* Output values used by 'row' and 'col' tables */ /* Output values used by all tables. */
i64 rowid; /* This table's current rowid value */ i64 rowid; /* This table's current rowid value */
Fts5Buffer term; /* Current value of 'term' column */ Fts5Buffer term; /* Current value of 'term' column */
/* Output values Used by 'instance' tables only */
i64 iInstPos;
int iInstOff;
}; };
#define FTS5_VOCAB_COL 0 #define FTS5_VOCAB_COL 0
#define FTS5_VOCAB_ROW 1 #define FTS5_VOCAB_ROW 1
#define FTS5_VOCAB_INSTANCE 2
#define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt" #define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt"
#define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt" #define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt"
#define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset"
/* /*
** Bits for the mask used as the idxNum value by xBestIndex/xFilter. ** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
@ -101,6 +112,9 @@ static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){
if( sqlite3_stricmp(zCopy, "row")==0 ){ if( sqlite3_stricmp(zCopy, "row")==0 ){
*peType = FTS5_VOCAB_ROW; *peType = FTS5_VOCAB_ROW;
}else }else
if( sqlite3_stricmp(zCopy, "instance")==0 ){
*peType = FTS5_VOCAB_INSTANCE;
}else
{ {
*pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy); *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
@ -161,7 +175,8 @@ static int fts5VocabInitVtab(
){ ){
const char *azSchema[] = { const char *azSchema[] = {
"CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")", "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
"CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")" "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")",
"CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")"
}; };
Fts5VocabTable *pRet = 0; Fts5VocabTable *pRet = 0;
@ -235,6 +250,15 @@ static int fts5VocabCreateMethod(
/* /*
** Implementation of the xBestIndex method. ** Implementation of the xBestIndex method.
**
** Only constraints of the form:
**
** term <= ?
** term == ?
** term >= ?
**
** are interpreted. Less-than and less-than-or-equal are treated
** identically, as are greater-than and greater-than-or-equal.
*/ */
static int fts5VocabBestIndexMethod( static int fts5VocabBestIndexMethod(
sqlite3_vtab *pUnused, sqlite3_vtab *pUnused,
@ -378,6 +402,54 @@ static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
return SQLITE_OK; return SQLITE_OK;
} }
static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){
int rc = SQLITE_OK;
if( sqlite3Fts5IterEof(pCsr->pIter) ){
pCsr->bEof = 1;
}else{
const char *zTerm;
int nTerm;
zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
if( pCsr->nLeTerm>=0 ){
int nCmp = MIN(nTerm, pCsr->nLeTerm);
int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){
pCsr->bEof = 1;
}
}
sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
}
return rc;
}
static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){
int eDetail = pCsr->pConfig->eDetail;
int rc = SQLITE_OK;
Fts5IndexIter *pIter = pCsr->pIter;
i64 *pp = &pCsr->iInstPos;
int *po = &pCsr->iInstOff;
while( eDetail==FTS5_DETAIL_NONE
|| sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp)
){
pCsr->iInstPos = 0;
pCsr->iInstOff = 0;
rc = sqlite3Fts5IterNextScan(pCsr->pIter);
if( rc==SQLITE_OK ){
rc = fts5VocabInstanceNewTerm(pCsr);
if( eDetail==FTS5_DETAIL_NONE ) break;
}
if( rc ){
pCsr->bEof = 1;
break;
}
}
return rc;
}
/* /*
** Advance the cursor to the next row in the table. ** Advance the cursor to the next row in the table.
@ -390,13 +462,17 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
pCsr->rowid++; pCsr->rowid++;
if( pTab->eType==FTS5_VOCAB_INSTANCE ){
return fts5VocabInstanceNext(pCsr);
}
if( pTab->eType==FTS5_VOCAB_COL ){ if( pTab->eType==FTS5_VOCAB_COL ){
for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){
if( pCsr->aDoc[pCsr->iCol] ) break; if( pCsr->aDoc[pCsr->iCol] ) break;
} }
} }
if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){ if( pTab->eType!=FTS5_VOCAB_COL || pCsr->iCol>=nCol ){
if( sqlite3Fts5IterEof(pCsr->pIter) ){ if( sqlite3Fts5IterEof(pCsr->pIter) ){
pCsr->bEof = 1; pCsr->bEof = 1;
}else{ }else{
@ -420,22 +496,26 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
while( rc==SQLITE_OK ){ while( rc==SQLITE_OK ){
int eDetail = pCsr->pConfig->eDetail;
const u8 *pPos; int nPos; /* Position list */ const u8 *pPos; int nPos; /* Position list */
i64 iPos = 0; /* 64-bit position read from poslist */ i64 iPos = 0; /* 64-bit position read from poslist */
int iOff = 0; /* Current offset within position list */ int iOff = 0; /* Current offset within position list */
pPos = pCsr->pIter->pData; pPos = pCsr->pIter->pData;
nPos = pCsr->pIter->nData; nPos = pCsr->pIter->nData;
switch( pCsr->pConfig->eDetail ){
case FTS5_DETAIL_FULL: switch( pTab->eType ){
pPos = pCsr->pIter->pData; case FTS5_VOCAB_ROW:
nPos = pCsr->pIter->nData; if( eDetail==FTS5_DETAIL_FULL ){
if( pTab->eType==FTS5_VOCAB_ROW ){
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
pCsr->aCnt[0]++; pCsr->aCnt[0]++;
} }
pCsr->aDoc[0]++; }
}else{ pCsr->aDoc[0]++;
break;
case FTS5_VOCAB_COL:
if( eDetail==FTS5_DETAIL_FULL ){
int iCol = -1; int iCol = -1;
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
int ii = FTS5_POS2COLUMN(iPos); int ii = FTS5_POS2COLUMN(iPos);
@ -449,13 +529,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
iCol = ii; iCol = ii;
} }
} }
} }else if( eDetail==FTS5_DETAIL_COLUMNS ){
break;
case FTS5_DETAIL_COLUMNS:
if( pTab->eType==FTS5_VOCAB_ROW ){
pCsr->aDoc[0]++;
}else{
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
assert_nc( iPos>=0 && iPos<nCol ); assert_nc( iPos>=0 && iPos<nCol );
if( iPos>=nCol ){ if( iPos>=nCol ){
@ -464,18 +538,21 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
} }
pCsr->aDoc[iPos]++; pCsr->aDoc[iPos]++;
} }
}else{
assert( eDetail==FTS5_DETAIL_NONE );
pCsr->aDoc[0]++;
} }
break; break;
default: default:
assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE ); assert( pTab->eType==FTS5_VOCAB_INSTANCE );
pCsr->aDoc[0]++;
break; break;
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3Fts5IterNextScan(pCsr->pIter); rc = sqlite3Fts5IterNextScan(pCsr->pIter);
} }
if( pTab->eType==FTS5_VOCAB_INSTANCE ) break;
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
@ -505,7 +582,9 @@ static int fts5VocabFilterMethod(
int nUnused, /* Number of elements in apVal */ int nUnused, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */ sqlite3_value **apVal /* Arguments for the indexing scheme */
){ ){
Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
int eType = pTab->eType;
int rc = SQLITE_OK; int rc = SQLITE_OK;
int iVal = 0; int iVal = 0;
@ -545,11 +624,16 @@ static int fts5VocabFilterMethod(
} }
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){
rc = fts5VocabInstanceNewTerm(pCsr);
}
if( rc==SQLITE_OK
&& !pCsr->bEof
&& (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE)
){
rc = fts5VocabNextMethod(pCursor); rc = fts5VocabNextMethod(pCursor);
} }
@ -591,13 +675,41 @@ static int fts5VocabColumnMethod(
}else{ }else{
iVal = pCsr->aCnt[pCsr->iCol]; iVal = pCsr->aCnt[pCsr->iCol];
} }
}else{ }else if( eType==FTS5_VOCAB_ROW ){
assert( iCol==1 || iCol==2 ); assert( iCol==1 || iCol==2 );
if( iCol==1 ){ if( iCol==1 ){
iVal = pCsr->aDoc[0]; iVal = pCsr->aDoc[0];
}else{ }else{
iVal = pCsr->aCnt[0]; iVal = pCsr->aCnt[0];
} }
}else{
assert( eType==FTS5_VOCAB_INSTANCE );
switch( iCol ){
case 1:
sqlite3_result_int64(pCtx, pCsr->pIter->iRowid);
break;
case 2: {
int ii = -1;
if( eDetail==FTS5_DETAIL_FULL ){
ii = FTS5_POS2COLUMN(pCsr->iInstPos);
}else if( eDetail==FTS5_DETAIL_COLUMNS ){
ii = (int)pCsr->iInstPos;
}
if( ii>=0 && ii<pCsr->pConfig->nCol ){
const char *z = pCsr->pConfig->azCol[ii];
sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
}
break;
}
default: {
assert( iCol==3 );
if( eDetail==FTS5_DETAIL_FULL ){
int ii = FTS5_POS2OFFSET(pCsr->iInstPos);
sqlite3_result_int(pCtx, ii);
}
break;
}
}
} }
if( iVal>0 ) sqlite3_result_int64(pCtx, iVal); if( iVal>0 ) sqlite3_result_int64(pCtx, iVal);

View File

@ -441,7 +441,7 @@ db func funk funk
do_catchsql_test 16.2 { do_catchsql_test 16.2 {
SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d' SELECT funk(), bm25(n1), funk() FROM n1 WHERE n1 MATCH 'a+b+c+d'
} {0 {{} -1e-06 {}}} } {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 finish_test

View File

@ -294,4 +294,3 @@ do_execsql_test 7.0 {
finish_test finish_test

View File

@ -276,4 +276,3 @@ foreach {tn expr tclexpr} {
} }
finish_test finish_test

View File

@ -231,7 +231,6 @@ foreach {T create} {
set res [lsort -integer -increasing $res] set res [lsort -integer -increasing $res]
} }
set n [llength $res] set n [llength $res]
if {$T==5} breakpoint
do_execsql_test $T.$bAsc.$tn.$n $sql $res do_execsql_test $T.$bAsc.$tn.$n $sql $res
} }
} }
@ -242,4 +241,3 @@ foreach {T create} {
} }
finish_test finish_test

View File

@ -309,4 +309,3 @@ foreach {tn q cnt} {
} }
finish_test finish_test

View File

@ -178,4 +178,3 @@ do_execsql_test 5.1 {
} ;# foreach_detail_mode } ;# foreach_detail_mode
finish_test finish_test

View File

@ -142,4 +142,3 @@ if {[detail_is_full]} {
finish_test finish_test

View File

@ -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} #db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
finish_test finish_test

View File

@ -55,4 +55,3 @@ do_execsql_test 1.2 {
finish_test finish_test

View File

@ -66,4 +66,3 @@ do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') }
finish_test finish_test

View File

@ -147,4 +147,3 @@ do_execsql_test 3.1 {
} }
finish_test finish_test

View File

@ -77,7 +77,7 @@ foreach {tn defn} {
} { } {
do_test 2.2.$tn { do_test 2.2.$tn {
catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) } 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 finish_test

View File

@ -89,7 +89,6 @@ do_execsql_test 3.1 {
BEGIN; BEGIN;
INSERT INTO abc(rowid, a) VALUES(2, 'a'); INSERT INTO abc(rowid, a) VALUES(2, 'a');
} }
breakpoint
do_execsql_test 3.2 { do_execsql_test 3.2 {
SELECT rowid FROM abc WHERE abc MATCH 'a'; SELECT rowid FROM abc WHERE abc MATCH 'a';
} {1 2} } {1 2}
@ -100,4 +99,3 @@ do_execsql_test 3.3 {
} {1 2} } {1 2}
finish_test finish_test

View File

@ -342,4 +342,3 @@ foreach {tn expr} {
finish_test finish_test

View File

@ -240,7 +240,6 @@ foreach {tn lRow res} {
} { } {
execsql { DELETE FROM x1 } execsql { DELETE FROM x1 }
foreach row $lRow { execsql { INSERT INTO x1 VALUES($row) } } foreach row $lRow { execsql { INSERT INTO x1 VALUES($row) } }
breakpoint
do_execsql_test 8.$tn { do_execsql_test 8.$tn {
SELECT highlight(x1, 0, '[', ']') FROM x1 WHERE x1 MATCH 'a OR (b AND d)'; SELECT highlight(x1, 0, '[', ']') FROM x1 WHERE x1 MATCH 'a OR (b AND d)';
} $res } $res
@ -279,4 +278,3 @@ do_execsql_test 9.3 {
finish_test finish_test

View File

@ -112,4 +112,3 @@ db eval {
} }
finish_test finish_test

View File

@ -61,4 +61,3 @@ do_test 2.1...slow {
} {} } {}
finish_test finish_test

View File

@ -64,5 +64,3 @@ foreach_detail_mode $::testprefix {
} }
finish_test finish_test

View File

@ -84,5 +84,3 @@ foreach_detail_mode $::testprefix {
finish_test finish_test

View File

@ -143,7 +143,6 @@ do_execsql_test 4.1.1 {
INSERT INTO t5 VALUES('2 4 6 8'); INSERT INTO t5 VALUES('2 4 6 8');
} }
breakpoint
do_execsql_test 4.1.2 { do_execsql_test 4.1.2 {
INSERT INTO t5(t5) VALUES('integrity-check'); INSERT INTO t5(t5) VALUES('integrity-check');
} }

View File

@ -66,7 +66,7 @@ foreach {tn val} {
} { } {
do_catchsql_test 3.$tn { do_catchsql_test 3.$tn {
INSERT INTO t1(t1, rank) VALUES('rank', $val); INSERT INTO t1(t1, rank) VALUES('rank', $val);
} {1 {SQL logic error or missing database}} } {1 {SQL logic error}}
} }
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -110,7 +110,6 @@ do_catchsql_test 5.1 {
CREATE VIRTUAL TABLE xx USING fts5(x, tokenize="porter 'ascii"); CREATE VIRTUAL TABLE xx USING fts5(x, tokenize="porter 'ascii");
} {1 {parse error in tokenize directive}} } {1 {parse error in tokenize directive}}
breakpoint
do_catchsql_test 5.2 { do_catchsql_test 5.2 {
CREATE VIRTUAL TABLE xx USING fts5(x, [y[]); CREATE VIRTUAL TABLE xx USING fts5(x, [y[]);
} {0 {}} } {0 {}}
@ -169,33 +168,33 @@ do_execsql_test 9.0 {
} {} } {}
do_catchsql_test 9.1.1 { do_catchsql_test 9.1.1 {
INSERT INTO abc(abc, rank) VALUES('pgsz', -5); 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 { do_catchsql_test 9.1.2 {
INSERT INTO abc(abc, rank) VALUES('pgsz', 50000000); 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 { do_catchsql_test 9.1.3 {
INSERT INTO abc(abc, rank) VALUES('pgsz', 66.67); 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 { do_catchsql_test 9.2.1 {
INSERT INTO abc(abc, rank) VALUES('automerge', -5); 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 { do_catchsql_test 9.2.2 {
INSERT INTO abc(abc, rank) VALUES('automerge', 50000000); 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 { do_catchsql_test 9.2.3 {
INSERT INTO abc(abc, rank) VALUES('automerge', 66.67); 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 { do_execsql_test 9.2.4 {
INSERT INTO abc(abc, rank) VALUES('automerge', 1); INSERT INTO abc(abc, rank) VALUES('automerge', 1);
} {} } {}
do_catchsql_test 9.3.1 { do_catchsql_test 9.3.1 {
INSERT INTO abc(abc, rank) VALUES('crisismerge', -5); 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 { do_catchsql_test 9.3.2 {
INSERT INTO abc(abc, rank) VALUES('crisismerge', 66.67); 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 { do_execsql_test 9.3.3 {
INSERT INTO abc(abc, rank) VALUES('crisismerge', 1); INSERT INTO abc(abc, rank) VALUES('crisismerge', 1);
} {} } {}
@ -205,14 +204,14 @@ do_execsql_test 9.3.4 {
do_catchsql_test 9.4.1 { do_catchsql_test 9.4.1 {
INSERT INTO abc(abc, rank) VALUES('nosuchoption', 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 { do_catchsql_test 9.5.1 {
INSERT INTO abc(abc, rank) VALUES('hashsize', 'not an integer'); 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 { do_catchsql_test 9.5.2 {
INSERT INTO abc(abc, rank) VALUES('hashsize', -500000); 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 { do_catchsql_test 9.5.3 {
INSERT INTO abc(abc, rank) VALUES('hashsize', 500000); INSERT INTO abc(abc, rank) VALUES('hashsize', 500000);
} {0 {}} } {0 {}}
@ -245,7 +244,7 @@ foreach {tn opt} {
do_catchsql_test 12.1 { do_catchsql_test 12.1 {
INSERT INTO t1(t1, rank) VALUES('rank', NULL);; INSERT INTO t1(t1, rank) VALUES('rank', NULL);;
} {1 {SQL logic error or missing database}} } {1 {SQL logic error}}
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# errors in the 'usermerge' option # errors in the 'usermerge' option
@ -260,8 +259,7 @@ foreach {tn val} {
4 1 4 1
} { } {
set sql "INSERT INTO tt(tt, rank) VALUES('usermerge', $val)" 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 finish_test

View File

@ -66,5 +66,3 @@ do_execsql_test 2.1 {
} }
finish_test finish_test

View File

@ -0,0 +1,247 @@
# 2017 August 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.
#
#*************************************************************************
#
source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5connect
ifcapable !fts5 {
finish_test
return
}
#-------------------------------------------------------------------------
# The tests in this file test the outcome of a schema-reset happening
# within the xConnect() method of an FTS5 table. At one point this
# was causing a problem in SQLite. Each test proceeds as follows:
#
# 1. Connection [db] opens the db and reads from some unrelated, non-FTS5
# table causing SQLite to load the db schema into memory.
#
# 2. Connection [db2] opens the db and modifies the db schema.
#
# 3. Connection [db] reads or writes an existing fts5 table. That the
# schema has been modified is detected inside the fts5 xConnect()
# callback that is invoked by sqlite3_prepare().
#
# 4. Verify that the statement in 3 has worked. SQLite should detect
# that the schema has changed and successfully prepare the
# statement against the new schema.
#
# Test plan:
#
# 1.*: Trigger the xConnect()/schema-reset using statements executed
# directly against an FTS5 table.
#
# 2.*: Using various statements executed by various BEFORE triggers.
#
# 3.*: Using various statements executed by various AFTER triggers.
#
# 4.*: Using various statements executed by various INSTEAD OF triggers.
#
do_execsql_test 1.0 {
CREATE VIRTUAL TABLE ft1 USING fts5(a, b);
CREATE TABLE abc(x INTEGER PRIMARY KEY);
CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
INSERT INTO ft1 VALUES('one', 'two');
INSERT INTO ft1 VALUES('three', 'four');
}
foreach {tn sql res} {
1 "SELECT * FROM ft1" {one two three four}
2 "REPLACE INTO ft1(rowid, a, b) VALUES(1, 'five', 'six')" {}
3 "SELECT * FROM ft1" {five six three four}
4 "INSERT INTO ft1 VALUES('seven', 'eight')" {}
5 "SELECT * FROM ft1" {five six three four seven eight}
6 "DELETE FROM ft1 WHERE rowid=2" {}
7 "UPDATE ft1 SET b='nine' WHERE rowid=1" {}
8 "SELECT * FROM ft1" {five nine seven eight}
} {
catch { db close }
catch { db2 close }
sqlite3 db test.db
sqlite3 db2 test.db
do_test 1.$tn.1 {
db eval { INSERT INTO abc DEFAULT VALUES }
db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable }
} {}
do_execsql_test 1.$tn.2 $sql $res
do_execsql_test 1.$tn.3 {
INSERT INTO ft1(ft1) VALUES('integrity-check');
}
}
do_execsql_test 2.0 {
CREATE VIRTUAL TABLE ft2 USING fts5(a, b);
CREATE TABLE t2(a, b);
CREATE TABLE log(txt);
CREATE TRIGGER t2_ai AFTER INSERT ON t2 BEGIN
INSERT INTO ft2(rowid, a, b) VALUES(new.rowid, new.a, new.b);
INSERT INTO log VALUES('insert');
END;
CREATE TRIGGER t2_ad AFTER DELETE ON t2 BEGIN
DELETE FROM ft2 WHERE rowid = old.rowid;
INSERT INTO log VALUES('delete');
END;
CREATE TRIGGER t2_au AFTER UPDATE ON t2 BEGIN
UPDATE ft2 SET a=new.a, b=new.b WHERE rowid=new.rowid;
INSERT INTO log VALUES('update');
END;
INSERT INTO t2 VALUES('one', 'two');
INSERT INTO t2 VALUES('three', 'four');
}
foreach {tn sql res} {
1 "SELECT * FROM t2" {one two three four}
2 "REPLACE INTO t2(rowid, a, b) VALUES(1, 'five', 'six')" {}
3 "SELECT * FROM ft2" {five six three four}
4 "INSERT INTO t2 VALUES('seven', 'eight')" {}
5 "SELECT * FROM ft2" {five six three four seven eight}
6 "DELETE FROM t2 WHERE rowid=2" {}
7 "UPDATE t2 SET b='nine' WHERE rowid=1" {}
8 "SELECT * FROM ft2" {five nine seven eight}
} {
catch { db close }
catch { db2 close }
sqlite3 db test.db
sqlite3 db2 test.db
do_test 2.$tn.1 {
db eval { INSERT INTO abc DEFAULT VALUES }
db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable }
} {}
do_execsql_test 2.$tn.2 $sql $res
do_execsql_test 2.$tn.3 {
INSERT INTO ft2(ft2) VALUES('integrity-check');
}
}
do_execsql_test 3.0 {
CREATE VIRTUAL TABLE ft3 USING fts5(a, b);
CREATE TABLE t3(a, b);
CREATE TRIGGER t3_ai BEFORE INSERT ON t3 BEGIN
INSERT INTO ft3(rowid, a, b) VALUES(new.rowid, new.a, new.b);
INSERT INTO log VALUES('insert');
END;
CREATE TRIGGER t3_ad BEFORE DELETE ON t3 BEGIN
DELETE FROM ft3 WHERE rowid = old.rowid;
INSERT INTO log VALUES('delete');
END;
CREATE TRIGGER t3_au BEFORE UPDATE ON t3 BEGIN
UPDATE ft3 SET a=new.a, b=new.b WHERE rowid=new.rowid;
INSERT INTO log VALUES('update');
END;
INSERT INTO t3(rowid, a, b) VALUES(1, 'one', 'two');
INSERT INTO t3(rowid, a, b) VALUES(2, 'three', 'four');
}
foreach {tn sql res} {
1 "SELECT * FROM t3" {one two three four}
2 "REPLACE INTO t3(rowid, a, b) VALUES(1, 'five', 'six')" {}
3 "SELECT * FROM ft3" {five six three four}
4 "INSERT INTO t3(rowid, a, b) VALUES(3, 'seven', 'eight')" {}
5 "SELECT * FROM ft3" {five six three four seven eight}
6 "DELETE FROM t3 WHERE rowid=2" {}
7 "UPDATE t3 SET b='nine' WHERE rowid=1" {}
8 "SELECT * FROM ft3" {five nine seven eight}
} {
catch { db close }
catch { db2 close }
sqlite3 db test.db
sqlite3 db2 test.db
do_test 3.$tn.1 {
db eval { INSERT INTO abc DEFAULT VALUES }
db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable }
} {}
do_execsql_test 3.$tn.2 $sql $res
do_execsql_test 3.$tn.3 {
INSERT INTO ft3(ft3) VALUES('integrity-check');
}
}
do_execsql_test 4.0 {
CREATE VIRTUAL TABLE ft4 USING fts5(a, b);
CREATE VIEW v4 AS SELECT rowid, * FROM ft4;
CREATE TRIGGER t4_ai INSTEAD OF INSERT ON v4 BEGIN
INSERT INTO ft4(rowid, a, b) VALUES(new.rowid, new.a, new.b);
INSERT INTO log VALUES('insert');
END;
CREATE TRIGGER t4_ad INSTEAD OF DELETE ON v4 BEGIN
DELETE FROM ft4 WHERE rowid = old.rowid;
INSERT INTO log VALUES('delete');
END;
CREATE TRIGGER t4_au INSTEAD OF UPDATE ON v4 BEGIN
UPDATE ft4 SET a=new.a, b=new.b WHERE rowid=new.rowid;
INSERT INTO log VALUES('update');
END;
INSERT INTO ft4(rowid, a, b) VALUES(1, 'one', 'two');
INSERT INTO ft4(rowid, a, b) VALUES(2, 'three', 'four');
}
foreach {tn sql res} {
1 "SELECT * FROM ft4" {one two three four}
2 "REPLACE INTO v4(rowid, a, b) VALUES(1, 'five', 'six')" {}
3 "SELECT * FROM ft4" {five six three four}
4 "INSERT INTO v4(rowid, a, b) VALUES(3, 'seven', 'eight')" {}
5 "SELECT * FROM ft4" {five six three four seven eight}
6 "DELETE FROM v4 WHERE rowid=2" {}
7 "UPDATE v4 SET b='nine' WHERE rowid=1" {}
8 "SELECT * FROM ft4" {five nine seven eight}
} {
catch { db close }
catch { db2 close }
sqlite3 db test.db
sqlite3 db2 test.db
do_test 4.$tn.1 {
db eval { INSERT INTO abc DEFAULT VALUES }
db2 eval { CREATE TABLE newtable(x,y); DROP TABLE newtable }
} {}
do_execsql_test 4.$tn.2 $sql $res
do_execsql_test 4.$tn.3 {
INSERT INTO ft3(ft3) VALUES('integrity-check');
}
}
finish_test

View File

@ -255,4 +255,3 @@ do_execsql_test 6.2 {
finish_test finish_test

View File

@ -96,4 +96,3 @@ do_catchsql_test 3.1 {
} {1 {database disk image is malformed}} } {1 {database disk image is malformed}}
finish_test finish_test

View File

@ -269,4 +269,3 @@ do_catchsql_test 6.2 {
sqlite3_fts5_may_be_corrupt 0 sqlite3_fts5_may_be_corrupt 0
finish_test finish_test

View File

@ -409,4 +409,3 @@ do_catchsql_test 9.2.2 {
sqlite3_fts5_may_be_corrupt 0 sqlite3_fts5_may_be_corrupt 0
finish_test finish_test

View File

@ -51,4 +51,3 @@ do_test 1.2 {
} {} } {}
finish_test finish_test

View File

@ -241,4 +241,3 @@ do_execsql_test 5.3 {
finish_test finish_test

View File

@ -63,5 +63,3 @@ foreach_detail_mode $::testprefix {
finish_test finish_test

View File

@ -66,7 +66,6 @@ proc do_dlidx_test1 {tn spc1 spc2 nEntry iFirst nStep} {
} }
execsql COMMIT execsql COMMIT
breakpoint
do_test $tn.1 { do_test $tn.1 {
execsql { INSERT INTO t1(t1) VALUES('integrity-check') } execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
} {} } {}
@ -124,7 +123,6 @@ proc do_dlidx_test2 {tn nEntry iFirst nStep} {
do_execsql_test $tn.1 { do_execsql_test $tn.1 {
SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a'
} {1} } {1}
breakpoint
do_execsql_test $tn.2 { do_execsql_test $tn.2 {
SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' ORDER BY rowid DESC SELECT rowid FROM t1 WHERE t1 MATCH 'b AND a' ORDER BY rowid DESC
} {1} } {1}
@ -197,4 +195,3 @@ foreach v $vocab {
finish_test finish_test

View File

@ -44,4 +44,3 @@ do_execsql_test 1.2 {
finish_test finish_test

View File

@ -81,6 +81,3 @@ do_execsql_test 3.3 {
finish_test finish_test

View File

@ -351,4 +351,3 @@ do_faultsim_test 9.1 -faults oom-* -prep {
finish_test finish_test

View File

@ -137,4 +137,3 @@ do_faultsim_test 5.0 -faults oom-* -prep {
} }
finish_test finish_test

View File

@ -110,4 +110,3 @@ do_faultsim_test 3.2 -faults oom-* -prep {
finish_test finish_test

View File

@ -395,4 +395,3 @@ do_faultsim_test 14.1 -faults oom-t* -prep {
} }
finish_test finish_test

View File

@ -105,7 +105,6 @@ do_faultsim_test 3.2 -faults oom-t* -body {
faultsim_test_result {0 {1 10 11 12 13 14 15 16 17 18 19 2}} faultsim_test_result {0 {1 10 11 12 13 14 15 16 17 18 19 2}}
} }
breakpoint
do_execsql_test 3.3.0 { do_execsql_test 3.3.0 {
SELECT * FROM tv2; SELECT * FROM tv2;
} { } {
@ -130,4 +129,3 @@ do_faultsim_test 3.3 -faults oom-t* -body {
finish_test finish_test

View File

@ -280,7 +280,6 @@ do_faultsim_test 5.4 -faults oom* -prep {
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
catch { db close } catch { db close }
breakpoint
do_faultsim_test 6 -faults oom* -prep { do_faultsim_test 6 -faults oom* -prep {
sqlite_orig db test.db sqlite_orig db test.db
sqlite3_db_config_lookaside db 0 0 0 sqlite3_db_config_lookaside db 0 0 0
@ -292,4 +291,3 @@ do_faultsim_test 6 -faults oom* -prep {
db close db close
} }
finish_test finish_test

View File

@ -116,4 +116,3 @@ do_faultsim_test 2.2 -faults oom-* -body {
} }
finish_test finish_test

View File

@ -82,4 +82,3 @@ do_faultsim_test 4 -faults oom-* -prep {
finish_test finish_test

View File

@ -153,4 +153,3 @@ do_faultsim_test 6 -faults oom-* -body {
} ;# foreach_detail_mode... } ;# foreach_detail_mode...
finish_test finish_test

View File

@ -61,4 +61,3 @@ do_faultsim_test 2 -faults oom* -prep {
faultsim_test_result {0 {1 2}} faultsim_test_result {0 {1 2}}
} }
finish_test finish_test

View File

@ -132,4 +132,3 @@ do_faultsim_test 4.2 -faults oom* -body {
finish_test finish_test

View File

@ -40,4 +40,3 @@ do_test 1.1 {
finish_test finish_test

View File

@ -90,4 +90,3 @@ do_catchsql_test 4.1 {
} {1 {fts5: syntax error near "`"}} } {1 {fts5: syntax error near "`"}}
finish_test finish_test

View File

@ -121,7 +121,6 @@ foreach_detail_mode $testprefix {
} }
execsql { CREATE VIRTUAL TABLE t2 USING fts5(x, detail=%DETAIL%) } execsql { CREATE VIRTUAL TABLE t2 USING fts5(x, detail=%DETAIL%) }
breakpoint
execsql { execsql {
INSERT INTO t2 VALUES($small || ' ' || $big); INSERT INTO t2 VALUES($small || ' ' || $big);
} }
@ -130,4 +129,3 @@ breakpoint
} ;# foreach_detail_mode } ;# foreach_detail_mode
finish_test finish_test

View File

@ -210,4 +210,3 @@ foreach {tn pgsz} {
} }
finish_test finish_test

View File

@ -70,4 +70,3 @@ do_execsql_test 1.6 {
finish_test finish_test

View File

@ -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

View File

@ -472,7 +472,7 @@ do_execsql_test 12.1 {
# #
reset_db reset_db
proc xyz {} {} proc xyz {} {}
db func fts5 -argcount 0 xyz db func fts5 -argcount 1 xyz
do_test 13.1 { do_test 13.1 {
list [catch { sqlite3_fts5_register_matchinfo db } msg] $msg list [catch { sqlite3_fts5_register_matchinfo db } msg] $msg
} {1 SQLITE_ERROR} } {1 SQLITE_ERROR}
@ -492,4 +492,3 @@ do_catchsql_test 14.2 {
} {1 {unrecognized matchinfo flag: d}} } {1 {unrecognized matchinfo flag: d}}
finish_test finish_test

View File

@ -241,4 +241,3 @@ do_execsql_test 6.3 {
finish_test finish_test

View File

@ -55,4 +55,3 @@ do_execsql_test 1.2 {
} }
finish_test finish_test

View File

@ -45,4 +45,3 @@ do_multiclient_test tn {
};# do_multiclient_test };# do_multiclient_test
};# foreach_detail_mode };# foreach_detail_mode
finish_test finish_test

View File

@ -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 finish_test

View File

@ -178,4 +178,3 @@ do_execsql_test 4.3.1 {
do_test 4.2.2 { fts5_level_segs ttt } {3} do_test 4.2.2 { fts5_level_segs ttt } {3}
finish_test finish_test

View File

@ -106,4 +106,3 @@ foreach {tn nStep} {
do_test 2.$tn.6 { fts5_segcount t1 } 1 do_test 2.$tn.6 { fts5_segcount t1 } 1
} }
finish_test finish_test

View File

@ -116,4 +116,3 @@ do_execsql_test 2.0 {
} }
finish_test finish_test

View File

@ -64,4 +64,3 @@ do_eqp_test 1.5 {
finish_test finish_test

View File

@ -11803,4 +11803,3 @@ foreach {in out} $test_vocab {
finish_test finish_test

View File

@ -67,4 +67,3 @@ foreach {in out} $test_vocab {
finish_test finish_test

View File

@ -341,5 +341,3 @@ foreach {tn create} {
} }
finish_test finish_test

View File

@ -79,5 +79,3 @@ for {set tn 1 ; set pgsz 64} {$tn<32} {incr tn; incr pgsz 16} {
finish_test finish_test

View File

@ -90,6 +90,7 @@ do_test 2.7 {
execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db execsql { SELECT rowid FROM tt('a') ORDER BY rank; } db
} {1 3 2} } {1 3 2}
db2 close
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
# At one point there was a problem with queries such as: # At one point there was a problem with queries such as:
@ -151,4 +152,3 @@ do_execsql_test 4.1 {
finish_test finish_test

View File

@ -64,4 +64,3 @@ do_catchsql_test 2.2 {
INSERT INTO nc(nc) VALUES('rebuild'); INSERT INTO nc(nc) VALUES('rebuild');
} {1 {'rebuild' may not be used with a contentless fts5 table}} } {1 {'rebuild' may not be used with a contentless fts5 table}}
finish_test finish_test

View File

@ -149,4 +149,3 @@ do_test 4.3 {
finish_test finish_test

View File

@ -216,4 +216,3 @@ do_execsql_test 6.2 {
finish_test finish_test

View File

@ -411,7 +411,6 @@ do_catchsql_test 19.2 {
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
reset_db reset_db
breakpoint
do_execsql_test 20.0 { do_execsql_test 20.0 {
CREATE VIRTUAL TABLE x1 USING fts5(x); CREATE VIRTUAL TABLE x1 USING fts5(x);
INSERT INTO x1(x1, rank) VALUES('pgsz', 32); INSERT INTO x1(x1, rank) VALUES('pgsz', 32);

View File

@ -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} #db eval {SELECT rowid, fts5_decode_none(rowid, block) aS r FROM t2_data} {puts $r}
finish_test finish_test

View File

@ -116,4 +116,3 @@ do_execsql_test 4.6 {
finish_test finish_test

View File

@ -421,4 +421,3 @@ do_execsql_test 7.1.2 {
} ;# foreach_detail_mode } ;# foreach_detail_mode
finish_test finish_test

View File

@ -161,4 +161,3 @@ foreach {tn expr} {
} }
finish_test finish_test

View File

@ -109,7 +109,7 @@ do_catchsql_test 2.0 {
do_catchsql_test 2.1 { do_catchsql_test 2.1 {
CREATE VIRTUAL TABLE t4 USING fts5tokenize; CREATE VIRTUAL TABLE t4 USING fts5tokenize;
SELECT * FROM t4; SELECT * FROM t4;
} {1 {SQL logic error or missing database}} } {1 {SQL logic error}}
finish_test finish_test

View File

@ -302,4 +302,3 @@ do_test 9.5.2 { set ::flags } {query}
finish_test finish_test

View File

@ -50,7 +50,6 @@ do_execsql_test 2.0 "
INSERT INTO t2 VALUES('\xC0\xC8\xCC'); INSERT INTO t2 VALUES('\xC0\xC8\xCC');
INSERT INTO t3 VALUES('\xC0\xC8\xCC'); INSERT INTO t3 VALUES('\xC0\xC8\xCC');
" "
breakpoint
do_execsql_test 2.1 " do_execsql_test 2.1 "
SELECT 't1' FROM t1 WHERE t1 MATCH '\xE0\xE8\xEC'; SELECT 't1' FROM t1 WHERE t1 MATCH '\xE0\xE8\xEC';
SELECT 't2' FROM t2 WHERE t2 MATCH '\xE0\xE8\xEC'; SELECT 't2' FROM t2 WHERE t2 MATCH '\xE0\xE8\xEC';
@ -59,4 +58,3 @@ do_execsql_test 2.1 "
finish_test finish_test

View File

@ -281,7 +281,6 @@ do_test 4.4 {
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
breakpoint
do_unicode_token_test3 5.1 {tokenchars {}} { do_unicode_token_test3 5.1 {tokenchars {}} {
sqlite3_reset sqlite3_column_int sqlite3_reset sqlite3_column_int
} { } {

View File

@ -126,4 +126,3 @@ do_test 1.5 {
finish_test finish_test

View File

@ -76,4 +76,3 @@ do_execsql_test 3.2 {
finish_test finish_test

View File

@ -117,5 +117,3 @@ do_execsql_test 2.2.integrity {
} }
finish_test finish_test

View File

@ -61,4 +61,3 @@ do_test 1.7 {
finish_test finish_test

View File

@ -210,7 +210,6 @@ do_execsql_test 5.0 {
INSERT INTO aux.t1 VALUES('x n z'); INSERT INTO aux.t1 VALUES('x n z');
} }
breakpoint
do_execsql_test 5.1 { do_execsql_test 5.1 {
CREATE VIRTUAL TABLE temp.vm USING fts5vocab(main, t1, row); CREATE VIRTUAL TABLE temp.vm USING fts5vocab(main, t1, row);
CREATE VIRTUAL TABLE temp.vt1 USING fts5vocab(t1, row); CREATE VIRTUAL TABLE temp.vt1 USING fts5vocab(t1, row);

Some files were not shown because too many files have changed in this diff Show More