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:
22
Makefile.in
22
Makefile.in
@ -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
|
||||||
|
22
Makefile.msc
22
Makefile.msc
@ -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
|
||||||
|
56
README.md
56
README.md
@ -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).
|
||||||
|
@ -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
84
configure
vendored
@ -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\\"
|
||||||
|
|
||||||
|
32
configure.ac
32
configure.ac
@ -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
|
||||||
|
399
doc/lemon.html
399
doc/lemon.html
@ -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 → Robust and secure
|
||||||
|
<li>The "lemon.exe" command line tool itself → 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(&sState);
|
||||||
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
|
11 while( GetNextToken(pTokenizer, &hTokenId, &sToken) ){
|
||||||
12 Parse(pParser, hTokenId, sToken, &sState);
|
12 Parse(pParser, hTokenId, sToken, &sState);
|
||||||
13 }
|
13 }
|
||||||
14 Parse(pParser, 0, sToken, &sState);
|
14 Parse(pParser, 0, sToken, &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,&hTokenId, &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 -> 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 — 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 —
|
||||||
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>
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 ){
|
||||||
|
@ -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));
|
||||||
|
@ -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);
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
@ -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)"
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -294,4 +294,3 @@ do_execsql_test 7.0 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -276,4 +276,3 @@ foreach {tn expr tclexpr} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -309,4 +309,3 @@ foreach {tn q cnt} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -178,4 +178,3 @@ do_execsql_test 5.1 {
|
|||||||
} ;# foreach_detail_mode
|
} ;# foreach_detail_mode
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -142,4 +142,3 @@ if {[detail_is_full]} {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -167,4 +167,3 @@ do_execsql_test 1.8.2 {
|
|||||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -55,4 +55,3 @@ do_execsql_test 1.2 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -66,4 +66,3 @@ do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') }
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -147,4 +147,3 @@ do_execsql_test 3.1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -342,4 +342,3 @@ foreach {tn expr} {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -112,4 +112,3 @@ db eval {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -61,4 +61,3 @@ do_test 2.1...slow {
|
|||||||
} {}
|
} {}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -64,5 +64,3 @@ foreach_detail_mode $::testprefix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,5 +84,3 @@ foreach_detail_mode $::testprefix {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@ -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');
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -66,5 +66,3 @@ do_execsql_test 2.1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
247
ext/fts5/test/fts5connect.test
Normal file
247
ext/fts5/test/fts5connect.test
Normal 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
|
||||||
|
|
@ -255,4 +255,3 @@ do_execsql_test 6.2 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -51,4 +51,3 @@ do_test 1.2 {
|
|||||||
} {}
|
} {}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -241,4 +241,3 @@ do_execsql_test 5.3 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -63,5 +63,3 @@ foreach_detail_mode $::testprefix {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -44,4 +44,3 @@ do_execsql_test 1.2 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -81,6 +81,3 @@ do_execsql_test 3.3 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -351,4 +351,3 @@ do_faultsim_test 9.1 -faults oom-* -prep {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -137,4 +137,3 @@ do_faultsim_test 5.0 -faults oom-* -prep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -110,4 +110,3 @@ do_faultsim_test 3.2 -faults oom-* -prep {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -395,4 +395,3 @@ do_faultsim_test 14.1 -faults oom-t* -prep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -116,4 +116,3 @@ do_faultsim_test 2.2 -faults oom-* -body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -82,4 +82,3 @@ do_faultsim_test 4 -faults oom-* -prep {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -153,4 +153,3 @@ do_faultsim_test 6 -faults oom-* -body {
|
|||||||
} ;# foreach_detail_mode...
|
} ;# foreach_detail_mode...
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -132,4 +132,3 @@ do_faultsim_test 4.2 -faults oom* -body {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -40,4 +40,3 @@ do_test 1.1 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -90,4 +90,3 @@ do_catchsql_test 4.1 {
|
|||||||
} {1 {fts5: syntax error near "`"}}
|
} {1 {fts5: syntax error near "`"}}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -210,4 +210,3 @@ foreach {tn pgsz} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -70,4 +70,3 @@ do_execsql_test 1.6 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
43
ext/fts5/test/fts5leftjoin.test
Normal file
43
ext/fts5/test/fts5leftjoin.test
Normal 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
|
@ -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
|
||||||
|
|
||||||
|
@ -241,4 +241,3 @@ do_execsql_test 6.3 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -55,4 +55,3 @@ do_execsql_test 1.2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -45,4 +45,3 @@ do_multiclient_test tn {
|
|||||||
};# do_multiclient_test
|
};# do_multiclient_test
|
||||||
};# foreach_detail_mode
|
};# foreach_detail_mode
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -68,4 +68,3 @@ do_near_test 1.25 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 4) } 0
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -116,4 +116,3 @@ do_execsql_test 2.0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -64,4 +64,3 @@ do_eqp_test 1.5 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -11803,4 +11803,3 @@ foreach {in out} $test_vocab {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -67,4 +67,3 @@ foreach {in out} $test_vocab {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -341,5 +341,3 @@ foreach {tn create} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,5 +79,3 @@ for {set tn 1 ; set pgsz 64} {$tn<32} {incr tn; incr pgsz 16} {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -149,4 +149,3 @@ do_test 4.3 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -216,4 +216,3 @@ do_execsql_test 6.2 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -116,4 +116,3 @@ do_execsql_test 4.6 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -421,4 +421,3 @@ do_execsql_test 7.1.2 {
|
|||||||
} ;# foreach_detail_mode
|
} ;# foreach_detail_mode
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -161,4 +161,3 @@ foreach {tn expr} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -302,4 +302,3 @@ do_test 9.5.2 { set ::flags } {query}
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
} {
|
} {
|
||||||
|
@ -126,4 +126,3 @@ do_test 1.5 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -76,4 +76,3 @@ do_execsql_test 3.2 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -117,5 +117,3 @@ do_execsql_test 2.2.integrity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,4 +61,3 @@ do_test 1.7 {
|
|||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
@ -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
Reference in New Issue
Block a user