Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -20,15 +20,19 @@ # C Compiler and options for use in building executables that # will run on the platform that is doing the build. # BCC = @BUILD_CC@ @BUILD_CFLAGS@ -# C Compile and options for use in building executables that +# TCC is the C Compile and options for use in building executables that # will run on the target platform. (BCC and TCC are usually the -# same unless your are cross-compiling.) +# same unless your are cross-compiling.) Separate CC and CFLAGS macros +# are provide so that these aspects of the build process can be changed +# on the "make" command-line. Ex: "make CC=clang CFLAGS=-fsanitize=undefined" # -TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree +CC = @CC@ +CFLAGS = @CPPFLAGS@ @CFLAGS@ +TCC = $(CC) $(CFLAGS) -I. -I${TOP}/src -I${TOP}/ext/rtree -I${TOP}/ext/fts3 # Define this for the autoconf-based build, so that the code knows it can # include the generated config.h # TCC += -D_HAVE_SQLITE_CONFIG_H -DBUILD_sqlite @@ -35,11 +39,11 @@ # Define -DNDEBUG to compile without debugging (i.e., for production usage) # Omitting the define will cause extra debugging code to be inserted and # includes extra comments when "EXPLAIN stmt" is used. # -TCC += @TARGET_DEBUG@ @XTHREADCONNECT@ +TCC += @TARGET_DEBUG@ # Compiler options needed for programs that use the TCL library. # TCC += @TCL_INCLUDE_SPEC@ @@ -228,10 +232,11 @@ $(TOP)/src/mem1.c \ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/memjournal.c \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ $(TOP)/src/mutex_unix.c \ $(TOP)/src/mutex_w32.c \ @@ -457,10 +462,11 @@ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/os_setup.h \ @@ -924,22 +930,40 @@ testfixture$(TEXE): $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ -o $@ $(TESTFIXTURE_SRC) $(LIBTCL) $(TLIBS) - +# A very detailed test running most or all test cases fulltest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test +# Really really long testing soaktest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/all.test -soak=1 +# Do extra testing but not aeverything. fulltestonly: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/full.test +# This is the common case. Run many tests but not those that take +# a really long time. +# test: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture$(TEXE) $(TOP)/test/veryquick.test + +# Run a test using valgrind. This can take a really long time +# because valgrind is so much slower than a native machine. +# +valgrindtest: testfixture$(TEXE) sqlite3$(TEXE) + OMIT_MISUSE=1 valgrind -v ./testfixture$(TEXE) $(TOP)/test/permutations.test valgrind + +# A very fast test that checks basic sanity. The name comes from +# the 60s-era electronics testing: "Turn it on and see if smoke +# comes out." +# +smoketest: testfixture$(TEXE) + ./testfixture$(TEXE) $(TOP)/test/main.test sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TOP)/tool/spaceanal.tcl echo "#define TCLSH 2" > $@ cat sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c >> $@ echo "static const char *tclsh_main_loop(void){" >> $@ Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -13,10 +13,27 @@ # Set this non-0 to create and use the SQLite amalgamation file. # !IFNDEF USE_AMALGAMATION USE_AMALGAMATION = 1 !ENDIF + +# Set this non-0 to enable full warnings (-W4, etc) when compiling. +# +!IFNDEF USE_FULLWARN +USE_FULLWARN = 0 +!ENDIF + +# If necessary, create a list of harmless compiler warnings to disable when +# compiling the build tools. For the SQLite source code itself, warnings, +# if any, will be disabled from within it. +# +!IFNDEF NO_WARN +!IF $(USE_FULLWARN)!=0 +NO_WARN = -wd4054 -wd4055 -wd4100 -wd4127 -wd4152 -wd4189 -wd4206 -wd4210 +NO_WARN = $(NO_WARN) -wd4232 -wd4244 -wd4305 -wd4306 -wd4702 -wd4706 +!ENDIF +!ENDIF # Set this non-0 to use the library paths and other options necessary for # Windows Phone 8.1. # !IFNDEF USE_WP81_OPTS @@ -230,11 +247,15 @@ NSDKLIBPATH = $(NSDKLIBPATH:\\=\) # C compiler and options for use in building executables that # will run on the platform that is doing the build. # +!IF $(USE_FULLWARN)!=0 +BCC = $(NCC) -W4 +!ELSE BCC = $(NCC) -W3 +!ENDIF # Check if assembly code listings should be generated for the source # code files to be compiled. # !IF $(USE_LISTINGS)!=0 @@ -251,11 +272,17 @@ # C compiler and options for use in building executables that # will run on the target platform. (BCC and TCC are usually the # same unless your are cross-compiling.) # -TCC = $(CC) -W3 -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise +!IF $(USE_FULLWARN)!=0 +TCC = $(CC) -W4 -DINCLUDE_MSVC_H=1 +!ELSE +TCC = $(CC) -W3 +!ENDIF + +TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src # Check if assembly code listings should be generated for the source # code files to be compiled. # @@ -704,10 +731,11 @@ $(TOP)\src\mem1.c \ $(TOP)\src\mem2.c \ $(TOP)\src\mem3.c \ $(TOP)\src\mem5.c \ $(TOP)\src\memjournal.c \ + $(TOP)\src\msvc.h \ $(TOP)\src\mutex.c \ $(TOP)\src\mutex.h \ $(TOP)\src\mutex_noop.c \ $(TOP)\src\mutex_unix.c \ $(TOP)\src\mutex_w32.c \ @@ -936,10 +964,11 @@ $(TOP)\src\btree.h \ $(TOP)\src\btreeInt.h \ $(TOP)\src\hash.h \ $(TOP)\src\hwtime.h \ keywordhash.h \ + $(TOP)\src\msvc.h \ $(TOP)\src\mutex.h \ opcodes.h \ $(TOP)\src\os.h \ $(TOP)\src\os_common.h \ $(TOP)\src\os_setup.h \ @@ -987,12 +1016,11 @@ libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCL:tcl=tclstub) $(TLIBS) sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h - $(LTLINK) $(READLINE_FLAGS) \ - $(TOP)\src\shell.c \ + $(LTLINK) $(READLINE_FLAGS) $(TOP)\src\shell.c \ /link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) mptester.exe: $(TOP)\mptest\mptest.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h $(LTLINK) $(TOP)\mptest\mptest.c \ /link $(LTLINKOPTS) $(LTLIBPATHS) libsqlite3.lib $(LIBRESOBJS) $(LIBREADLINE) $(LTLIBS) $(TLIBS) @@ -1038,11 +1066,12 @@ # lempar.c: $(TOP)\src\lempar.c copy $(TOP)\src\lempar.c . lemon.exe: $(TOP)\tool\lemon.c lempar.c - $(BCC) -Daccess=_access -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) + $(BCC) $(NO_WARN) -Daccess=_access \ + -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) # Rules to build individual *.lo files from generated *.c files. This # applies to: # # parse.lo @@ -1309,11 +1338,12 @@ sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c - $(BCC) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) + $(BCC) $(NO_WARN) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) \ + $(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS) keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe .\mkkeywordhash.exe > keywordhash.h @@ -1390,11 +1420,12 @@ # fixture. Otherwise link against libsqlite3.lib. (This distinction is # necessary because the test fixture requires non-API symbols which are # hidden when the library is built via the amalgamation). # TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 -TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" +TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_CORE $(NO_WARN) TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) libsqlite3.lib TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C) !IF $(USE_AMALGAMATION)==0 TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0) Index: Makefile.vxworks ================================================================== --- Makefile.vxworks +++ Makefile.vxworks @@ -251,10 +251,11 @@ $(TOP)/src/mem1.c \ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/memjournal.c \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ $(TOP)/src/mutex_unix.c \ $(TOP)/src/mutex_w32.c \ @@ -412,10 +413,11 @@ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/os_setup.h \ Index: config.h.in ================================================================== --- config.h.in +++ config.h.in @@ -24,10 +24,13 @@ /* Define to 1 if the system has the type `intptr_t'. */ #undef HAVE_INTPTR_T /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `isnan' function. */ +#undef HAVE_ISNAN /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R /* Define to 1 if you have the `localtime_s' function. */ @@ -45,10 +48,13 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H + +/* Define to 1 if you have the strchrnul() function */ +#undef HAVE_STRCHRNUL /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ Index: configure ================================================================== --- configure +++ configure @@ -866,11 +866,10 @@ VERSION RELEASE VERSION_NUMBER BUILD_CC SQLITE_THREADSAFE -XTHREADCONNECT ALLOWRELEASE TEMP_STORE BUILD_EXEEXT SQLITE_OS_UNIX SQLITE_OS_WIN @@ -904,13 +903,11 @@ with_pic enable_fast_install with_gnu_ld enable_libtool_lock enable_largefile -with_hints enable_threadsafe -enable_cross_thread_connections enable_releasemode enable_tempstore enable_tcl with_tcl enable_readline @@ -1560,31 +1557,29 @@ --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --disable-largefile omit support for large files - --enable-threadsafe Support threadsafe operation - --enable-cross-thread-connections - Allow connection sharing across threads + --disable-threadsafe Disable mutexing --enable-releasemode Support libtool link to release mode --enable-tempstore Use an in-ram database for temporary tables (never,no,yes,always) --disable-tcl do not build TCL extension --disable-readline disable readline support [default=detect] --enable-debug enable debugging & verbose explain --disable-amalgamation Disable the amalgamation and instead build all files separately - --enable-load-extension Enable loading of external extensions + --disable-load-extension + Disable loading of external extensions --enable-gcov Enable coverage testing using gcov Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic try to use only PIC/non-PIC objects [default=use both] --with-gnu-ld assume the C compiler uses GNU ld [default=no] - --with-hints=FILE Read configuration options from FILE --with-tcl=DIR directory containing tcl configuration (tclConfig.sh) --with-readline-lib specify readline library --with-readline-inc specify readline include paths @@ -2056,13 +2051,10 @@ top level VERSION file = $sqlite_version_sanity_check please regen with autoconf" >&2;} { (exit 1); exit 1; }; } fi -# The following RCS revision string applies to configure.in -# $Revision: 1.56 $ - ######### # Programs needed # case `pwd` in *\ * | *\ *) @@ -3730,17 +3722,17 @@ if test "${lt_cv_nm_interface+set}" = set; then $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3735: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3727: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3738: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3730: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3741: output\"" >&5) + (eval echo "\"\$as_me:3733: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* @@ -4958,11 +4950,11 @@ fi rm -rf conftest* ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4963 "configure"' > conftest.$ac_ext + echo '#line 4955 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then @@ -6827,15 +6819,15 @@ # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:6832: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6824: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6836: \$? = $ac_status" >&5 + echo "$as_me:6828: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 @@ -7166,15 +7158,15 @@ # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7171: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7163: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7175: \$? = $ac_status" >&5 + echo "$as_me:7167: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 @@ -7271,15 +7263,15 @@ # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7276: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7268: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7280: \$? = $ac_status" >&5 + echo "$as_me:7272: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp @@ -7326,15 +7318,15 @@ # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7331: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7323: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7335: \$? = $ac_status" >&5 + echo "$as_me:7327: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp @@ -10139,11 +10131,11 @@ lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10144 "configure" +#line 10136 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif @@ -10235,11 +10227,11 @@ lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10240 "configure" +#line 10232 "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif @@ -12144,11 +12136,13 @@ -for ac_func in usleep fdatasync localtime_r gmtime_r localtime_s utime malloc_usable_size + + +for ac_func in fdatasync gmtime_r isnan localtime_r localtime_s malloc_usabel_size strchrnul usleep utime do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5 $as_echo_n "checking for $ac_func... " >&6; } if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then @@ -12340,49 +12334,10 @@ | awk '{printf "%d%03d%03d",$1,$2,$3}'` { $as_echo "$as_me:$LINENO: Version number set to $VERSION_NUMBER" >&5 $as_echo "$as_me: Version number set to $VERSION_NUMBER" >&6;} -######### -# Check to see if the --with-hints=FILE option is used. If there is none, -# then check for a files named "$host.hints" and ../$hosts.hints where -# $host is the hostname of the build system. If still no hints are -# found, try looking in $system.hints and ../$system.hints where -# $system is the result of uname -s. -# - -# Check whether --with-hints was given. -if test "${with_hints+set}" = set; then - withval=$with_hints; hints=$withval -fi - -if test "$hints" = ""; then - host=`hostname | sed 's/\..*//'` - if test -r $host.hints; then - hints=$host.hints - else - if test -r ../$host.hints; then - hints=../$host.hints - fi - fi -fi -if test "$hints" = ""; then - sys=`uname -s` - if test -r $sys.hints; then - hints=$sys.hints - else - if test -r ../$sys.hints; then - hints=../$sys.hints - fi - fi -fi -if test "$hints" != ""; then - { $as_echo "$as_me:$LINENO: result: reading hints from $hints" >&5 -$as_echo "reading hints from $hints" >&6; } - . $hints -fi - ######### # Locate a compiler for the build machine. This compiler should # generate command-line programs that run on the build machine. # if test x"$cross_compiling" = xno; then @@ -12549,35 +12504,10 @@ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi fi - -########## -# Do we want to allow a connection created in one thread to be used -# in another thread. This does not work on many Linux systems (ex: RedHat 9) -# due to bugs in the threading implementations. This is thus off by default. -# -# Check whether --enable-cross-thread-connections was given. -if test "${enable_cross_thread_connections+set}" = set; then - enableval=$enable_cross_thread_connections; -else - enable_xthreadconnect=no -fi - -{ $as_echo "$as_me:$LINENO: checking whether to allow connections to be shared across threads" >&5 -$as_echo_n "checking whether to allow connections to be shared across threads... " >&6; } -if test "$enable_xthreadconnect" = "no"; then - XTHREADCONNECT='' - { $as_echo "$as_me:$LINENO: result: no" >&5 -$as_echo "no" >&6; } -else - XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1' - { $as_echo "$as_me:$LINENO: result: yes" >&5 -$as_echo "yes" >&6; } -fi - ########## # Do we want to support release # # Check whether --enable-releasemode was given. @@ -13425,11 +13355,11 @@ # See whether we should allow loadable extensions # Check whether --enable-load-extension was given. if test "${enable_load_extension+set}" = set; then enableval=$enable_load_extension; use_loadextension=$enableval else - use_loadextension=no + use_loadextension=yes fi if test "${use_loadextension}" = "yes" ; then OPT_FEATURE_FLAGS="" { $as_echo "$as_me:$LINENO: checking for library containing dlopen" >&5 Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -67,23 +67,10 @@ # TARGET_EXEEXT # # The filename extension for executables on the # target platform. "" for Unix and ".exe" for windows. # -# The generated configure script will make an attempt to guess -# at all of the above parameters. You can override any of -# the guesses by setting the environment variable named -# "config_AAAA" where "AAAA" is the name of the parameter -# described above. (Exception: srcdir cannot be set this way.) -# If you have a file that sets one or more of these environment -# variables, you can invoke configure as follows: -# -# configure --with-hints=FILE -# -# where FILE is the name of the file that sets the environment -# variables. FILE should be an absolute pathname. -# # This configure.in file is easy to reuse on other projects. Just # change the argument to AC_INIT(). And disable any features that # you don't need (for example BLT) by erasing or commenting out # the corresponding code. # @@ -96,15 +83,10 @@ configure \$PACKAGE_VERSION = $PACKAGE_VERSION top level VERSION file = $sqlite_version_sanity_check please regen with autoconf]) fi -dnl Put the RCS revision string after AC_INIT so that it will also -dnl show in in configure. -# The following RCS revision string applies to configure.in -# $Revision: 1.56 $ - ######### # Programs needed # AC_PROG_LIBTOOL AC_PROG_INSTALL @@ -125,11 +107,11 @@ AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h malloc.h]) ######### # Figure out whether or not we have these functions # -AC_CHECK_FUNCS([usleep fdatasync localtime_r gmtime_r localtime_s utime malloc_usable_size]) +AC_CHECK_FUNCS([fdatasync gmtime_r isnan localtime_r localtime_s malloc_usabel_size strchrnul usleep utime]) ######### # By default, we use the amalgamation (this may be changed below...) # USE_AMALGAMATION=1 @@ -178,45 +160,10 @@ | sed 's/[^0-9]/ /g' \ | awk '{printf "%d%03d%03d",$1,$2,$3}'`] AC_MSG_NOTICE(Version number set to $VERSION_NUMBER) AC_SUBST(VERSION_NUMBER) -######### -# Check to see if the --with-hints=FILE option is used. If there is none, -# then check for a files named "$host.hints" and ../$hosts.hints where -# $host is the hostname of the build system. If still no hints are -# found, try looking in $system.hints and ../$system.hints where -# $system is the result of uname -s. -# -AC_ARG_WITH(hints, - AC_HELP_STRING([--with-hints=FILE],[Read configuration options from FILE]), - hints=$withval) -if test "$hints" = ""; then - host=`hostname | sed 's/\..*//'` - if test -r $host.hints; then - hints=$host.hints - else - if test -r ../$host.hints; then - hints=../$host.hints - fi - fi -fi -if test "$hints" = ""; then - sys=`uname -s` - if test -r $sys.hints; then - hints=$sys.hints - else - if test -r ../$sys.hints; then - hints=../$sys.hints - fi - fi -fi -if test "$hints" != ""; then - AC_MSG_RESULT(reading hints from $hints) - . $hints -fi - ######### # Locate a compiler for the build machine. This compiler should # generate command-line programs that run on the build machine. # if test x"$cross_compiling" = xno; then @@ -234,11 +181,11 @@ ########## # Do we want to support multithreaded use of sqlite # AC_ARG_ENABLE(threadsafe, -AC_HELP_STRING([--enable-threadsafe],[Support threadsafe operation]),,enable_threadsafe=yes) +AC_HELP_STRING([--disable-threadsafe],[Disable mutexing]),,enable_threadsafe=yes) AC_MSG_CHECKING([whether to support threadsafe operation]) if test "$enable_threadsafe" = "no"; then SQLITE_THREADSAFE=0 AC_MSG_RESULT([no]) else @@ -249,27 +196,10 @@ if test "$SQLITE_THREADSAFE" = "1"; then AC_SEARCH_LIBS(pthread_create, pthread) fi -########## -# Do we want to allow a connection created in one thread to be used -# in another thread. This does not work on many Linux systems (ex: RedHat 9) -# due to bugs in the threading implementations. This is thus off by default. -# -AC_ARG_ENABLE(cross-thread-connections, -AC_HELP_STRING([--enable-cross-thread-connections],[Allow connection sharing across threads]),,enable_xthreadconnect=no) -AC_MSG_CHECKING([whether to allow connections to be shared across threads]) -if test "$enable_xthreadconnect" = "no"; then - XTHREADCONNECT='' - AC_MSG_RESULT([no]) -else - XTHREADCONNECT='-DSQLITE_ALLOW_XTHREAD_CONNECT=1' - AC_MSG_RESULT([yes]) -fi -AC_SUBST(XTHREADCONNECT) - ########## # Do we want to support release # AC_ARG_ENABLE(releasemode, AC_HELP_STRING([--enable-releasemode],[Support libtool link to release mode]),,enable_releasemode=no) @@ -603,13 +533,13 @@ fi AC_SUBST(USE_AMALGAMATION) ######### # See whether we should allow loadable extensions -AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--enable-load-extension], - [Enable loading of external extensions]), - [use_loadextension=$enableval],[use_loadextension=no]) +AC_ARG_ENABLE(load-extension, AC_HELP_STRING([--disable-load-extension], + [Disable loading of external extensions]), + [use_loadextension=$enableval],[use_loadextension=yes]) if test "${use_loadextension}" = "yes" ; then OPT_FEATURE_FLAGS="" AC_SEARCH_LIBS(dlopen, dl) else OPT_FEATURE_FLAGS="-DSQLITE_OMIT_LOAD_EXTENSION=1" Index: ext/fts3/fts3.c ================================================================== --- ext/fts3/fts3.c +++ ext/fts3/fts3.c @@ -1851,11 +1851,11 @@ const char *zNode, /* Buffer containing segment interior node */ int nNode, /* Size of buffer at zNode */ sqlite3_int64 *piLeaf, /* Selected leaf node */ sqlite3_int64 *piLeaf2 /* Selected leaf node */ ){ - int rc; /* Return code */ + int rc = SQLITE_OK; /* Return code */ int iHeight; /* Height of this node in tree */ assert( piLeaf || piLeaf2 ); fts3GetVarint32(zNode, &iHeight); @@ -1862,11 +1862,11 @@ rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); if( rc==SQLITE_OK && iHeight>1 ){ char *zBlob = 0; /* Blob read from %_segments table */ - int nBlob; /* Size of zBlob in bytes */ + int nBlob = 0; /* Size of zBlob in bytes */ if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); @@ -3084,11 +3084,11 @@ int idxNum, /* Strategy index */ const char *idxStr, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ - int rc; + int rc = SQLITE_OK; char *zSql; /* SQL statement used to access %_content */ int eSearch; Fts3Table *p = (Fts3Table *)pCursor->pVtab; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; Index: ext/fts3/fts3.h ================================================================== --- ext/fts3/fts3.h +++ ext/fts3/fts3.h @@ -18,10 +18,9 @@ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ int sqlite3Fts3Init(sqlite3 *db); -int sqlite3Fts5Init(sqlite3 *db); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ Index: ext/fts3/fts3_tokenize_vtab.c ================================================================== --- ext/fts3/fts3_tokenize_vtab.c +++ ext/fts3/fts3_tokenize_vtab.c @@ -161,11 +161,11 @@ int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ - Fts3tokTable *pTab; + Fts3tokTable *pTab = 0; const sqlite3_tokenizer_module *pMod = 0; sqlite3_tokenizer *pTok = 0; int rc; char **azDequote = 0; int nDequote; Index: ext/fts3/fts3_write.c ================================================================== --- ext/fts3/fts3_write.c +++ ext/fts3/fts3_write.c @@ -3080,12 +3080,12 @@ } rc = sqlite3_reset(pRange); if( bOk ){ int iIdx = 0; - sqlite3_stmt *pUpdate1; - sqlite3_stmt *pUpdate2; + sqlite3_stmt *pUpdate1 = 0; + sqlite3_stmt *pUpdate2 = 0; if( rc==SQLITE_OK ){ rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0); } if( rc==SQLITE_OK ){ Index: ext/fts3/unicode/mkunicode.tcl ================================================================== --- ext/fts3/unicode/mkunicode.tcl +++ ext/fts3/unicode/mkunicode.tcl @@ -730,16 +730,12 @@ /* ** DO NOT EDIT THIS MACHINE GENERATED FILE. */ }] puts "" - if {$::generate_fts5_code} { - puts "#if defined(SQLITE_ENABLE_FTS5)" - } else { - puts "#ifndef SQLITE_DISABLE_FTS3_UNICODE" - puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)" - } + puts "#ifndef SQLITE_DISABLE_FTS3_UNICODE" + puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)" puts "" puts "#include " puts "" } @@ -762,42 +758,26 @@ # Proces the command line arguments. Exit early if they are not to # our liking. # proc usage {} { - puts -nonewline stderr "Usage: $::argv0 ?-test? ?-fts5? " + puts -nonewline stderr "Usage: $::argv0 ?-test? " puts stderr " " exit 1 } -if {[llength $argv]<2} usage +if {[llength $argv]!=2 && [llength $argv]!=3} usage +if {[llength $argv]==3 && [lindex $argv 0]!="-test"} usage set unicodedata.txt [lindex $argv end] set casefolding.txt [lindex $argv end-1] - -set generate_test_code 0 -set generate_fts5_code 0 -set function_prefix "sqlite3Fts" -for {set i 0} {$i < [llength $argv]-2} {incr i} { - switch -- [lindex $argv $i] { - -test { - set generate_test_code 1 - } - -fts5 { - set function_prefix sqlite3Fts5 - set generate_fts5_code 1 - } - default { - usage - } - } -} +set generate_test_code [expr {[llength $argv]==3}] print_fileheader # Print the isalnum() function to stdout. # set lRange [an_load_separator_ranges] -print_isalnum ${function_prefix}UnicodeIsalnum $lRange +print_isalnum sqlite3FtsUnicodeIsalnum $lRange # Leave a gap between the two generated C functions. # puts "" puts "" @@ -808,28 +788,24 @@ set mappings [rd_load_unicodedata_text ${unicodedata.txt}] print_rd $mappings puts "" puts "" -print_isdiacritic ${function_prefix}UnicodeIsdiacritic $mappings +print_isdiacritic sqlite3FtsUnicodeIsdiacritic $mappings puts "" puts "" # Print the fold() function to stdout. # -print_fold ${function_prefix}UnicodeFold +print_fold sqlite3FtsUnicodeFold # Print the test routines and main() function to stdout, if -test # was specified. # if {$::generate_test_code} { - print_test_isalnum ${function_prefix}UnicodeIsalnum $lRange - print_fold_test ${function_prefix}UnicodeFold $mappings + print_test_isalnum sqlite3FtsUnicodeIsalnum $lRange + print_fold_test sqlite3FtsUnicodeFold $mappings print_test_main } -if {$generate_fts5_code} { - puts "#endif /* defined(SQLITE_ENABLE_FTS5) */" -} else { - puts "#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */" - puts "#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */" -} +puts "#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */" +puts "#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */" DELETED ext/fts5/extract_api_docs.tcl Index: ext/fts5/extract_api_docs.tcl ================================================================== --- ext/fts5/extract_api_docs.tcl +++ /dev/null @@ -1,237 +0,0 @@ -# -# 2014 August 24 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#-------------------------------------------------------------------------- -# -# This script extracts the documentation for the API used by fts5 auxiliary -# functions from header file fts5.h. It outputs html text on stdout that -# is included in the documentation on the web. -# - -set ::fts5_docs_output "" -if {[info commands hd_putsnl]==""} { - if {[llength $argv]>0} { set ::extract_api_docs_mode [lindex $argv 0] } - proc output {text} { - puts $text - } -} else { - proc output {text} { - append ::fts5_docs_output "$text\n" - } -} -if {[info exists ::extract_api_docs_mode]==0} {set ::extract_api_docs_mode api} - - -set input_file [file join [file dir [info script]] fts5.h] -set fd [open $input_file] -set data [read $fd] -close $fd - - -# Argument $data is the entire text of the fts5.h file. This function -# extracts the definition of the Fts5ExtensionApi structure from it and -# returns a key/value list of structure member names and definitions. i.e. -# -# iVersion {int iVersion} xUserData {void *(*xUserData)(Fts5Context*)} ... -# -proc get_struct_members {data} { - - # Extract the structure definition from the fts5.h file. - regexp "struct Fts5ExtensionApi {(.*?)};" $data -> defn - - # Remove all comments from the structure definition - regsub -all {/[*].*?[*]/} $defn {} defn2 - - set res [list] - foreach member [split $defn2 {;}] { - - set member [string trim $member] - if {$member!=""} { - catch { set name [lindex $member end] } - regexp {.*?[(][*]([^)]*)[)]} $member -> name - lappend res $name $member - } - } - - set res -} - -proc get_struct_docs {data names} { - # Extract the structure definition from the fts5.h file. - regexp {EXTENSION API FUNCTIONS(.*?)[*]/} $data -> docs - - set current_doc "" - set current_header "" - - foreach line [split $docs "\n"] { - regsub {[*]*} $line {} line - if {[regexp {^ } $line]} { - append current_doc "$line\n" - } elseif {[string trim $line]==""} { - if {$current_header!=""} { append current_doc "\n" } - } else { - if {$current_doc != ""} { - lappend res $current_header $current_doc - set current_doc "" - } - set subject n/a - regexp {^ *([[:alpha:]]*)} $line -> subject - if {[lsearch $names $subject]>=0} { - set current_header $subject - } else { - set current_header [string trim $line] - } - } - } - - if {$current_doc != ""} { - lappend res $current_header $current_doc - } - - set res -} - -proc get_tokenizer_docs {data} { - regexp {(xCreate:.*?)[*]/} $data -> docs - - set res "
\n" - foreach line [split [string trim $docs] "\n"] { - regexp {[*][*](.*)} $line -> line - if {[regexp {^ ?x.*:} $line]} { - append res "
$line

\n" - continue - } - if {[string trim $line] == ""} { - append res "

\n" - } else { - append res "$line\n" - } - } - append res "

\n" - - set res -} - -proc get_api_docs {data} { - # Initialize global array M as a map from Fts5StructureApi member name - # to member definition. i.e. - # - # iVersion -> {int iVersion} - # xUserData -> {void *(*xUserData)(Fts5Context*)} - # ... - # - array set M [get_struct_members $data] - - # Initialize global list D as a map from section name to documentation - # text. Most (all?) section names are structure member names. - # - set D [get_struct_docs $data [array names M]] - - foreach {sub docs} $D { - if {[info exists M($sub)]} { - set hdr $M($sub) - set link " id=$sub" - } else { - set link "" - } - - output "
" - set style "padding-left:6ex;font-size:1.4em;display:block" - output "
$hdr
" - - set mode "" - set bEmpty 1 - foreach line [split [string trim $docs] "\n"] { - if {[string trim $line]==""} { - if {$mode != ""} {output ""} - set mode "" - } elseif {$mode == ""} { - if {[regexp {^ } $line]} { - set mode codeblock - } else { - set mode p - } - output "<$mode>" - } - output $line - } - if {$mode != ""} {output ""} - } -} - -proc get_fts5_struct {data start end} { - set res "" - set bOut 0 - foreach line [split $data "\n"] { - if {$bOut==0} { - if {[regexp $start $line]} { - set bOut 1 - } - } - - if {$bOut} { - append res "$line\n" - } - - if {$bOut} { - if {[regexp $end $line]} { - set bOut 0 - } - } - } - - set map [list /* /* */ */] - string map $map $res -} - -proc main {data} { - switch $::extract_api_docs_mode { - fts5_api { - output [get_fts5_struct $data "typedef struct fts5_api" "^\};"] - } - - fts5_tokenizer { - output [get_fts5_struct $data "typedef struct Fts5Tokenizer" "^\};"] - } - - fts5_extension { - output [get_fts5_struct $data "typedef.*Fts5ExtensionApi" "^.;"] - } - - Fts5ExtensionApi { - set struct [get_fts5_struct $data "^struct Fts5ExtensionApi" "^.;"] - set map [list] - foreach {k v} [get_struct_members $data] { - if {[string match x* $k]==0} continue - lappend map $k "$k" - } - output [string map $map $struct] - } - - api { - get_api_docs $data - } - - tokenizer_api { - output [get_tokenizer_docs $data] - } - - default { - } - } -} -main $data - -set ::fts5_docs_output - - - - - DELETED ext/fts5/fts5.c Index: ext/fts5/fts5.c ================================================================== --- ext/fts5/fts5.c +++ /dev/null @@ -1,1963 +0,0 @@ -/* -** 2014 Jun 09 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This is an SQLite module implementing full-text search. -*/ - - -#include "fts5Int.h" - - -typedef struct Fts5Table Fts5Table; -typedef struct Fts5Cursor Fts5Cursor; -typedef struct Fts5Global Fts5Global; -typedef struct Fts5Auxiliary Fts5Auxiliary; -typedef struct Fts5Auxdata Fts5Auxdata; - -typedef struct Fts5TokenizerModule Fts5TokenizerModule; - -/* -** NOTES ON TRANSACTIONS: -** -** SQLite invokes the following virtual table methods as transactions are -** opened and closed by the user: -** -** xBegin(): Start of a new transaction. -** xSync(): Initial part of two-phase commit. -** xCommit(): Final part of two-phase commit. -** xRollback(): Rollback the transaction. -** -** Anything that is required as part of a commit that may fail is performed -** in the xSync() callback. Current versions of SQLite ignore any errors -** returned by xCommit(). -** -** And as sub-transactions are opened/closed: -** -** xSavepoint(int S): Open savepoint S. -** xRelease(int S): Commit and close savepoint S. -** xRollbackTo(int S): Rollback to start of savepoint S. -** -** During a write-transaction the fts5_index.c module may cache some data -** in-memory. It is flushed to disk whenever xSync(), xRelease() or -** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo() -** is called. -** -** Additionally, if SQLITE_DEBUG is defined, an instance of the following -** structure is used to record the current transaction state. This information -** is not required, but it is used in the assert() statements executed by -** function fts5CheckTransactionState() (see below). -*/ -struct Fts5TransactionState { - int eState; /* 0==closed, 1==open, 2==synced */ - int iSavepoint; /* Number of open savepoints (0 -> none) */ -}; - -/* -** A single object of this type is allocated when the FTS5 module is -** registered with a database handle. It is used to store pointers to -** all registered FTS5 extensions - tokenizers and auxiliary functions. -*/ -struct Fts5Global { - fts5_api api; /* User visible part of object (see fts5.h) */ - sqlite3 *db; /* Associated database connection */ - i64 iNextId; /* Used to allocate unique cursor ids */ - Fts5Auxiliary *pAux; /* First in list of all aux. functions */ - Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ - Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ - Fts5Cursor *pCsr; /* First in list of all open cursors */ -}; - -/* -** Each auxiliary function registered with the FTS5 module is represented -** by an object of the following type. All such objects are stored as part -** of the Fts5Global.pAux list. -*/ -struct Fts5Auxiliary { - Fts5Global *pGlobal; /* Global context for this function */ - char *zFunc; /* Function name (nul-terminated) */ - void *pUserData; /* User-data pointer */ - fts5_extension_function xFunc; /* Callback function */ - void (*xDestroy)(void*); /* Destructor function */ - Fts5Auxiliary *pNext; /* Next registered auxiliary function */ -}; - -/* -** Each tokenizer module registered with the FTS5 module is represented -** by an object of the following type. All such objects are stored as part -** of the Fts5Global.pTok list. -*/ -struct Fts5TokenizerModule { - char *zName; /* Name of tokenizer */ - void *pUserData; /* User pointer passed to xCreate() */ - fts5_tokenizer x; /* Tokenizer functions */ - void (*xDestroy)(void*); /* Destructor function */ - Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ -}; - -/* -** Virtual-table object. -*/ -struct Fts5Table { - sqlite3_vtab base; /* Base class used by SQLite core */ - Fts5Config *pConfig; /* Virtual table configuration */ - Fts5Index *pIndex; /* Full-text index */ - Fts5Storage *pStorage; /* Document store */ - Fts5Global *pGlobal; /* Global (connection wide) data */ - Fts5Cursor *pSortCsr; /* Sort data from this cursor */ -#ifdef SQLITE_DEBUG - struct Fts5TransactionState ts; -#endif -}; - -struct Fts5MatchPhrase { - Fts5Buffer *pPoslist; /* Pointer to current poslist */ - int nTerm; /* Size of phrase in terms */ -}; - -/* -** pStmt: -** SELECT rowid, FROM ORDER BY +rank; -** -** aIdx[]: -** There is one entry in the aIdx[] array for each phrase in the query, -** the value of which is the offset within aPoslist[] following the last -** byte of the position list for the corresponding phrase. -*/ -struct Fts5Sorter { - sqlite3_stmt *pStmt; - i64 iRowid; /* Current rowid */ - const u8 *aPoslist; /* Position lists for current row */ - int nIdx; /* Number of entries in aIdx[] */ - int aIdx[0]; /* Offsets into aPoslist for current row */ -}; - - -/* -** Virtual-table cursor object. -** -** zSpecial: -** If this is a 'special' query (refer to function fts5SpecialMatch()), -** then this variable points to a nul-terminated buffer containing the -** result to return through the table-name column. It is nul-terminated -** and should eventually be freed using sqlite3_free(). -*/ -struct Fts5Cursor { - sqlite3_vtab_cursor base; /* Base class used by SQLite core */ - int idxNum; /* idxNum passed to xFilter() */ - sqlite3_stmt *pStmt; /* Statement used to read %_content */ - Fts5Expr *pExpr; /* Expression for MATCH queries */ - Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */ - int csrflags; /* Mask of cursor flags (see below) */ - Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */ - char *zSpecial; /* Result of special query */ - - /* "rank" function. Populated on demand from vtab.xColumn(). */ - char *zRank; /* Custom rank function */ - char *zRankArgs; /* Custom rank function args */ - Fts5Auxiliary *pRank; /* Rank callback (or NULL) */ - int nRankArg; /* Number of trailing arguments for rank() */ - sqlite3_value **apRankArg; /* Array of trailing arguments */ - sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */ - - /* Variables used by auxiliary functions */ - i64 iCsrId; /* Cursor id */ - Fts5Auxiliary *pAux; /* Currently executing extension function */ - Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ - int *aColumnSize; /* Values for xColumnSize() */ - - int nInstCount; /* Number of phrase instances */ - int *aInst; /* 3 integers per phrase instance */ -}; - -/* -** Values for Fts5Cursor.csrflags -*/ -#define FTS5CSR_REQUIRE_CONTENT 0x01 -#define FTS5CSR_REQUIRE_DOCSIZE 0x02 -#define FTS5CSR_EOF 0x04 -#define FTS5CSR_FREE_ZRANK 0x08 - -/* -** Macros to Set(), Clear() and Test() cursor flags. -*/ -#define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) -#define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag)) -#define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag)) - -struct Fts5Auxdata { - Fts5Auxiliary *pAux; /* Extension to which this belongs */ - void *pPtr; /* Pointer value */ - void(*xDelete)(void*); /* Destructor */ - Fts5Auxdata *pNext; /* Next object in linked list */ -}; - -#ifdef SQLITE_DEBUG -#define FTS5_BEGIN 1 -#define FTS5_SYNC 2 -#define FTS5_COMMIT 3 -#define FTS5_ROLLBACK 4 -#define FTS5_SAVEPOINT 5 -#define FTS5_RELEASE 6 -#define FTS5_ROLLBACKTO 7 -static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ - switch( op ){ - case FTS5_BEGIN: - assert( p->ts.eState==0 ); - p->ts.eState = 1; - p->ts.iSavepoint = -1; - break; - - case FTS5_SYNC: - assert( p->ts.eState==1 ); - p->ts.eState = 2; - break; - - case FTS5_COMMIT: - assert( p->ts.eState==2 ); - p->ts.eState = 0; - break; - - case FTS5_ROLLBACK: - assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 ); - p->ts.eState = 0; - break; - - case FTS5_SAVEPOINT: - assert( p->ts.eState==1 ); - assert( iSavepoint>=0 ); - assert( iSavepoint>p->ts.iSavepoint ); - p->ts.iSavepoint = iSavepoint; - break; - - case FTS5_RELEASE: - assert( p->ts.eState==1 ); - assert( iSavepoint>=0 ); - assert( iSavepoint<=p->ts.iSavepoint ); - p->ts.iSavepoint = iSavepoint-1; - break; - - case FTS5_ROLLBACKTO: - assert( p->ts.eState==1 ); - assert( iSavepoint>=0 ); - assert( iSavepoint<=p->ts.iSavepoint ); - p->ts.iSavepoint = iSavepoint; - break; - } -} -#else -# define fts5CheckTransactionState(x,y,z) -#endif - -/* -** Return true if pTab is a contentless table. -*/ -static int fts5IsContentless(Fts5Table *pTab){ - return pTab->pConfig->eContent==FTS5_CONTENT_NONE; -} - -/* -** Close a virtual table handle opened by fts5InitVtab(). If the bDestroy -** argument is non-zero, attempt delete the shadow tables from teh database -*/ -static int fts5FreeVtab(Fts5Table *pTab, int bDestroy){ - int rc = SQLITE_OK; - if( pTab ){ - int rc2; - rc2 = sqlite3Fts5IndexClose(pTab->pIndex, bDestroy); - if( rc==SQLITE_OK ) rc = rc2; - rc2 = sqlite3Fts5StorageClose(pTab->pStorage, bDestroy); - if( rc==SQLITE_OK ) rc = rc2; - sqlite3Fts5ConfigFree(pTab->pConfig); - sqlite3_free(pTab); - } - return rc; -} - -/* -** The xDisconnect() virtual table method. -*/ -static int fts5DisconnectMethod(sqlite3_vtab *pVtab){ - return fts5FreeVtab((Fts5Table*)pVtab, 0); -} - -/* -** The xDestroy() virtual table method. -*/ -static int fts5DestroyMethod(sqlite3_vtab *pVtab){ - return fts5FreeVtab((Fts5Table*)pVtab, 1); -} - -/* -** This function is the implementation of both the xConnect and xCreate -** methods of the FTS3 virtual table. -** -** The argv[] array contains the following: -** -** argv[0] -> module name ("fts5") -** argv[1] -> database name -** argv[2] -> table name -** argv[...] -> "column name" and other module argument fields. -*/ -static int fts5InitVtab( - int bCreate, /* True for xCreate, false for xConnect */ - sqlite3 *db, /* The SQLite database connection */ - void *pAux, /* Hash table containing tokenizers */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ - char **pzErr /* Write any error message here */ -){ - Fts5Global *pGlobal = (Fts5Global*)pAux; - const char **azConfig = (const char**)argv; - int rc = SQLITE_OK; /* Return code */ - Fts5Config *pConfig; /* Results of parsing argc/argv */ - Fts5Table *pTab = 0; /* New virtual table object */ - - /* Allocate the new vtab object and parse the configuration */ - pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table)); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr); - assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); - } - if( rc==SQLITE_OK ){ - pTab->pConfig = pConfig; - pTab->pGlobal = pGlobal; - } - - /* Open the index sub-system */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr); - } - - /* Open the storage sub-system */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageOpen( - pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr - ); - } - - /* Call sqlite3_declare_vtab() */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ConfigDeclareVtab(pConfig); - } - - if( rc!=SQLITE_OK ){ - fts5FreeVtab(pTab, 0); - pTab = 0; - }else if( bCreate ){ - fts5CheckTransactionState(pTab, FTS5_BEGIN, 0); - } - *ppVTab = (sqlite3_vtab*)pTab; - return rc; -} - -/* -** The xConnect() and xCreate() methods for the virtual table. All the -** work is done in function fts5InitVtab(). -*/ -static int fts5ConnectMethod( - sqlite3 *db, /* Database connection */ - void *pAux, /* Pointer to tokenizer hash table */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ - char **pzErr /* OUT: sqlite3_malloc'd error message */ -){ - return fts5InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr); -} -static int fts5CreateMethod( - sqlite3 *db, /* Database connection */ - void *pAux, /* Pointer to tokenizer hash table */ - int argc, /* Number of elements in argv array */ - const char * const *argv, /* xCreate/xConnect argument array */ - sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ - char **pzErr /* OUT: sqlite3_malloc'd error message */ -){ - return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); -} - -/* -** The three query plans xBestIndex may choose between. -*/ -#define FTS5_PLAN_SCAN 1 /* No usable constraint */ -#define FTS5_PLAN_MATCH 2 /* ( MATCH ?) */ -#define FTS5_PLAN_SORTED_MATCH 3 /* ( MATCH ? ORDER BY rank) */ -#define FTS5_PLAN_ROWID 4 /* (rowid = ?) */ -#define FTS5_PLAN_SOURCE 5 /* A source cursor for SORTED_MATCH */ -#define FTS5_PLAN_SPECIAL 6 /* An internal query */ - -#define FTS5_PLAN(idxNum) ((idxNum) & 0x7) - -#define FTS5_ORDER_DESC 8 /* ORDER BY rowid DESC */ -#define FTS5_ORDER_ASC 16 /* ORDER BY rowid ASC */ - -/* -** Search the object passed as the first argument for a usable constraint -** on column iCol using operator eOp. If one is found, return its index in -** the pInfo->aConstraint[] array. If no such constraint is found, return -** a negative value. -*/ -static int fts5FindConstraint(sqlite3_index_info *pInfo, int eOp, int iCol){ - int i; - for(i=0; inConstraint; i++){ - struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; - if( p->usable && p->iColumn==iCol && p->op==eOp ) return i; - } - return -1; -} - -/* -** Implementation of the xBestIndex method for FTS5 tables. There -** are three possible strategies, in order of preference: -** -** 1. Full-text search using a MATCH operator. -** 2. A by-rowid lookup. -** 3. A full-table scan. -*/ -static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - Fts5Table *pTab = (Fts5Table*)pVTab; - Fts5Config *pConfig = pTab->pConfig; - int iCons; - int ePlan = FTS5_PLAN_SCAN; - int iRankMatch; - - iCons = fts5FindConstraint(pInfo,SQLITE_INDEX_CONSTRAINT_MATCH,pConfig->nCol); - if( iCons>=0 ){ - ePlan = FTS5_PLAN_MATCH; - pInfo->estimatedCost = 1.0; - }else{ - iCons = fts5FindConstraint(pInfo, SQLITE_INDEX_CONSTRAINT_EQ, -1); - if( iCons>=0 ){ - ePlan = FTS5_PLAN_ROWID; - pInfo->estimatedCost = 2.0; - } - } - - if( iCons>=0 ){ - pInfo->aConstraintUsage[iCons].argvIndex = 1; - pInfo->aConstraintUsage[iCons].omit = 1; - }else{ - pInfo->estimatedCost = 10000000.0; - } - - if( pInfo->nOrderBy==1 ){ - int iSort = pInfo->aOrderBy[0].iColumn; - if( iSort<0 ){ - /* ORDER BY rowid [ASC|DESC] */ - pInfo->orderByConsumed = 1; - }else if( iSort==(pConfig->nCol+1) && ePlan==FTS5_PLAN_MATCH ){ - /* ORDER BY rank [ASC|DESC] */ - pInfo->orderByConsumed = 1; - ePlan = FTS5_PLAN_SORTED_MATCH; - } - - if( pInfo->orderByConsumed ){ - ePlan |= pInfo->aOrderBy[0].desc ? FTS5_ORDER_DESC : FTS5_ORDER_ASC; - } - } - - iRankMatch = fts5FindConstraint( - pInfo, SQLITE_INDEX_CONSTRAINT_MATCH, pConfig->nCol+1 - ); - if( iRankMatch>=0 ){ - pInfo->aConstraintUsage[iRankMatch].argvIndex = 1 + (iCons>=0); - pInfo->aConstraintUsage[iRankMatch].omit = 1; - } - - pInfo->idxNum = ePlan; - return SQLITE_OK; -} - -/* -** Implementation of xOpen method. -*/ -static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ - Fts5Table *pTab = (Fts5Table*)pVTab; - Fts5Config *pConfig = pTab->pConfig; - Fts5Cursor *pCsr; /* New cursor object */ - int nByte; /* Bytes of space to allocate */ - int rc = SQLITE_OK; /* Return code */ - - nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); - pCsr = (Fts5Cursor*)sqlite3_malloc(nByte); - if( pCsr ){ - Fts5Global *pGlobal = pTab->pGlobal; - memset(pCsr, 0, nByte); - pCsr->aColumnSize = (int*)&pCsr[1]; - pCsr->pNext = pGlobal->pCsr; - pGlobal->pCsr = pCsr; - pCsr->iCsrId = ++pGlobal->iNextId; - }else{ - rc = SQLITE_NOMEM; - } - *ppCsr = (sqlite3_vtab_cursor*)pCsr; - return rc; -} - -static int fts5StmtType(int idxNum){ - if( FTS5_PLAN(idxNum)==FTS5_PLAN_SCAN ){ - return (idxNum&FTS5_ORDER_ASC) ? FTS5_STMT_SCAN_ASC : FTS5_STMT_SCAN_DESC; - } - return FTS5_STMT_LOOKUP; -} - -/* -** This function is called after the cursor passed as the only argument -** is moved to point at a different row. It clears all cached data -** specific to the previous row stored by the cursor object. -*/ -static void fts5CsrNewrow(Fts5Cursor *pCsr){ - CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE ); - sqlite3_free(pCsr->aInst); - pCsr->aInst = 0; - pCsr->nInstCount = 0; -} - -/* -** Close the cursor. For additional information see the documentation -** on the xClose method of the virtual table interface. -*/ -static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; - Fts5Cursor **pp; - Fts5Auxdata *pData; - Fts5Auxdata *pNext; - - fts5CsrNewrow(pCsr); - if( pCsr->pStmt ){ - int eStmt = fts5StmtType(pCsr->idxNum); - sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt); - } - if( pCsr->pSorter ){ - Fts5Sorter *pSorter = pCsr->pSorter; - sqlite3_finalize(pSorter->pStmt); - sqlite3_free(pSorter); - } - - if( pCsr->idxNum!=FTS5_PLAN_SOURCE ){ - sqlite3Fts5ExprFree(pCsr->pExpr); - } - - for(pData=pCsr->pAuxdata; pData; pData=pNext){ - pNext = pData->pNext; - if( pData->xDelete ) pData->xDelete(pData->pPtr); - sqlite3_free(pData); - } - - /* Remove the cursor from the Fts5Global.pCsr list */ - for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext); - *pp = pCsr->pNext; - - sqlite3_finalize(pCsr->pRankArgStmt); - sqlite3_free(pCsr->apRankArg); - - sqlite3_free(pCsr->zSpecial); - if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){ - sqlite3_free(pCsr->zRank); - sqlite3_free(pCsr->zRankArgs); - } - sqlite3_free(pCsr); - return SQLITE_OK; -} - -static int fts5SorterNext(Fts5Cursor *pCsr){ - Fts5Sorter *pSorter = pCsr->pSorter; - int rc; - - rc = sqlite3_step(pSorter->pStmt); - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - CsrFlagSet(pCsr, FTS5CSR_EOF); - }else if( rc==SQLITE_ROW ){ - const u8 *a; - const u8 *aBlob; - int nBlob; - int i; - int iOff = 0; - rc = SQLITE_OK; - - pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0); - nBlob = sqlite3_column_bytes(pSorter->pStmt, 1); - aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1); - - for(i=0; i<(pSorter->nIdx-1); i++){ - int iVal; - a += getVarint32(a, iVal); - iOff += iVal; - pSorter->aIdx[i] = iOff; - } - pSorter->aIdx[i] = &aBlob[nBlob] - a; - - pSorter->aPoslist = a; - fts5CsrNewrow(pCsr); - } - - return rc; -} - -/* -** Advance the cursor to the next row in the table that matches the -** search criteria. -** -** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned -** even if we reach end-of-file. The fts5EofMethod() will be called -** subsequently to determine whether or not an EOF was hit. -*/ -static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; - int ePlan = FTS5_PLAN(pCsr->idxNum); - int rc = SQLITE_OK; - - switch( ePlan ){ - case FTS5_PLAN_MATCH: - case FTS5_PLAN_SOURCE: - rc = sqlite3Fts5ExprNext(pCsr->pExpr); - if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ - CsrFlagSet(pCsr, FTS5CSR_EOF); - } - fts5CsrNewrow(pCsr); - break; - - case FTS5_PLAN_SPECIAL: { - CsrFlagSet(pCsr, FTS5CSR_EOF); - break; - } - - case FTS5_PLAN_SORTED_MATCH: { - rc = fts5SorterNext(pCsr); - break; - } - - default: - rc = sqlite3_step(pCsr->pStmt); - if( rc!=SQLITE_ROW ){ - CsrFlagSet(pCsr, FTS5CSR_EOF); - rc = sqlite3_reset(pCsr->pStmt); - }else{ - rc = SQLITE_OK; - } - break; - } - - return rc; -} - -static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bAsc){ - Fts5Config *pConfig = pTab->pConfig; - Fts5Sorter *pSorter; - int nPhrase; - int nByte; - int rc = SQLITE_OK; - char *zSql; - const char *zRank = pCsr->zRank; - const char *zRankArgs = pCsr->zRankArgs; - - nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); - nByte = sizeof(Fts5Sorter) + sizeof(int) * nPhrase; - pSorter = (Fts5Sorter*)sqlite3_malloc(nByte); - if( pSorter==0 ) return SQLITE_NOMEM; - memset(pSorter, 0, nByte); - pSorter->nIdx = nPhrase; - - /* TODO: It would be better to have some system for reusing statement - ** handles here, rather than preparing a new one for each query. But that - ** is not possible as SQLite reference counts the virtual table objects. - ** And since the statement required here reads from this very virtual - ** table, saving it creates a circular reference. - ** - ** If SQLite a built-in statement cache, this wouldn't be a problem. */ - zSql = sqlite3_mprintf("SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s", - pConfig->zDb, pConfig->zName, zRank, pConfig->zName, - (zRankArgs ? ", " : ""), - (zRankArgs ? zRankArgs : ""), - bAsc ? "ASC" : "DESC" - ); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pSorter->pStmt, 0); - sqlite3_free(zSql); - } - - pCsr->pSorter = pSorter; - if( rc==SQLITE_OK ){ - assert( pTab->pSortCsr==0 ); - pTab->pSortCsr = pCsr; - rc = fts5SorterNext(pCsr); - pTab->pSortCsr = 0; - } - - if( rc!=SQLITE_OK ){ - sqlite3_finalize(pSorter->pStmt); - sqlite3_free(pSorter); - pCsr->pSorter = 0; - } - - return rc; -} - -static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bAsc){ - int rc; - rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, bAsc); - if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ - CsrFlagSet(pCsr, FTS5CSR_EOF); - } - fts5CsrNewrow(pCsr); - return rc; -} - -/* -** Process a "special" query. A special query is identified as one with a -** MATCH expression that begins with a '*' character. The remainder of -** the text passed to the MATCH operator are used as the special query -** parameters. -*/ -static int fts5SpecialMatch( - Fts5Table *pTab, - Fts5Cursor *pCsr, - const char *zQuery -){ - int rc = SQLITE_OK; /* Return code */ - const char *z = zQuery; /* Special query text */ - int n; /* Number of bytes in text at z */ - - while( z[0]==' ' ) z++; - for(n=0; z[n] && z[n]!=' '; n++); - - assert( pTab->base.zErrMsg==0 ); - assert( pCsr->zSpecial==0 ); - - if( 0==sqlite3_strnicmp("reads", z, n) ){ - pCsr->zSpecial = sqlite3_mprintf("%d", sqlite3Fts5IndexReads(pTab->pIndex)); - pCsr->idxNum = FTS5_PLAN_SPECIAL; - if( pCsr->zSpecial==0 ) rc = SQLITE_NOMEM; - } - else{ - /* An unrecognized directive. Return an error message. */ - pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z); - rc = SQLITE_ERROR; - } - - return rc; -} - -/* -** Search for an auxiliary function named zName that can be used with table -** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary -** structure. Otherwise, if no such function exists, return NULL. -*/ -static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){ - Fts5Auxiliary *pAux; - - for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){ - if( sqlite3_stricmp(zName, pAux->zFunc)==0 ) return pAux; - } - - /* No function of the specified name was found. Return 0. */ - return 0; -} - - -static int fts5FindRankFunction(Fts5Cursor *pCsr){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - Fts5Config *pConfig = pTab->pConfig; - int rc = SQLITE_OK; - Fts5Auxiliary *pAux = 0; - const char *zRank = pCsr->zRank; - const char *zRankArgs = pCsr->zRankArgs; - - if( zRankArgs ){ - char *zSql = sqlite3_mprintf("SELECT %s", zRankArgs); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_stmt *pStmt = 0; - rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 ); - if( rc==SQLITE_OK ){ - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - int nByte; - pCsr->nRankArg = sqlite3_column_count(pStmt); - nByte = sizeof(sqlite3_value*)*pCsr->nRankArg; - pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte); - if( rc==SQLITE_OK ){ - int i; - for(i=0; inRankArg; i++){ - pCsr->apRankArg[i] = sqlite3_column_value(pStmt, i); - } - } - pCsr->pRankArgStmt = pStmt; - }else{ - rc = sqlite3_finalize(pStmt); - assert( rc!=SQLITE_OK ); - } - } - } - } - - if( rc==SQLITE_OK ){ - pAux = fts5FindAuxiliary(pTab, zRank); - if( pAux==0 ){ - assert( pTab->base.zErrMsg==0 ); - pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank); - rc = SQLITE_ERROR; - } - } - - pCsr->pRank = pAux; - return rc; -} - - -static int fts5CursorParseRank( - Fts5Config *pConfig, - Fts5Cursor *pCsr, - sqlite3_value *pRank -){ - int rc = SQLITE_OK; - if( pRank ){ - const char *z = (const char*)sqlite3_value_text(pRank); - char *zRank = 0; - char *zRankArgs = 0; - - rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs); - if( rc==SQLITE_OK ){ - pCsr->zRank = zRank; - pCsr->zRankArgs = zRankArgs; - CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK); - }else if( rc==SQLITE_ERROR ){ - pCsr->base.pVtab->zErrMsg = sqlite3_mprintf( - "parse error in rank function: %s", z - ); - } - }else{ - if( pConfig->zRank ){ - pCsr->zRank = (char*)pConfig->zRank; - pCsr->zRankArgs = (char*)pConfig->zRankArgs; - }else{ - pCsr->zRank = (char*)FTS5_DEFAULT_RANK; - pCsr->zRankArgs = 0; - } - } - return rc; -} - -/* -** This is the xFilter interface for the virtual table. See -** the virtual table xFilter method documentation for additional -** information. -** -** There are three possible query strategies: -** -** 1. Full-text search using a MATCH operator. -** 2. A by-rowid lookup. -** 3. A full-table scan. -*/ -static int fts5FilterMethod( - sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ - int idxNum, /* Strategy index */ - const char *idxStr, /* Unused */ - int nVal, /* Number of elements in apVal */ - sqlite3_value **apVal /* Arguments for the indexing scheme */ -){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; - int bAsc = ((idxNum & FTS5_ORDER_ASC) ? 1 : 0); - int rc = SQLITE_OK; - - assert( nVal<=2 ); - assert( pCsr->pStmt==0 ); - assert( pCsr->pExpr==0 ); - assert( pCsr->csrflags==0 ); - assert( pCsr->pRank==0 ); - assert( pCsr->zRank==0 ); - assert( pCsr->zRankArgs==0 ); - - if( pTab->pSortCsr ){ - /* If pSortCsr is non-NULL, then this call is being made as part of - ** processing for a "... MATCH ORDER BY rank" query (ePlan is - ** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will - ** return results to the user for this query. The current cursor - ** (pCursor) is used to execute the query issued by function - ** fts5CursorFirstSorted() above. */ - assert( FTS5_PLAN(idxNum)==FTS5_PLAN_SCAN ); - pCsr->idxNum = FTS5_PLAN_SOURCE; - pCsr->pExpr = pTab->pSortCsr->pExpr; - rc = fts5CursorFirst(pTab, pCsr, bAsc); - }else{ - int ePlan = FTS5_PLAN(idxNum); - pCsr->idxNum = idxNum; - if( ePlan==FTS5_PLAN_MATCH || ePlan==FTS5_PLAN_SORTED_MATCH ){ - const char *zExpr = (const char*)sqlite3_value_text(apVal[0]); - - rc = fts5CursorParseRank(pTab->pConfig, pCsr, (nVal==2 ? apVal[1] : 0)); - if( rc==SQLITE_OK ){ - if( zExpr[0]=='*' ){ - /* The user has issued a query of the form "MATCH '*...'". This - ** indicates that the MATCH expression is not a full text query, - ** but a request for an internal parameter. */ - rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]); - }else{ - char **pzErr = &pTab->base.zErrMsg; - rc = sqlite3Fts5ExprNew(pTab->pConfig, zExpr, &pCsr->pExpr, pzErr); - if( rc==SQLITE_OK ){ - if( ePlan==FTS5_PLAN_MATCH ){ - rc = fts5CursorFirst(pTab, pCsr, bAsc); - }else{ - rc = fts5CursorFirstSorted(pTab, pCsr, bAsc); - } - } - } - } - }else{ - /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup - ** by rowid (ePlan==FTS5_PLAN_ROWID). */ - int eStmt = fts5StmtType(idxNum); - rc = sqlite3Fts5StorageStmt( - pTab->pStorage, eStmt, &pCsr->pStmt, &pTab->base.zErrMsg - ); - if( rc==SQLITE_OK ){ - if( ePlan==FTS5_PLAN_ROWID ){ - sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); - } - rc = fts5NextMethod(pCursor); - } - } - } - - return rc; -} - -/* -** This is the xEof method of the virtual table. SQLite calls this -** routine to find out if it has reached the end of a result set. -*/ -static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; - return (CsrFlagTest(pCsr, FTS5CSR_EOF) ? 1 : 0); -} - -/* -** Return the rowid that the cursor currently points to. -*/ -static i64 fts5CursorRowid(Fts5Cursor *pCsr){ - assert( FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_MATCH - || FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_SORTED_MATCH - || FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_SOURCE - ); - if( pCsr->pSorter ){ - return pCsr->pSorter->iRowid; - }else{ - return sqlite3Fts5ExprRowid(pCsr->pExpr); - } -} - -/* -** This is the xRowid method. The SQLite core calls this routine to -** retrieve the rowid for the current row of the result set. fts5 -** exposes %_content.docid as the rowid for the virtual table. The -** rowid should be written to *pRowid. -*/ -static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; - int ePlan = FTS5_PLAN(pCsr->idxNum); - - assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); - switch( ePlan ){ - case FTS5_PLAN_SPECIAL: - *pRowid = 0; - break; - - case FTS5_PLAN_SOURCE: - case FTS5_PLAN_MATCH: - case FTS5_PLAN_SORTED_MATCH: - *pRowid = fts5CursorRowid(pCsr); - break; - - default: - *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); - break; - } - - return SQLITE_OK; -} - -/* -** If the cursor requires seeking (bSeekRequired flag is set), seek it. -** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise. -*/ -static int fts5SeekCursor(Fts5Cursor *pCsr){ - int rc = SQLITE_OK; - - /* If the cursor does not yet have a statement handle, obtain one now. */ - if( pCsr->pStmt==0 ){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - int eStmt = fts5StmtType(pCsr->idxNum); - rc = sqlite3Fts5StorageStmt( - pTab->pStorage, eStmt, &pCsr->pStmt, &pTab->base.zErrMsg - ); - assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ); - } - - if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){ - assert( pCsr->pExpr ); - sqlite3_reset(pCsr->pStmt); - sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr)); - rc = sqlite3_step(pCsr->pStmt); - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT); - }else{ - rc = sqlite3_reset(pCsr->pStmt); - if( rc==SQLITE_OK ){ - rc = SQLITE_CORRUPT_VTAB; - } - } - } - return rc; -} - -static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ - va_list ap; /* ... printf arguments */ - va_start(ap, zFormat); - assert( p->base.zErrMsg==0 ); - p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap); - va_end(ap); -} - -/* -** This function is called to handle an FTS INSERT command. In other words, -** an INSERT statement of the form: -** -** INSERT INTO fts(fts) VALUES($pCmd) -** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal) -** -** Argument pVal is the value assigned to column "fts" by the INSERT -** statement. This function returns SQLITE_OK if successful, or an SQLite -** error code if an error occurs. -** -** The commands implemented by this function are documented in the "Special -** INSERT Directives" section of the documentation. It should be updated if -** more commands are added to this function. -*/ -static int fts5SpecialInsert( - Fts5Table *pTab, /* Fts5 table object */ - sqlite3_value *pCmd, /* Value inserted into special column */ - sqlite3_value *pVal /* Value inserted into rowid column */ -){ - Fts5Config *pConfig = pTab->pConfig; - const char *z = (const char*)sqlite3_value_text(pCmd); - int rc = SQLITE_OK; - int bError = 0; - - if( 0==sqlite3_stricmp("delete-all", z) ){ - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ - fts5SetVtabError(pTab, - "'delete-all' may only be used with a " - "contentless or external content fts5 table" - ); - rc = SQLITE_ERROR; - }else{ - rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); - } - }else if( 0==sqlite3_stricmp("rebuild", z) ){ - if( pConfig->eContent==FTS5_CONTENT_NONE ){ - fts5SetVtabError(pTab, - "'rebuild' may not be used with a contentless fts5 table" - ); - rc = SQLITE_ERROR; - }else{ - rc = sqlite3Fts5StorageRebuild(pTab->pStorage); - } - }else if( 0==sqlite3_stricmp("optimize", z) ){ - rc = sqlite3Fts5StorageOptimize(pTab->pStorage); - }else if( 0==sqlite3_stricmp("integrity-check", z) ){ - rc = sqlite3Fts5StorageIntegrity(pTab->pStorage); - }else{ - rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, z, pVal, &bError); - if( rc==SQLITE_OK ){ - if( bError ){ - rc = SQLITE_ERROR; - }else{ - rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal); - } - } - } - return rc; -} - -static int fts5SpecialDelete( - Fts5Table *pTab, - sqlite3_value **apVal, - sqlite3_int64 *piRowid -){ - int rc = SQLITE_OK; - int eType1 = sqlite3_value_type(apVal[1]); - if( eType1==SQLITE_INTEGER ){ - sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); - rc = sqlite3Fts5StorageSpecialDelete(pTab->pStorage, iDel, &apVal[2]); - } - return rc; -} - -/* -** This function is the implementation of the xUpdate callback used by -** FTS3 virtual tables. It is invoked by SQLite each time a row is to be -** inserted, updated or deleted. -*/ -static int fts5UpdateMethod( - sqlite3_vtab *pVtab, /* Virtual table handle */ - int nArg, /* Size of argument array */ - sqlite3_value **apVal, /* Array of arguments */ - sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ -){ - Fts5Table *pTab = (Fts5Table*)pVtab; - Fts5Config *pConfig = pTab->pConfig; - int eType0; /* value_type() of apVal[0] */ - int eConflict; /* ON CONFLICT for this DML */ - int rc = SQLITE_OK; /* Return code */ - - /* A transaction must be open when this is called. */ - assert( pTab->ts.eState==1 ); - - /* A delete specifies a single argument - the rowid of the row to remove. - ** Update and insert operations pass: - ** - ** 1. The "old" rowid, or NULL. - ** 2. The "new" rowid. - ** 3. Values for each of the nCol matchable columns. - ** 4. Values for the two hidden columns ( and "rank"). - */ - assert( nArg==1 || nArg==(2 + pConfig->nCol + 2) ); - - eType0 = sqlite3_value_type(apVal[0]); - eConflict = sqlite3_vtab_on_conflict(pConfig->db); - - assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); - assert( pVtab->zErrMsg==0 ); - - if( rc==SQLITE_OK && eType0==SQLITE_INTEGER ){ - if( fts5IsContentless(pTab) ){ - pTab->base.zErrMsg = sqlite3_mprintf( - "cannot %s contentless fts5 table: %s", - (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName - ); - rc = SQLITE_ERROR; - }else{ - i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); - } - }else if( nArg>1 ){ - sqlite3_value *pCmd = apVal[2 + pConfig->nCol]; - if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){ - const char *z = (const char*)sqlite3_value_text(pCmd); - if( pConfig->eContent!=FTS5_CONTENT_NORMAL - && 0==sqlite3_stricmp("delete", z) - ){ - return fts5SpecialDelete(pTab, apVal, pRowid); - }else{ - return fts5SpecialInsert(pTab, pCmd, apVal[2 + pConfig->nCol + 1]); - } - } - } - - - if( rc==SQLITE_OK && nArg>1 ){ - rc = sqlite3Fts5StorageInsert(pTab->pStorage, apVal, eConflict, pRowid); - } - - return rc; -} - -/* -** Implementation of xSync() method. -*/ -static int fts5SyncMethod(sqlite3_vtab *pVtab){ - int rc; - Fts5Table *pTab = (Fts5Table*)pVtab; - fts5CheckTransactionState(pTab, FTS5_SYNC, 0); - rc = sqlite3Fts5StorageSync(pTab->pStorage, 1); - return rc; -} - -/* -** Implementation of xBegin() method. -*/ -static int fts5BeginMethod(sqlite3_vtab *pVtab){ - fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0); - return SQLITE_OK; -} - -/* -** Implementation of xCommit() method. This is a no-op. The contents of -** the pending-terms hash-table have already been flushed into the database -** by fts5SyncMethod(). -*/ -static int fts5CommitMethod(sqlite3_vtab *pVtab){ - fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0); - return SQLITE_OK; -} - -/* -** Implementation of xRollback(). Discard the contents of the pending-terms -** hash-table. Any changes made to the database are reverted by SQLite. -*/ -static int fts5RollbackMethod(sqlite3_vtab *pVtab){ - int rc; - Fts5Table *pTab = (Fts5Table*)pVtab; - fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); - rc = sqlite3Fts5StorageRollback(pTab->pStorage); - return rc; -} - -static void *fts5ApiUserData(Fts5Context *pCtx){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - return pCsr->pAux->pUserData; -} - -static int fts5ApiColumnCount(Fts5Context *pCtx){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol; -} - -static int fts5ApiColumnTotalSize( - Fts5Context *pCtx, - int iCol, - sqlite3_int64 *pnToken -){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken); -} - -static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); -} - -static int fts5ApiTokenize( - Fts5Context *pCtx, - const char *pText, int nText, - void *pUserData, - int (*xToken)(void*, const char*, int, int, int) -){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - return sqlite3Fts5Tokenize(pTab->pConfig, pText, nText, pUserData, xToken); -} - -static int fts5ApiPhraseCount(Fts5Context *pCtx){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - return sqlite3Fts5ExprPhraseCount(pCsr->pExpr); -} - -static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); -} - -static int fts5CsrPoslist(Fts5Cursor *pCsr, int iPhrase, const u8 **pa){ - int n; - if( pCsr->pSorter ){ - Fts5Sorter *pSorter = pCsr->pSorter; - int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); - n = pSorter->aIdx[iPhrase] - i1; - *pa = &pSorter->aPoslist[i1]; - }else{ - n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); - } - return n; -} - -/* -** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated -** correctly for the current view. Return SQLITE_OK if successful, or an -** SQLite error code otherwise. -*/ -static int fts5CacheInstArray(Fts5Cursor *pCsr){ - int rc = SQLITE_OK; - if( pCsr->aInst==0 ){ - Fts5PoslistReader *aIter; /* One iterator for each phrase */ - int nIter; /* Number of iterators/phrases */ - int nByte; - - nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); - nByte = sizeof(Fts5PoslistReader) * nIter; - aIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); - if( aIter ){ - Fts5Buffer buf = {0, 0, 0}; /* Build up aInst[] here */ - int nInst = 0; /* Number instances seen so far */ - int i; - - /* Initialize all iterators */ - for(i=0; iaInst = (int*)buf.p; - pCsr->nInstCount = nInst; - sqlite3_free(aIter); - } - } - return rc; -} - -static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - int rc; - if( SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ - *pnInst = pCsr->nInstCount; - } - return rc; -} - -static int fts5ApiInst( - Fts5Context *pCtx, - int iIdx, - int *piPhrase, - int *piCol, - int *piOff -){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - int rc; - if( SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ - if( iIdx<0 || iIdx>=pCsr->nInstCount ){ - rc = SQLITE_RANGE; - }else{ - *piPhrase = pCsr->aInst[iIdx*3]; - *piCol = pCsr->aInst[iIdx*3 + 1]; - *piOff = pCsr->aInst[iIdx*3 + 2]; - } - } - return rc; -} - -static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ - return fts5CursorRowid((Fts5Cursor*)pCtx); -} - -static int fts5ApiColumnText( - Fts5Context *pCtx, - int iCol, - const char **pz, - int *pn -){ - int rc = SQLITE_OK; - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ - *pz = 0; - *pn = 0; - }else{ - rc = fts5SeekCursor(pCsr); - if( rc==SQLITE_OK ){ - *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); - *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); - } - } - return rc; -} - -static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - int rc = SQLITE_OK; - - if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ - i64 iRowid = fts5CursorRowid(pCsr); - rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); - } - if( iCol<0 ){ - int i; - *pnToken = 0; - for(i=0; ipConfig->nCol; i++){ - *pnToken += pCsr->aColumnSize[i]; - } - }else if( iColpConfig->nCol ){ - *pnToken = pCsr->aColumnSize[iCol]; - }else{ - *pnToken = 0; - rc = SQLITE_RANGE; - } - return rc; -} - -static int fts5ApiSetAuxdata( - Fts5Context *pCtx, /* Fts5 context */ - void *pPtr, /* Pointer to save as auxdata */ - void(*xDelete)(void*) /* Destructor for pPtr (or NULL) */ -){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Auxdata *pData; - - for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){ - if( pData->pAux==pCsr->pAux ) break; - } - - if( pData ){ - if( pData->xDelete ){ - pData->xDelete(pData->pPtr); - } - }else{ - pData = (Fts5Auxdata*)sqlite3_malloc(sizeof(Fts5Auxdata)); - if( pData==0 ){ - if( xDelete ) xDelete(pPtr); - return SQLITE_NOMEM; - } - memset(pData, 0, sizeof(Fts5Auxdata)); - pData->pAux = pCsr->pAux; - pData->pNext = pCsr->pAuxdata; - pCsr->pAuxdata = pData; - } - - pData->xDelete = xDelete; - pData->pPtr = pPtr; - return SQLITE_OK; -} - -static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Auxdata *pData; - void *pRet = 0; - - for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){ - if( pData->pAux==pCsr->pAux ) break; - } - - if( pData ){ - pRet = pData->pPtr; - if( bClear ){ - pData->pPtr = 0; - pData->xDelete = 0; - } - } - - return pRet; -} - -static int fts5ApiQueryPhrase(Fts5Context*, int, void*, - int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) -); - -static const Fts5ExtensionApi sFts5Api = { - 1, /* iVersion */ - fts5ApiUserData, - fts5ApiColumnCount, - fts5ApiRowCount, - fts5ApiColumnTotalSize, - fts5ApiTokenize, - fts5ApiPhraseCount, - fts5ApiPhraseSize, - fts5ApiInstCount, - fts5ApiInst, - fts5ApiRowid, - fts5ApiColumnText, - fts5ApiColumnSize, - fts5ApiQueryPhrase, - fts5ApiSetAuxdata, - fts5ApiGetAuxdata, -}; - - -/* -** Implementation of API function xQueryPhrase(). -*/ -static int fts5ApiQueryPhrase( - Fts5Context *pCtx, - int iPhrase, - void *pUserData, - int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*) -){ - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - int rc; - Fts5Cursor *pNew = 0; - - rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew); - if( rc==SQLITE_OK ){ - Fts5Config *pConf = pTab->pConfig; - pNew->idxNum = FTS5_PLAN_MATCH; - pNew->base.pVtab = (sqlite3_vtab*)pTab; - rc = sqlite3Fts5ExprPhraseExpr(pConf, pCsr->pExpr, iPhrase, &pNew->pExpr); - } - - if( rc==SQLITE_OK ){ - for(rc = fts5CursorFirst(pTab, pNew, 0); - rc==SQLITE_OK && CsrFlagTest(pNew, FTS5CSR_EOF)==0; - rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew) - ){ - rc = xCallback(&sFts5Api, (Fts5Context*)pNew, pUserData); - if( rc!=SQLITE_OK ){ - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - break; - } - } - } - - fts5CloseMethod((sqlite3_vtab_cursor*)pNew); - return rc; -} - -static void fts5ApiInvoke( - Fts5Auxiliary *pAux, - Fts5Cursor *pCsr, - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - assert( pCsr->pAux==0 ); - pCsr->pAux = pAux; - pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); - pCsr->pAux = 0; -} - -static void fts5ApiCallback( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - - Fts5Auxiliary *pAux; - Fts5Cursor *pCsr; - i64 iCsrId; - - assert( argc>=1 ); - pAux = (Fts5Auxiliary*)sqlite3_user_data(context); - iCsrId = sqlite3_value_int64(argv[0]); - - for(pCsr=pAux->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ - if( pCsr->iCsrId==iCsrId ) break; - } - if( pCsr==0 ){ - char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId); - sqlite3_result_error(context, zErr, -1); - }else{ - fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); - } -} - -/* -** Return a "position-list blob" corresponding to the current position of -** cursor pCsr via sqlite3_result_blob(). A position-list blob contains -** the current position-list for each phrase in the query associated with -** cursor pCsr. -** -** A position-list blob begins with (nPhrase-1) varints, where nPhrase is -** the number of phrases in the query. Following the varints are the -** concatenated position lists for each phrase, in order. -** -** The first varint (if it exists) contains the size of the position list -** for phrase 0. The second (same disclaimer) contains the size of position -** list 1. And so on. There is no size field for the final position list, -** as it can be derived from the total size of the blob. -*/ -static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){ - int i; - int rc = SQLITE_OK; - int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); - Fts5Buffer val; - - memset(&val, 0, sizeof(Fts5Buffer)); - - /* Append the varints */ - for(i=0; i<(nPhrase-1); i++){ - const u8 *dummy; - int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); - sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); - } - - /* Append the position lists */ - for(i=0; ipExpr, i, &pPoslist); - sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); - } - - sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); - return rc; -} - -/* -** This is the xColumn method, called by SQLite to request a value from -** the row that the supplied cursor currently points to. -*/ -static int fts5ColumnMethod( - sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ - sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ - int iCol /* Index of column to read value from */ -){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Config *pConfig = pTab->pConfig; - Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; - int rc = SQLITE_OK; - - assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); - - if( pCsr->idxNum==FTS5_PLAN_SPECIAL ){ - if( iCol==pConfig->nCol ){ - sqlite3_result_text(pCtx, pCsr->zSpecial, -1, SQLITE_TRANSIENT); - } - }else - - if( iCol==pConfig->nCol ){ - /* User is requesting the value of the special column with the same name - ** as the table. Return the cursor integer id number. This value is only - ** useful in that it may be passed as the first argument to an FTS5 - ** auxiliary function. */ - sqlite3_result_int64(pCtx, pCsr->iCsrId); - }else if( iCol==pConfig->nCol+1 ){ - - /* The value of the "rank" column. */ - if( FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_SOURCE ){ - fts5PoslistBlob(pCtx, pCsr); - }else if( - FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_MATCH - || FTS5_PLAN(pCsr->idxNum)==FTS5_PLAN_SORTED_MATCH - ){ - if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){ - fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); - } - } - }else if( !fts5IsContentless(pTab) ){ - rc = fts5SeekCursor(pCsr); - if( rc==SQLITE_OK ){ - sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); - } - } - return rc; -} - - -/* -** This routine implements the xFindFunction method for the FTS3 -** virtual table. -*/ -static int fts5FindFunctionMethod( - sqlite3_vtab *pVtab, /* Virtual table handle */ - int nArg, /* Number of SQL function arguments */ - const char *zName, /* Name of SQL function */ - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ - void **ppArg /* OUT: User data for *pxFunc */ -){ - Fts5Table *pTab = (Fts5Table*)pVtab; - Fts5Auxiliary *pAux; - - pAux = fts5FindAuxiliary(pTab, zName); - if( pAux ){ - *pxFunc = fts5ApiCallback; - *ppArg = (void*)pAux; - return 1; - } - - /* No function of the specified name was found. Return 0. */ - return 0; -} - -/* -** Implementation of FTS3 xRename method. Rename an fts5 table. -*/ -static int fts5RenameMethod( - sqlite3_vtab *pVtab, /* Virtual table handle */ - const char *zName /* New name of table */ -){ - int rc = SQLITE_OK; - return rc; -} - -/* -** The xSavepoint() method. -** -** Flush the contents of the pending-terms table to disk. -*/ -static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; - fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); - return sqlite3Fts5StorageSync(pTab->pStorage, 0); -} - -/* -** The xRelease() method. -** -** This is a no-op. -*/ -static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; - fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); - return sqlite3Fts5StorageSync(pTab->pStorage, 0); -} - -/* -** The xRollbackTo() method. -** -** Discard the contents of the pending terms table. -*/ -static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; - fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); - return sqlite3Fts5StorageRollback(pTab->pStorage); -} - -/* -** Register a new auxiliary function with global context pGlobal. -*/ -static int fts5CreateAux( - fts5_api *pApi, /* Global context (one per db handle) */ - const char *zName, /* Name of new function */ - void *pUserData, /* User data for aux. function */ - fts5_extension_function xFunc, /* Aux. function implementation */ - void(*xDestroy)(void*) /* Destructor for pUserData */ -){ - Fts5Global *pGlobal = (Fts5Global*)pApi; - int rc = sqlite3_overload_function(pGlobal->db, zName, -1); - if( rc==SQLITE_OK ){ - Fts5Auxiliary *pAux; - int nByte; /* Bytes of space to allocate */ - - nByte = sizeof(Fts5Auxiliary) + strlen(zName) + 1; - pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte); - if( pAux ){ - memset(pAux, 0, nByte); - pAux->zFunc = (char*)&pAux[1]; - strcpy(pAux->zFunc, zName); - pAux->pGlobal = pGlobal; - pAux->pUserData = pUserData; - pAux->xFunc = xFunc; - pAux->xDestroy = xDestroy; - pAux->pNext = pGlobal->pAux; - pGlobal->pAux = pAux; - }else{ - rc = SQLITE_NOMEM; - } - } - - return rc; -} - -/* -** Register a new tokenizer. This is the implementation of the -** fts5_api.xCreateTokenizer() method. -*/ -static int fts5CreateTokenizer( - fts5_api *pApi, /* Global context (one per db handle) */ - const char *zName, /* Name of new function */ - void *pUserData, /* User data for aux. function */ - fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ - void(*xDestroy)(void*) /* Destructor for pUserData */ -){ - Fts5Global *pGlobal = (Fts5Global*)pApi; - Fts5TokenizerModule *pNew; - int nByte; /* Bytes of space to allocate */ - int rc = SQLITE_OK; - - nByte = sizeof(Fts5TokenizerModule) + strlen(zName) + 1; - pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte); - if( pNew ){ - memset(pNew, 0, nByte); - pNew->zName = (char*)&pNew[1]; - strcpy(pNew->zName, zName); - pNew->pUserData = pUserData; - pNew->x = *pTokenizer; - pNew->xDestroy = xDestroy; - pNew->pNext = pGlobal->pTok; - pGlobal->pTok = pNew; - if( pNew->pNext==0 ){ - pGlobal->pDfltTok = pNew; - } - }else{ - rc = SQLITE_NOMEM; - } - - return rc; -} - -/* -** Find a tokenizer. This is the implementation of the -** fts5_api.xFindTokenizer() method. -*/ -static int fts5FindTokenizer( - fts5_api *pApi, /* Global context (one per db handle) */ - const char *zName, /* Name of new function */ - void **ppUserData, - fts5_tokenizer *pTokenizer /* Populate this object */ -){ - Fts5Global *pGlobal = (Fts5Global*)pApi; - int rc = SQLITE_OK; - Fts5TokenizerModule *pTok; - - if( zName==0 ){ - pTok = pGlobal->pDfltTok; - }else{ - for(pTok=pGlobal->pTok; pTok; pTok=pTok->pNext){ - if( sqlite3_stricmp(zName, pTok->zName)==0 ) break; - } - } - - if( pTok ){ - *pTokenizer = pTok->x; - *ppUserData = pTok->pUserData; - }else{ - memset(pTokenizer, 0, sizeof(fts5_tokenizer)); - rc = SQLITE_ERROR; - } - - return rc; -} - -int sqlite3Fts5GetTokenizer( - Fts5Global *pGlobal, - const char **azArg, - int nArg, - Fts5Tokenizer **ppTok, - fts5_tokenizer **ppTokApi -){ - Fts5TokenizerModule *pMod = 0; - int rc = SQLITE_OK; - - if( nArg==0 ){ - pMod = pGlobal->pDfltTok; - }else{ - for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){ - if( sqlite3_stricmp(azArg[0], pMod->zName)==0 ) break; - } - } - - if( pMod==0 ){ - rc = SQLITE_ERROR; - }else{ - rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok); - *ppTokApi = &pMod->x; - } - - if( rc!=SQLITE_OK ){ - *ppTokApi = 0; - *ppTok = 0; - } - - return rc; -} - -static void fts5ModuleDestroy(void *pCtx){ - Fts5TokenizerModule *pTok, *pNextTok; - Fts5Auxiliary *pAux, *pNextAux; - Fts5Global *pGlobal = (Fts5Global*)pCtx; - - for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){ - pNextAux = pAux->pNext; - if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData); - sqlite3_free(pAux); - } - - for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){ - pNextTok = pTok->pNext; - if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData); - sqlite3_free(pTok); - } - - sqlite3_free(pGlobal); -} - -static void fts5Fts5Func( - sqlite3_context *pCtx, /* Function call context */ - int nArg, /* Number of args */ - sqlite3_value **apVal /* Function arguments */ -){ - Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); - char buf[8]; - assert( nArg==0 ); - assert( sizeof(buf)>=sizeof(pGlobal) ); - memcpy(buf, (void*)&pGlobal, sizeof(pGlobal)); - sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT); -} - -int sqlite3Fts5Init(sqlite3 *db){ - static const sqlite3_module fts5Mod = { - /* iVersion */ 2, - /* xCreate */ fts5CreateMethod, - /* xConnect */ fts5ConnectMethod, - /* xBestIndex */ fts5BestIndexMethod, - /* xDisconnect */ fts5DisconnectMethod, - /* xDestroy */ fts5DestroyMethod, - /* xOpen */ fts5OpenMethod, - /* xClose */ fts5CloseMethod, - /* xFilter */ fts5FilterMethod, - /* xNext */ fts5NextMethod, - /* xEof */ fts5EofMethod, - /* xColumn */ fts5ColumnMethod, - /* xRowid */ fts5RowidMethod, - /* xUpdate */ fts5UpdateMethod, - /* xBegin */ fts5BeginMethod, - /* xSync */ fts5SyncMethod, - /* xCommit */ fts5CommitMethod, - /* xRollback */ fts5RollbackMethod, - /* xFindFunction */ fts5FindFunctionMethod, - /* xRename */ fts5RenameMethod, - /* xSavepoint */ fts5SavepointMethod, - /* xRelease */ fts5ReleaseMethod, - /* xRollbackTo */ fts5RollbackToMethod, - }; - - int rc; - Fts5Global *pGlobal = 0; - pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); - - if( pGlobal==0 ){ - rc = SQLITE_NOMEM; - }else{ - void *p = (void*)pGlobal; - memset(pGlobal, 0, sizeof(Fts5Global)); - pGlobal->db = db; - pGlobal->api.iVersion = 1; - pGlobal->api.xCreateFunction = fts5CreateAux; - pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; - pGlobal->api.xFindTokenizer = fts5FindTokenizer; - rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); - if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); - if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); - if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); - if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function( - db, "fts5", 0, SQLITE_UTF8, p, fts5Fts5Func, 0, 0 - ); - } - } - return rc; -} - - DELETED ext/fts5/fts5.h Index: ext/fts5/fts5.h ================================================================== --- ext/fts5/fts5.h +++ /dev/null @@ -1,319 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -** -** Interfaces to extend FTS5. Using the interfaces defined in this file, -** FTS5 may be extended with: -** -** * custom tokenizers, and -** * custom auxiliary functions. -*/ - - -#ifndef _FTS5_H -#define _FTS5_H - -#include "sqlite3.h" - -/************************************************************************* -** CUSTOM AUXILIARY FUNCTIONS -** -** Virtual table implementations may overload SQL functions by implementing -** the sqlite3_module.xFindFunction() method. -*/ - -typedef struct Fts5ExtensionApi Fts5ExtensionApi; -typedef struct Fts5Context Fts5Context; - -typedef void (*fts5_extension_function)( - const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ - Fts5Context *pFts, /* First arg to pass to pApi functions */ - sqlite3_context *pCtx, /* Context for returning result/error */ - int nVal, /* Number of values in apVal[] array */ - sqlite3_value **apVal /* Array of trailing arguments */ -); - -/* -** EXTENSION API FUNCTIONS -** -** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. -** -** xColumnTotalSize(pFts, iCol, pnToken): -** If parameter iCol is less than zero, set output variable *pnToken -** to the total number of tokens in the FTS5 table. Or, if iCol is -** non-negative but less than the number of columns in the table, return -** the total number of tokens in column iCol, considering all rows in -** the FTS5 table. -** -** If parameter iCol is greater than or equal to the number of columns -** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. -** an OOM condition or IO error), an appropriate SQLite error code is -** returned. -** -** xColumnCount: -** Returns the number of columns in the FTS5 table. -** -** xColumnSize: -** Reports the size in tokens of a column value from the current row. -** -** xColumnText: -** Reports the size in tokens of a column value from the current row. -** -** xPhraseCount: -** Returns the number of phrases in the current query expression. -** -** xPhraseSize: -** Returns the number of tokens in phrase iPhrase of the query. Phrases -** are numbered starting from zero. -** -** xInstCount: -** Set *pnInst to the total number of occurrences of all phrases within -** the query within the current row. Return SQLITE_OK if successful, or -** an error code (i.e. SQLITE_NOMEM) if an error occurs. -** -** xInst: -** Query for the details of phrase match iIdx within the current row. -** Phrase matches are numbered starting from zero, so the iIdx argument -** should be greater than or equal to zero and smaller than the value -** output by xInstCount(). -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. -** -** xRowid: -** Returns the rowid of the current row. -** -** xTokenize: -** Tokenize text using the tokenizer belonging to the FTS5 table. -** -** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback): -** This API function is used to query the FTS table for phrase iPhrase -** of the current query. Specifically, a query equivalent to: -** -** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid DESC -** -** with $p set to a phrase equivalent to the phrase iPhrase of the -** current query is executed. For each row visited, the callback function -** passed as the fourth argument is invoked. The context and API objects -** passed to the callback function may be used to access the properties of -** each matched row. Invoking Api.xUserData() returns a copy of the pointer -** passed as the third argument to pUserData. -** -** If the callback function returns any value other than SQLITE_OK, the -** query is abandoned and the xQueryPhrase function returns immediately. -** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. -** Otherwise, the error code is propagated upwards. -** -** If the query runs to completion without incident, SQLITE_OK is returned. -** Or, if some error occurs before the query completes or is aborted by -** the callback, an SQLite error code is returned. -** -** -** xSetAuxdata(pFts5, pAux, xDelete) -** -** Save the pointer passed as the second argument as the extension functions -** "auxiliary data". The pointer may then be retrieved by the current or any -** future invocation of the same fts5 extension function made as part of -** of the same MATCH query using the xGetAuxdata() API. -** -** Each extension function is allocated a single auxiliary data slot for -** each FTS query (MATCH expression). If the extension function is invoked -** more than once for a single FTS query, then all invocations share a -** single auxiliary data context. -** -** If there is already an auxiliary data pointer when this function is -** invoked, then it is replaced by the new pointer. If an xDelete callback -** was specified along with the original pointer, it is invoked at this -** point. -** -** The xDelete callback, if one is specified, is also invoked on the -** auxiliary data pointer after the FTS5 query has finished. -** -** If an error (e.g. an OOM condition) occurs within this function, an -** the auxiliary data is set to NULL and an error code returned. If the -** xDelete parameter was not NULL, it is invoked on the auxiliary data -** pointer before returning. -** -** -** xGetAuxdata(pFts5, bClear) -** -** Returns the current auxiliary data pointer for the fts5 extension -** function. See the xSetAuxdata() method for details. -** -** If the bClear argument is non-zero, then the auxiliary data is cleared -** (set to NULL) before this function returns. In this case the xDelete, -** if any, is not invoked. -** -** -** xRowCount(pFts5, pnRow) -** -** This function is used to retrieve the total number of rows in the table. -** In other words, the same value that would be returned by: -** -** SELECT count(*) FROM ftstable; -*/ -struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 1 */ - - void *(*xUserData)(Fts5Context*); - - int (*xColumnCount)(Fts5Context*); - int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); - int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken); - - int (*xTokenize)(Fts5Context*, - const char *pText, int nText, /* Text to tokenize */ - void *pCtx, /* Context passed to xToken() */ - int (*xToken)(void*, const char*, int, int, int) /* Callback */ - ); - - int (*xPhraseCount)(Fts5Context*); - int (*xPhraseSize)(Fts5Context*, int iPhrase); - - int (*xInstCount)(Fts5Context*, int *pnInst); - int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff); - - sqlite3_int64 (*xRowid)(Fts5Context*); - int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn); - int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken); - - int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData, - int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) - ); - int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); - void *(*xGetAuxdata)(Fts5Context*, int bClear); -}; - -/* -** CUSTOM AUXILIARY FUNCTIONS -*************************************************************************/ - -/************************************************************************* -** CUSTOM TOKENIZERS -** -** Applications may also register custom tokenizer types. A tokenizer -** is registered by providing fts5 with a populated instance of the -** following structure. The structure methods are expected to function -** as follows: -** -** xCreate: -** This function is used to allocate and inititalize a tokenizer instance. -** A tokenizer instance is required to actually tokenize text. -** -** The first argument passed to this function is a copy of the (void*) -** pointer provided by the application when the fts5_tokenizer object -** was registered with FTS5 (the third argument to xCreateTokenizer()). -** The second and third arguments are an array of nul-terminated strings -** containing the tokenizer arguments, if any, specified following the -** tokenizer name as part of the CREATE VIRTUAL TABLE statement used -** to create the FTS5 table. -** -** The final argument is an output variable. If successful, (*ppOut) -** should be set to point to the new tokenizer handle and SQLITE_OK -** returned. If an error occurs, some value other than SQLITE_OK should -** be returned. In this case, fts5 assumes that the final value of *ppOut -** is undefined. -** -** xDelete: -** This function is invoked to delete a tokenizer handle previously -** allocated using xCreate(). Fts5 guarantees that this function will -** be invoked exactly once for each successful call to xCreate(). -** -** xTokenize: -** This function is expected to tokenize the nText byte string indicated -** by argument pText. pText may not be nul-terminated. The first argument -** passed to this function is a pointer to an Fts5Tokenizer object returned -** by an earlier call to xCreate(). -** -** For each token in the input string, the supplied callback xToken() must -** be invoked. The first argument to it should be a copy of the pointer -** passed as the second argument to xTokenize(). The next two arguments -** are a pointer to a buffer containing the token text, and the size of -** the token in bytes. The 4th and 5th arguments are the byte offsets of -** the first byte of and first byte immediately following the text from -** which the token is derived within the input. -** -** FTS5 assumes the xToken() callback is invoked for each token in the -** order that they occur within the input text. -** -** If an xToken() callback returns any value other than SQLITE_OK, then -** the tokenization should be abandoned and the xTokenize() method should -** immediately return a copy of the xToken() return value. Or, if the -** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, -** if an error occurs with the xTokenize() implementation itself, it -** may abandon the tokenization and return any error code other than -** SQLITE_OK or SQLITE_DONE. -** -*/ -typedef struct Fts5Tokenizer Fts5Tokenizer; -typedef struct fts5_tokenizer fts5_tokenizer; -struct fts5_tokenizer { - int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut); - void (*xDelete)(Fts5Tokenizer*); - int (*xTokenize)(Fts5Tokenizer*, - void *pCtx, - const char *pText, int nText, - int (*xToken)( - void *pCtx, /* Copy of 2nd argument to xTokenize() */ - const char *pToken, /* Pointer to buffer containing token */ - int nToken, /* Size of token in bytes */ - int iStart, /* Byte offset of token within input text */ - int iEnd /* Byte offset of end of token within input text */ - ) - ); -}; - -/* -** END OF CUSTOM TOKENIZERS -*************************************************************************/ - -/************************************************************************* -** FTS5 EXTENSION REGISTRATION API -*/ -typedef struct fts5_api fts5_api; -struct fts5_api { - int iVersion; /* Currently always set to 1 */ - - /* Create a new tokenizer */ - int (*xCreateTokenizer)( - fts5_api *pApi, - const char *zName, - void *pContext, - fts5_tokenizer *pTokenizer, - void (*xDestroy)(void*) - ); - - /* Find an existing tokenizer */ - int (*xFindTokenizer)( - fts5_api *pApi, - const char *zName, - void **ppContext, - fts5_tokenizer *pTokenizer - ); - - /* Create a new auxiliary function */ - int (*xCreateFunction)( - fts5_api *pApi, - const char *zName, - void *pContext, - fts5_extension_function xFunction, - void (*xDestroy)(void*) - ); -}; - -/* -** END OF REGISTRATION API -*************************************************************************/ - -#endif /* _FTS5_H */ - DELETED ext/fts5/fts5Int.h Index: ext/fts5/fts5Int.h ================================================================== --- ext/fts5/fts5Int.h +++ /dev/null @@ -1,556 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -** -*/ -#ifndef _FTS5INT_H -#define _FTS5INT_H - -#include "fts5.h" -#include "sqliteInt.h" -#include "fts3_tokenizer.h" - - -/* -** Maximum number of prefix indexes on single FTS5 table. This must be -** less than 32. If it is set to anything large than that, an #error -** directive in fts5_index.c will cause the build to fail. -*/ -#define FTS5_MAX_PREFIX_INDEXES 31 - -#define FTS5_DEFAULT_NEARDIST 10 -#define FTS5_DEFAULT_RANK "bm25" - -/* Name of rank and rowid columns */ -#define FTS5_RANK_NAME "rank" -#define FTS5_ROWID_NAME "rowid" - -/************************************************************************** -** Interface to code in fts5.c. -*/ -typedef struct Fts5Global Fts5Global; - -int sqlite3Fts5GetTokenizer( - Fts5Global*, - const char **azArg, - int nArg, - Fts5Tokenizer**, - fts5_tokenizer** -); - -/* -** End of interface to code in fts5.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_config.c. fts5_config.c contains contains code -** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. -*/ - -typedef struct Fts5Config Fts5Config; - -/* -** An instance of the following structure encodes all information that can -** be gleaned from the CREATE VIRTUAL TABLE statement. -** -** And all information loaded from the %_config table. -** -** nAutomerge: -** The minimum number of segments that an auto-merge operation should -** attempt to merge together. A value of 1 sets the object to use the -** compile time default. Zero disables auto-merge altogether. -*/ -struct Fts5Config { - sqlite3 *db; /* Database handle */ - char *zDb; /* Database holding FTS index (e.g. "main") */ - char *zName; /* Name of FTS index */ - int nCol; /* Number of columns */ - char **azCol; /* Column names */ - int nPrefix; /* Number of prefix indexes */ - int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ - int eContent; /* An FTS5_CONTENT value */ - char *zContent; /* content table */ - char *zContentRowid; /* "content_rowid=" option value */ - Fts5Tokenizer *pTok; - fts5_tokenizer *pTokApi; - - /* Values loaded from the %_config table */ - int iCookie; /* Incremented when %_config is modified */ - int pgsz; /* Approximate page size used in %_data */ - int nAutomerge; /* 'automerge' setting */ - char *zRank; /* Name of rank function */ - char *zRankArgs; /* Arguments to rank function */ -}; - -#define FTS5_CONTENT_NORMAL 0 -#define FTS5_CONTENT_NONE 1 -#define FTS5_CONTENT_EXTERNAL 2 - - - -int sqlite3Fts5ConfigParse( - Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** -); -void sqlite3Fts5ConfigFree(Fts5Config*); - -int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig); - -int sqlite3Fts5Tokenize( - Fts5Config *pConfig, /* FTS5 Configuration object */ - const char *pText, int nText, /* Text to tokenize */ - void *pCtx, /* Context passed to xToken() */ - int (*xToken)(void*, const char*, int, int, int) /* Callback */ -); - -void sqlite3Fts5Dequote(char *z); - -/* Load the contents of the %_config table */ -int sqlite3Fts5ConfigLoad(Fts5Config*, int); - -/* Set the value of a single config attribute */ -int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*); - -int sqlite3Fts5ConfigParseRank(const char*, char**, char**); - -/* -** End of interface to code in fts5_config.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_buffer.c. -*/ - -/* -** Buffer object for the incremental building of string data. -*/ -typedef struct Fts5Buffer Fts5Buffer; -struct Fts5Buffer { - u8 *p; - int n; - int nSpace; -}; - -int sqlite3Fts5BufferGrow(int*, Fts5Buffer*, int); -void sqlite3Fts5BufferAppendVarint(int*, Fts5Buffer*, i64); -void sqlite3Fts5BufferAppendBlob(int*, Fts5Buffer*, int, const u8*); -void sqlite3Fts5BufferAppendString(int *, Fts5Buffer*, const char*); -void sqlite3Fts5BufferFree(Fts5Buffer*); -void sqlite3Fts5BufferZero(Fts5Buffer*); -void sqlite3Fts5BufferSet(int*, Fts5Buffer*, int, const u8*); -void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...); -void sqlite3Fts5BufferAppendListElem(int*, Fts5Buffer*, const char*, int); -void sqlite3Fts5BufferAppend32(int*, Fts5Buffer*, int); - -#define fts5BufferZero(x) sqlite3Fts5BufferZero(x) -#define fts5BufferGrow(a,b,c) sqlite3Fts5BufferGrow(a,b,c) -#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c) -#define fts5BufferFree(a) sqlite3Fts5BufferFree(a) -#define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d) -#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d) -#define fts5BufferAppend32(a,b,c) sqlite3Fts5BufferAppend32(a,b,c) - -/* Write and decode big-endian 32-bit integer values */ -void sqlite3Fts5Put32(u8*, int); -int sqlite3Fts5Get32(const u8*); - -#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32) -#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0xFFFFFFFF) - -typedef struct Fts5PoslistReader Fts5PoslistReader; -struct Fts5PoslistReader { - /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */ - int iCol; /* If (iCol>=0), this column only */ - const u8 *a; /* Position list to iterate through */ - int n; /* Size of buffer at a[] in bytes */ - int i; /* Current offset in a[] */ - - /* Output variables */ - int bEof; /* Set to true at EOF */ - i64 iPos; /* (iCol<<32) + iPos */ -}; -int sqlite3Fts5PoslistReaderInit( - int iCol, /* If (iCol>=0), this column only */ - const u8 *a, int n, /* Poslist buffer to iterate through */ - Fts5PoslistReader *pIter /* Iterator object to initialize */ -); -int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*); - -typedef struct Fts5PoslistWriter Fts5PoslistWriter; -struct Fts5PoslistWriter { - i64 iPrev; -}; -int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64); - -int sqlite3Fts5PoslistNext( - const u8 *a, int n, /* Buffer containing poslist */ - int *pi, /* IN/OUT: Offset within a[] */ - int *piCol, /* IN/OUT: Current column */ - int *piOff /* IN/OUT: Current token offset */ -); - -int sqlite3Fts5PoslistNext64( - const u8 *a, int n, /* Buffer containing poslist */ - int *pi, /* IN/OUT: Offset within a[] */ - i64 *piOff /* IN/OUT: Current offset */ -); - -/* Malloc utility */ -void *sqlite3Fts5MallocZero(int *pRc, int nByte); - -/* -** End of interface to code in fts5_buffer.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_index.c. fts5_index.c contains contains code -** to access the data stored in the %_data table. -*/ - -typedef struct Fts5Index Fts5Index; -typedef struct Fts5IndexIter Fts5IndexIter; - -/* -** Values used as part of the flags argument passed to IndexQuery(). -*/ -#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ -#define FTS5INDEX_QUERY_ASC 0x0002 /* Docs in ascending rowid order */ - -/* -** Create/destroy an Fts5Index object. -*/ -int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**); -int sqlite3Fts5IndexClose(Fts5Index *p, int bDestroy); - -/* -** for( -** pIter = sqlite3Fts5IndexQuery(p, "token", 5, 0); -** 0==sqlite3Fts5IterEof(pIter); -** sqlite3Fts5IterNext(pIter) -** ){ -** i64 iDocid = sqlite3Fts5IndexDocid(pIter); -** } -*/ - -/* -** Open a new iterator to iterate though all docids that match the -** specified token or token prefix. -*/ -int sqlite3Fts5IndexQuery( - Fts5Index *p, /* FTS index to query */ - const char *pToken, int nToken, /* Token (or prefix) to query for */ - int flags, /* Mask of FTS5INDEX_QUERY_X flags */ - Fts5IndexIter **ppIter -); - -/* -** Docid list iteration. -*/ -int sqlite3Fts5IterEof(Fts5IndexIter*); -int sqlite3Fts5IterNext(Fts5IndexIter*); -int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch); -i64 sqlite3Fts5IterRowid(Fts5IndexIter*); - -/* -** Obtain the position list that corresponds to the current position. -*/ -int sqlite3Fts5IterPoslist(Fts5IndexIter*, const u8 **pp, int *pn); - -/* -** Close an iterator opened by sqlite3Fts5IndexQuery(). -*/ -void sqlite3Fts5IterClose(Fts5IndexIter*); - -/* -** Insert or remove data to or from the index. Each time a document is -** added to or removed from the index, this function is called one or more -** times. -** -** For an insert, it must be called once for each token in the new document. -** If the operation is a delete, it must be called (at least) once for each -** unique token in the document with an iCol value less than zero. The iPos -** argument is ignored for a delete. -*/ -int sqlite3Fts5IndexWrite( - Fts5Index *p, /* Index to write to */ - int iCol, /* Column token appears in (-ve -> delete) */ - int iPos, /* Position of token within column */ - const char *pToken, int nToken /* Token to add or remove to or from index */ -); - -/* -** Indicate that subsequent calls to sqlite3Fts5IndexWrite() pertain to -** document iDocid. -*/ -int sqlite3Fts5IndexBeginWrite( - Fts5Index *p, /* Index to write to */ - i64 iDocid /* Docid to add or remove data from */ -); - -/* -** Flush any data stored in the in-memory hash tables to the database. -** If the bCommit flag is true, also close any open blob handles. -*/ -int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit); - -/* -** Discard any data stored in the in-memory hash tables. Do not write it -** to the database. Additionally, assume that the contents of the %_data -** table may have changed on disk. So any in-memory caches of %_data -** records must be invalidated. -*/ -int sqlite3Fts5IndexRollback(Fts5Index *p); - -/* -** Retrieve and clear the current error code, respectively. -*/ -int sqlite3Fts5IndexErrcode(Fts5Index*); -void sqlite3Fts5IndexReset(Fts5Index*); - -/* -** Get or set the "averages" record. -*/ -int sqlite3Fts5IndexGetAverages(Fts5Index *p, Fts5Buffer *pBuf); -int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); - -/* -** Functions called by the storage module as part of integrity-check. -*/ -u64 sqlite3Fts5IndexCksum(Fts5Config*,i64,int,int,const char*,int); -int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum); - -/* -** Called during virtual module initialization to register UDF -** fts5_decode() with SQLite -*/ -int sqlite3Fts5IndexInit(sqlite3*); - -int sqlite3Fts5IndexSetCookie(Fts5Index*, int); - -/* -** Return the total number of entries read from the %_data table by -** this connection since it was created. -*/ -int sqlite3Fts5IndexReads(Fts5Index *p); - -int sqlite3Fts5IndexReinit(Fts5Index *p); -int sqlite3Fts5IndexOptimize(Fts5Index *p); - -/* -** End of interface to code in fts5_index.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_hash.c. -*/ -typedef struct Fts5Hash Fts5Hash; - -/* -** Create a hash table, free a hash table. -*/ -int sqlite3Fts5HashNew(Fts5Hash**, int *pnSize); -void sqlite3Fts5HashFree(Fts5Hash*); - -int sqlite3Fts5HashWrite( - Fts5Hash*, - i64 iRowid, /* Rowid for this entry */ - int iCol, /* Column token appears in (-ve -> delete) */ - int iPos, /* Position of token within column */ - const char *pToken, int nToken /* Token to add or remove to or from index */ -); - -/* -** Empty (but do not delete) a hash table. -*/ -void sqlite3Fts5HashClear(Fts5Hash*); - -/* -** Iterate through the contents of the hash table. -*/ -int sqlite3Fts5HashIterate( - Fts5Hash*, - void *pCtx, - int (*xTerm)(void*, const char*, int), - int (*xEntry)(void*, i64, const u8*, int), - int (*xTermDone)(void*) -); - - - -/* -** End of interface to code in fts5_hash.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_storage.c. fts5_storage.c contains contains -** code to access the data stored in the %_content and %_docsize tables. -*/ - -#define FTS5_STMT_SCAN_ASC 0 /* SELECT rowid, * FROM ... ORDER BY 1 ASC */ -#define FTS5_STMT_SCAN_DESC 1 /* SELECT rowid, * FROM ... ORDER BY 1 DESC */ -#define FTS5_STMT_LOOKUP 2 /* SELECT rowid, * FROM ... WHERE rowid=? */ - -typedef struct Fts5Storage Fts5Storage; - -int sqlite3Fts5StorageOpen(Fts5Config*, Fts5Index*, int, Fts5Storage**, char**); -int sqlite3Fts5StorageClose(Fts5Storage *p, int bDestroy); - -int sqlite3Fts5DropTable(Fts5Config*, const char *zPost); -int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **); - -int sqlite3Fts5StorageDelete(Fts5Storage *p, i64); -int sqlite3Fts5StorageInsert(Fts5Storage *p, sqlite3_value **apVal, int, i64*); - -int sqlite3Fts5StorageIntegrity(Fts5Storage *p); - -int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**); -void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*); - -int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol); -int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnAvg); -int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow); - -int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit); -int sqlite3Fts5StorageRollback(Fts5Storage *p); - -int sqlite3Fts5StorageConfigValue(Fts5Storage *p, const char*, sqlite3_value*); - -int sqlite3Fts5StorageSpecialDelete(Fts5Storage *p, i64 iDel, sqlite3_value**); - -int sqlite3Fts5StorageDeleteAll(Fts5Storage *p); -int sqlite3Fts5StorageRebuild(Fts5Storage *p); -int sqlite3Fts5StorageOptimize(Fts5Storage *p); - -/* -** End of interface to code in fts5_storage.c. -**************************************************************************/ - - -/************************************************************************** -** Interface to code in fts5_expr.c. -*/ -typedef struct Fts5Expr Fts5Expr; -typedef struct Fts5ExprNode Fts5ExprNode; -typedef struct Fts5Parse Fts5Parse; -typedef struct Fts5Token Fts5Token; -typedef struct Fts5ExprPhrase Fts5ExprPhrase; -typedef struct Fts5ExprNearset Fts5ExprNearset; - -struct Fts5Token { - const char *p; /* Token text (not NULL terminated) */ - int n; /* Size of buffer p in bytes */ -}; - -/* Parse a MATCH expression. */ -int sqlite3Fts5ExprNew( - Fts5Config *pConfig, - const char *zExpr, - Fts5Expr **ppNew, - char **pzErr -); - -/* -** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bAsc); -** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr); -** rc = sqlite3Fts5ExprNext(pExpr) -** ){ -** // The document with rowid iRowid matches the expression! -** i64 iRowid = sqlite3Fts5ExprRowid(pExpr); -** } -*/ -int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, int bAsc); -int sqlite3Fts5ExprNext(Fts5Expr*); -int sqlite3Fts5ExprEof(Fts5Expr*); -i64 sqlite3Fts5ExprRowid(Fts5Expr*); - -void sqlite3Fts5ExprFree(Fts5Expr*); - -/* Called during startup to register a UDF with SQLite */ -int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*); - -int sqlite3Fts5ExprPhraseCount(Fts5Expr*); -int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase); -int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **); - -int sqlite3Fts5ExprPhraseExpr(Fts5Config*, Fts5Expr*, int, Fts5Expr**); - -/******************************************* -** The fts5_expr.c API above this point is used by the other hand-written -** C code in this module. The interfaces below this point are called by -** the parser code in fts5parse.y. */ - -void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...); - -Fts5ExprNode *sqlite3Fts5ParseNode( - Fts5Parse *pParse, - int eType, - Fts5ExprNode *pLeft, - Fts5ExprNode *pRight, - Fts5ExprNearset *pNear -); - -Fts5ExprPhrase *sqlite3Fts5ParseTerm( - Fts5Parse *pParse, - Fts5ExprPhrase *pPhrase, - Fts5Token *pToken, - int bPrefix -); - -Fts5ExprNearset *sqlite3Fts5ParseNearset( - Fts5Parse*, - Fts5ExprNearset*, - Fts5ExprPhrase* -); - -void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*); -void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); -void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); - -void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); -void sqlite3Fts5ParseSetColumn(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); -void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); -void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); - -/* -** End of interface to code in fts5_expr.c. -**************************************************************************/ - - - -/************************************************************************** -** Interface to code in fts5_aux.c. -*/ - -int sqlite3Fts5AuxInit(fts5_api*); -/* -** End of interface to code in fts5_aux.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_tokenizer.c. -*/ - -int sqlite3Fts5TokenizerInit(fts5_api*); -/* -** End of interface to code in fts5_tokenizer.c. -**************************************************************************/ - -/************************************************************************** -** Interface to code in fts5_sorter.c. -*/ -typedef struct Fts5Sorter Fts5Sorter; - -int sqlite3Fts5SorterNew(Fts5Expr *pExpr, Fts5Sorter **pp); - -/* -** End of interface to code in fts5_sorter.c. -**************************************************************************/ - -#endif DELETED ext/fts5/fts5_aux.c Index: ext/fts5/fts5_aux.c ================================================================== --- ext/fts5/fts5_aux.c +++ /dev/null @@ -1,553 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -*/ - -#include "fts5Int.h" -#include - -/* -** Object used to iterate through all "coalesced phrase instances" in -** a single column of the current row. If the phrase instances in the -** column being considered do not overlap, this object simply iterates -** through them. Or, if they do overlap (share one or more tokens in -** common), each set of overlapping instances is treated as a single -** match. See documentation for the highlight() auxiliary function for -** details. -** -** Usage is: -** -** for(rc = fts5CInstIterNext(pApi, pFts, iCol, &iter); -** (rc==SQLITE_OK && 0==fts5CInstIterEof(&iter); -** rc = fts5CInstIterNext(&iter) -** ){ -** printf("instance starts at %d, ends at %d\n", iter.iStart, iter.iEnd); -** } -** -*/ -typedef struct CInstIter CInstIter; -struct CInstIter { - const Fts5ExtensionApi *pApi; /* API offered by current FTS version */ - Fts5Context *pFts; /* First arg to pass to pApi functions */ - int iCol; /* Column to search */ - int iInst; /* Next phrase instance index */ - int nInst; /* Total number of phrase instances */ - - /* Output variables */ - int iStart; /* First token in coalesced phrase instance */ - int iEnd; /* Last token in coalesced phrase instance */ -}; - -/* -** Advance the iterator to the next coalesced phrase instance. Return -** an SQLite error code if an error occurs, or SQLITE_OK otherwise. -*/ -static int fts5CInstIterNext(CInstIter *pIter){ - int rc = SQLITE_OK; - pIter->iStart = -1; - pIter->iEnd = -1; - - while( rc==SQLITE_OK && pIter->iInstnInst ){ - int ip; int ic; int io; - rc = pIter->pApi->xInst(pIter->pFts, pIter->iInst, &ip, &ic, &io); - if( rc==SQLITE_OK ){ - if( ic==pIter->iCol ){ - int iEnd = io - 1 + pIter->pApi->xPhraseSize(pIter->pFts, ip); - if( pIter->iStart<0 ){ - pIter->iStart = io; - pIter->iEnd = iEnd; - }else if( io<=pIter->iEnd ){ - if( iEnd>pIter->iEnd ) pIter->iEnd = iEnd; - }else{ - break; - } - } - pIter->iInst++; - } - } - - return rc; -} - -/* -** Initialize the iterator object indicated by the final parameter to -** iterate through coalesced phrase instances in column iCol. -*/ -static int fts5CInstIterInit( - const Fts5ExtensionApi *pApi, - Fts5Context *pFts, - int iCol, - CInstIter *pIter -){ - int rc; - - memset(pIter, 0, sizeof(CInstIter)); - pIter->pApi = pApi; - pIter->pFts = pFts; - pIter->iCol = iCol; - rc = pApi->xInstCount(pFts, &pIter->nInst); - - if( rc==SQLITE_OK ){ - rc = fts5CInstIterNext(pIter); - } - - return rc; -} - - - -/************************************************************************* -** Start of highlight() implementation. -*/ -typedef struct HighlightContext HighlightContext; -struct HighlightContext { - CInstIter iter; /* Coalesced Instance Iterator */ - int iPos; /* Current token offset in zIn[] */ - int iRangeStart; /* First token to include */ - int iRangeEnd; /* If non-zero, last token to include */ - const char *zOpen; /* Opening highlight */ - const char *zClose; /* Closing highlight */ - const char *zIn; /* Input text */ - int nIn; /* Size of input text in bytes */ - int iOff; /* Current offset within zIn[] */ - char *zOut; /* Output value */ -}; - -/* -** Append text to the HighlightContext output string - p->zOut. Argument -** z points to a buffer containing n bytes of text to append. If n is -** negative, everything up until the first '\0' is appended to the output. -** -** If *pRc is set to any value other than SQLITE_OK when this function is -** called, it is a no-op. If an error (i.e. an OOM condition) is encountered, -** *pRc is set to an error code before returning. -*/ -static void fts5HighlightAppend( - int *pRc, - HighlightContext *p, - const char *z, int n -){ - if( *pRc==SQLITE_OK ){ - if( n<0 ) n = strlen(z); - p->zOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z); - if( p->zOut==0 ) *pRc = SQLITE_NOMEM; - } -} - -/* -** Tokenizer callback used by implementation of highlight() function. -*/ -static int fts5HighlightCb( - void *pContext, /* Pointer to HighlightContext object */ - const char *pToken, /* Buffer containing token */ - int nToken, /* Size of token in bytes */ - int iStartOff, /* Start offset of token */ - int iEndOff /* End offset of token */ -){ - HighlightContext *p = (HighlightContext*)pContext; - int rc = SQLITE_OK; - int iPos = p->iPos++; - - if( p->iRangeEnd>0 ){ - if( iPosiRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK; - if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff; - } - - if( iPos==p->iter.iStart ){ - fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff); - fts5HighlightAppend(&rc, p, p->zOpen, -1); - p->iOff = iStartOff; - } - - if( iPos==p->iter.iEnd ){ - if( p->iRangeEnd && p->iter.iStartiRangeStart ){ - fts5HighlightAppend(&rc, p, p->zOpen, -1); - } - fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); - fts5HighlightAppend(&rc, p, p->zClose, -1); - p->iOff = iEndOff; - if( rc==SQLITE_OK ){ - rc = fts5CInstIterNext(&p->iter); - } - } - - if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){ - fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); - p->iOff = iEndOff; - if( iPositer.iEnd ){ - fts5HighlightAppend(&rc, p, p->zClose, -1); - } - } - - return rc; -} - -/* -** Implementation of highlight() function. -*/ -static void fts5HighlightFunction( - const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ - Fts5Context *pFts, /* First arg to pass to pApi functions */ - sqlite3_context *pCtx, /* Context for returning result/error */ - int nVal, /* Number of values in apVal[] array */ - sqlite3_value **apVal /* Array of trailing arguments */ -){ - HighlightContext ctx; - int rc; - int iCol; - - if( nVal!=3 ){ - const char *zErr = "wrong number of arguments to function highlight()"; - sqlite3_result_error(pCtx, zErr, -1); - return; - } - - iCol = sqlite3_value_int(apVal[0]); - memset(&ctx, 0, sizeof(HighlightContext)); - ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); - ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); - rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); - - if( ctx.zIn ){ - if( rc==SQLITE_OK ){ - rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); - } - - if( rc==SQLITE_OK ){ - rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); - } - fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); - - if( rc==SQLITE_OK ){ - sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); - }else{ - sqlite3_result_error_code(pCtx, rc); - } - sqlite3_free(ctx.zOut); - } -} -/* -** End of highlight() implementation. -**************************************************************************/ - -/* -** Implementation of snippet() function. -*/ -static void fts5SnippetFunction( - const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ - Fts5Context *pFts, /* First arg to pass to pApi functions */ - sqlite3_context *pCtx, /* Context for returning result/error */ - int nVal, /* Number of values in apVal[] array */ - sqlite3_value **apVal /* Array of trailing arguments */ -){ - HighlightContext ctx; - int rc = SQLITE_OK; /* Return code */ - int iCol; /* 1st argument to snippet() */ - const char *zEllips; /* 4th argument to snippet() */ - int nToken; /* 5th argument to snippet() */ - int nInst; /* Number of instance matches this row */ - int i; /* Used to iterate through instances */ - int nPhrase; /* Number of phrases in query */ - unsigned char *aSeen; /* Array of "seen instance" flags */ - int iBestCol; /* Column containing best snippet */ - int iBestStart = 0; /* First token of best snippet */ - int iBestLast; /* Last token of best snippet */ - int nBestScore = 0; /* Score of best snippet */ - int nColSize; /* Total size of iBestCol in tokens */ - - if( nVal!=5 ){ - const char *zErr = "wrong number of arguments to function snippet()"; - sqlite3_result_error(pCtx, zErr, -1); - return; - } - - memset(&ctx, 0, sizeof(HighlightContext)); - iCol = sqlite3_value_int(apVal[0]); - ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]); - ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); - zEllips = (const char*)sqlite3_value_text(apVal[3]); - nToken = sqlite3_value_int(apVal[4]); - iBestLast = nToken-1; - - iBestCol = (iCol>=0 ? iCol : 0); - nPhrase = pApi->xPhraseCount(pFts); - aSeen = sqlite3_malloc(nPhrase); - if( aSeen==0 ){ - rc = SQLITE_NOMEM; - } - - if( rc==SQLITE_OK ){ - rc = pApi->xInstCount(pFts, &nInst); - } - for(i=0; rc==SQLITE_OK && ixInst(pFts, i, &ip, &iSnippetCol, &iStart); - if( rc==SQLITE_OK && (iCol<0 || iSnippetCol==iCol) ){ - int nScore = 1000; - int iLast = iStart - 1 + pApi->xPhraseSize(pFts, ip); - int j; - aSeen[ip] = 1; - - for(j=i+1; rc==SQLITE_OK && jxInst(pFts, j, &ip, &ic, &io); - iFinal = io + pApi->xPhraseSize(pFts, ip) - 1; - if( rc==SQLITE_OK && ic==iSnippetCol && iLastiLast ) iLast = iFinal; - } - } - - if( rc==SQLITE_OK && nScore>nBestScore ){ - iBestCol = iSnippetCol; - iBestStart = iStart; - iBestLast = iLast; - nBestScore = nScore; - } - } - } - - if( rc==SQLITE_OK ){ - rc = pApi->xColumnSize(pFts, iBestCol, &nColSize); - } - if( rc==SQLITE_OK ){ - rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn); - } - if( ctx.zIn ){ - if( rc==SQLITE_OK ){ - rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter); - } - - if( (iBestStart+nToken-1)>iBestLast ){ - iBestStart -= (iBestStart+nToken-1-iBestLast) / 2; - } - if( iBestStart+nToken>nColSize ){ - iBestStart = nColSize - nToken; - } - if( iBestStart<0 ) iBestStart = 0; - - ctx.iRangeStart = iBestStart; - ctx.iRangeEnd = iBestStart + nToken - 1; - - if( iBestStart>0 ){ - fts5HighlightAppend(&rc, &ctx, zEllips, -1); - } - if( rc==SQLITE_OK ){ - rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); - } - if( ctx.iRangeEnd>=(nColSize-1) ){ - fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); - }else{ - fts5HighlightAppend(&rc, &ctx, zEllips, -1); - } - - if( rc==SQLITE_OK ){ - sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT); - }else{ - sqlite3_result_error_code(pCtx, rc); - } - sqlite3_free(ctx.zOut); - } - sqlite3_free(aSeen); -} - -/************************************************************************/ - -/* -** The first time the bm25() function is called for a query, an instance -** of the following structure is allocated and populated. -*/ -typedef struct Fts5Bm25Data Fts5Bm25Data; -struct Fts5Bm25Data { - int nPhrase; /* Number of phrases in query */ - double avgdl; /* Average number of tokens in each row */ - double *aIDF; /* IDF for each phrase */ - double *aFreq; /* Array used to calculate phrase freq. */ -}; - -/* -** Callback used by fts5Bm25GetData() to count the number of rows in the -** table matched by each individual phrase within the query. -*/ -static int fts5CountCb( - const Fts5ExtensionApi *pApi, - Fts5Context *pFts, - void *pUserData /* Pointer to sqlite3_int64 variable */ -){ - sqlite3_int64 *pn = (sqlite3_int64*)pUserData; - (*pn)++; - return SQLITE_OK; -} - -/* -** Set *ppData to point to the Fts5Bm25Data object for the current query. -** If the object has not already been allocated, allocate and populate it -** now. -*/ -static int fts5Bm25GetData( - const Fts5ExtensionApi *pApi, - Fts5Context *pFts, - Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */ -){ - int rc = SQLITE_OK; /* Return code */ - Fts5Bm25Data *p; /* Object to return */ - - p = pApi->xGetAuxdata(pFts, 0); - if( p==0 ){ - int nPhrase; /* Number of phrases in query */ - sqlite3_int64 nRow; /* Number of rows in table */ - sqlite3_int64 nToken; /* Number of tokens in table */ - int nByte; /* Bytes of space to allocate */ - int i; - - /* Allocate the Fts5Bm25Data object */ - nPhrase = pApi->xPhraseCount(pFts); - nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double); - p = (Fts5Bm25Data*)sqlite3_malloc(nByte); - if( p==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(p, 0, nByte); - p->nPhrase = nPhrase; - p->aIDF = (double*)&p[1]; - p->aFreq = &p->aIDF[nPhrase]; - } - - /* Calculate the average document length for this FTS5 table */ - if( rc==SQLITE_OK ) rc = pApi->xRowCount(pFts, &nRow); - if( rc==SQLITE_OK ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken); - if( rc==SQLITE_OK ) p->avgdl = (double)nToken / (double)nRow; - - /* Calculate an IDF for each phrase in the query */ - for(i=0; rc==SQLITE_OK && ixQueryPhrase(pFts, i, (void*)&nHit, fts5CountCb); - if( rc==SQLITE_OK ){ - /* Calculate the IDF (Inverse Document Frequency) for phrase i. - ** This is done using the standard BM25 formula as found on wikipedia: - ** - ** IDF = log( (N - nHit + 0.5) / (nHit + 0.5) ) - ** - ** where "N" is the total number of documents in the set and nHit - ** is the number that contain at least one instance of the phrase - ** under consideration. - ** - ** The problem with this is that if (N < 2*nHit), the IDF is - ** negative. Which is undesirable. So the mimimum allowable IDF is - ** (1e-6) - roughly the same as a term that appears in just over - ** half of set of 5,000,000 documents. */ - double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); - if( idf<=0.0 ) idf = 1e-6; - p->aIDF[i] = idf; - } - } - - if( rc!=SQLITE_OK ){ - sqlite3_free(p); - }else{ - rc = pApi->xSetAuxdata(pFts, p, sqlite3_free); - } - if( rc!=SQLITE_OK ) p = 0; - } - *ppData = p; - return rc; -} - -/* -** Implementation of bm25() function. -*/ -static void fts5Bm25Function( - const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ - Fts5Context *pFts, /* First arg to pass to pApi functions */ - sqlite3_context *pCtx, /* Context for returning result/error */ - int nVal, /* Number of values in apVal[] array */ - sqlite3_value **apVal /* Array of trailing arguments */ -){ - const double k1 = 1.2; /* Constant "k1" from BM25 formula */ - const double b = 0.75; /* Constant "b" from BM25 formula */ - int rc = SQLITE_OK; /* Error code */ - double score = 0.0; /* SQL function return value */ - Fts5Bm25Data *pData; /* Values allocated/calculated once only */ - int i; /* Iterator variable */ - int nInst; /* Value returned by xInstCount() */ - double D; /* Total number of tokens in row */ - double *aFreq; /* Array of phrase freq. for current row */ - - /* Calculate the phrase frequency (symbol "f(qi,D)" in the documentation) - ** for each phrase in the query for the current row. */ - rc = fts5Bm25GetData(pApi, pFts, &pData); - if( rc==SQLITE_OK ){ - aFreq = pData->aFreq; - memset(aFreq, 0, sizeof(double) * pData->nPhrase); - rc = pApi->xInstCount(pFts, &nInst); - } - for(i=0; rc==SQLITE_OK && ixInst(pFts, i, &ip, &ic, &io); - if( rc==SQLITE_OK ){ - double w = (nVal > ic) ? sqlite3_value_double(apVal[ic]) : 1.0; - aFreq[ip] += w; - } - } - - /* Figure out the total size of the current row in tokens. */ - if( rc==SQLITE_OK ){ - int nTok; - rc = pApi->xColumnSize(pFts, -1, &nTok); - D = (double)nTok; - } - - /* Determine the BM25 score for the current row. */ - for(i=0; rc==SQLITE_OK && inPhrase; i++){ - score += pData->aIDF[i] * ( - ( aFreq[i] * (k1 + 1.0) ) / - ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) ) - ); - } - - /* If no error has occurred, return the calculated score. Otherwise, - ** throw an SQL exception. */ - if( rc==SQLITE_OK ){ - sqlite3_result_double(pCtx, score); - }else{ - sqlite3_result_error_code(pCtx, rc); - } -} - -int sqlite3Fts5AuxInit(fts5_api *pApi){ - struct Builtin { - const char *zFunc; /* Function name (nul-terminated) */ - void *pUserData; /* User-data pointer */ - fts5_extension_function xFunc;/* Callback function */ - void (*xDestroy)(void*); /* Destructor function */ - } aBuiltin [] = { - { "snippet", 0, fts5SnippetFunction, 0 }, - { "highlight", 0, fts5HighlightFunction, 0 }, - { "bm25", 0, fts5Bm25Function, 0 }, - }; - int rc = SQLITE_OK; /* Return code */ - int i; /* To iterate through builtin functions */ - - for(i=0; rc==SQLITE_OK && ixCreateFunction(pApi, - aBuiltin[i].zFunc, - aBuiltin[i].pUserData, - aBuiltin[i].xFunc, - aBuiltin[i].xDestroy - ); - } - - return rc; -} - - DELETED ext/fts5/fts5_buffer.c Index: ext/fts5/fts5_buffer.c ================================================================== --- ext/fts5/fts5_buffer.c +++ /dev/null @@ -1,298 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -*/ - - - -#include "fts5Int.h" - -int sqlite3Fts5BufferGrow(int *pRc, Fts5Buffer *pBuf, int nByte){ - /* A no-op if an error has already occurred */ - if( *pRc ) return 1; - - if( (pBuf->n + nByte) > pBuf->nSpace ){ - u8 *pNew; - int nNew = pBuf->nSpace ? pBuf->nSpace*2 : 64; - while( nNew<(pBuf->n + nByte) ){ - nNew = nNew * 2; - } - pNew = sqlite3_realloc(pBuf->p, nNew); - if( pNew==0 ){ - *pRc = SQLITE_NOMEM; - return 1; - }else{ - pBuf->nSpace = nNew; - pBuf->p = pNew; - } - } - return 0; -} - -/* -** Encode value iVal as an SQLite varint and append it to the buffer object -** pBuf. If an OOM error occurs, set the error code in p. -*/ -void sqlite3Fts5BufferAppendVarint(int *pRc, Fts5Buffer *pBuf, i64 iVal){ - if( sqlite3Fts5BufferGrow(pRc, pBuf, 9) ) return; - pBuf->n += sqlite3PutVarint(&pBuf->p[pBuf->n], iVal); -} - -void sqlite3Fts5Put32(u8 *aBuf, int iVal){ - aBuf[0] = (iVal>>24) & 0x00FF; - aBuf[1] = (iVal>>16) & 0x00FF; - aBuf[2] = (iVal>> 8) & 0x00FF; - aBuf[3] = (iVal>> 0) & 0x00FF; -} - -int sqlite3Fts5Get32(const u8 *aBuf){ - return (aBuf[0] << 24) + (aBuf[1] << 16) + (aBuf[2] << 8) + aBuf[3]; -} - -void sqlite3Fts5BufferAppend32(int *pRc, Fts5Buffer *pBuf, int iVal){ - if( sqlite3Fts5BufferGrow(pRc, pBuf, 4) ) return; - sqlite3Fts5Put32(&pBuf->p[pBuf->n], iVal); - pBuf->n += 4; -} - -/* -** Append buffer nData/pData to buffer pBuf. If an OOM error occurs, set -** the error code in p. If an error has already occurred when this function -** is called, it is a no-op. -*/ -void sqlite3Fts5BufferAppendBlob( - int *pRc, - Fts5Buffer *pBuf, - int nData, - const u8 *pData -){ - if( sqlite3Fts5BufferGrow(pRc, pBuf, nData) ) return; - memcpy(&pBuf->p[pBuf->n], pData, nData); - pBuf->n += nData; -} - -/* -** Append the nul-terminated string zStr to the buffer pBuf. This function -** ensures that the byte following the buffer data is set to 0x00, even -** though this byte is not included in the pBuf->n count. -*/ -void sqlite3Fts5BufferAppendString( - int *pRc, - Fts5Buffer *pBuf, - const char *zStr -){ - int nStr = strlen(zStr); - if( sqlite3Fts5BufferGrow(pRc, pBuf, nStr+1) ) return; - sqlite3Fts5BufferAppendBlob(pRc, pBuf, nStr, (const u8*)zStr); - if( *pRc==SQLITE_OK ) pBuf->p[pBuf->n] = 0x00; -} - -/* -** Argument zFmt is a printf() style format string. This function performs -** the printf() style processing, then appends the results to buffer pBuf. -** -** Like sqlite3Fts5BufferAppendString(), this function ensures that the byte -** following the buffer data is set to 0x00, even though this byte is not -** included in the pBuf->n count. -*/ -void sqlite3Fts5BufferAppendPrintf( - int *pRc, - Fts5Buffer *pBuf, - char *zFmt, ... -){ - if( *pRc==SQLITE_OK ){ - char *zTmp; - va_list ap; - va_start(ap, zFmt); - zTmp = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - - if( zTmp==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - sqlite3Fts5BufferAppendString(pRc, pBuf, zTmp); - sqlite3_free(zTmp); - } - } -} - -/* -** Free any buffer allocated by pBuf. Zero the structure before returning. -*/ -void sqlite3Fts5BufferFree(Fts5Buffer *pBuf){ - sqlite3_free(pBuf->p); - memset(pBuf, 0, sizeof(Fts5Buffer)); -} - -/* -** Zero the contents of the buffer object. But do not free the associated -** memory allocation. -*/ -void sqlite3Fts5BufferZero(Fts5Buffer *pBuf){ - pBuf->n = 0; -} - -/* -** Set the buffer to contain nData/pData. If an OOM error occurs, leave an -** the error code in p. If an error has already occurred when this function -** is called, it is a no-op. -*/ -void sqlite3Fts5BufferSet( - int *pRc, - Fts5Buffer *pBuf, - int nData, - const u8 *pData -){ - pBuf->n = 0; - sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData); -} - -int sqlite3Fts5PoslistNext64( - const u8 *a, int n, /* Buffer containing poslist */ - int *pi, /* IN/OUT: Offset within a[] */ - i64 *piOff /* IN/OUT: Current offset */ -){ - int i = *pi; - if( i>=n ){ - /* EOF */ - *piOff = -1; - return 1; - }else{ - i64 iOff = *piOff; - int iVal; - i += getVarint32(&a[i], iVal); - if( iVal==1 ){ - i += getVarint32(&a[i], iVal); - iOff = ((i64)iVal) << 32; - i += getVarint32(&a[i], iVal); - } - *piOff = iOff + (iVal-2); - *pi = i; - return 0; - } -} - - -/* -** Advance the iterator object passed as the only argument. Return true -** if the iterator reaches EOF, or false otherwise. -*/ -int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){ - if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) - || (pIter->iCol>=0 && (pIter->iPos >> 32) > pIter->iCol) - ){ - pIter->bEof = 1; - } - return pIter->bEof; -} - -int sqlite3Fts5PoslistReaderInit( - int iCol, /* If (iCol>=0), this column only */ - const u8 *a, int n, /* Poslist buffer to iterate through */ - Fts5PoslistReader *pIter /* Iterator object to initialize */ -){ - memset(pIter, 0, sizeof(*pIter)); - pIter->a = a; - pIter->n = n; - pIter->iCol = iCol; - do { - sqlite3Fts5PoslistReaderNext(pIter); - }while( pIter->bEof==0 && (pIter->iPos >> 32)bEof; -} - -int sqlite3Fts5PoslistWriterAppend( - Fts5Buffer *pBuf, - Fts5PoslistWriter *pWriter, - i64 iPos -){ - static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32; - int rc = SQLITE_OK; - if( (iPos & colmask) != (pWriter->iPrev & colmask) ){ - fts5BufferAppendVarint(&rc, pBuf, 1); - fts5BufferAppendVarint(&rc, pBuf, (iPos >> 32)); - pWriter->iPrev = (iPos & colmask); - } - fts5BufferAppendVarint(&rc, pBuf, (iPos - pWriter->iPrev) + 2); - pWriter->iPrev = iPos; - return rc; -} - -int sqlite3Fts5PoslistNext( - const u8 *a, int n, /* Buffer containing poslist */ - int *pi, /* IN/OUT: Offset within a[] */ - int *piCol, /* IN/OUT: Current column */ - int *piOff /* IN/OUT: Current token offset */ -){ - int i = *pi; - int iVal; - if( i>=n ){ - /* EOF */ - return 1; - } - i += getVarint32(&a[i], iVal); - if( iVal==1 ){ - i += getVarint32(&a[i], iVal); - *piCol = iVal; - *piOff = 0; - i += getVarint32(&a[i], iVal); - } - *piOff += (iVal-2); - *pi = i; - return 0; -} - -void sqlite3Fts5BufferAppendListElem( - int *pRc, /* IN/OUT: Error code */ - Fts5Buffer *pBuf, /* Buffer to append to */ - const char *z, int n /* Value to append to buffer */ -){ - int bParen = (n==0); - int nMax = n*2 + 2 + 1; - u8 *pOut; - int i; - - /* Ensure the buffer has space for the new list element */ - if( sqlite3Fts5BufferGrow(pRc, pBuf, nMax) ) return; - pOut = &pBuf->p[pBuf->n]; - - /* Figure out if we need the enclosing {} */ - for(i=0; in = pOut - pBuf->p; - *pOut = '\0'; -} - -void *sqlite3Fts5MallocZero(int *pRc, int nByte){ - void *pRet = 0; - if( *pRc==SQLITE_OK ){ - pRet = sqlite3_malloc(nByte); - if( pRet==0 && nByte>0 ){ - *pRc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, nByte); - } - } - return pRet; -} - - - DELETED ext/fts5/fts5_config.c Index: ext/fts5/fts5_config.c ================================================================== --- ext/fts5/fts5_config.c +++ /dev/null @@ -1,776 +0,0 @@ -/* -** 2014 Jun 09 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This is an SQLite module implementing full-text search. -*/ - -#include "fts5Int.h" - -#define FTS5_DEFAULT_PAGE_SIZE 1000 -#define FTS5_DEFAULT_AUTOMERGE 4 - -/* Maximum allowed page size */ -#define FTS5_MAX_PAGE_SIZE (128*1024) - -static int fts5_iswhitespace(char x){ - return (x==' '); -} - -static int fts5_isopenquote(char x){ - return (x=='"' || x=='\'' || x=='[' || x=='`'); -} - -/* -** Argument pIn points to a character that is part of a nul-terminated -** string. Return a pointer to the first character following *pIn in -** the string that is not a white-space character. -*/ -static const char *fts5ConfigSkipWhitespace(const char *pIn){ - const char *p = pIn; - if( p ){ - while( fts5_iswhitespace(*p) ){ p++; } - } - return p; -} - -/* -** Argument pIn points to a character that is part of a nul-terminated -** string. Return a pointer to the first character following *pIn in -** the string that is not a "bareword" character. -*/ -static const char *fts5ConfigSkipBareword(const char *pIn){ - const char *p = pIn; - while( *p && *p!=' ' && *p!=':' && *p!='!' && *p!='@' - && *p!='#' && *p!='$' && *p!='%' && *p!='^' && *p!='&' - && *p!='*' && *p!='(' && *p!=')' && *p!='=' - ){ - p++; - } - if( p==pIn ) p = 0; - return p; -} - -static int fts5_isdigit(char a){ - return (a>='0' && a<='9'); -} - - - -static const char *fts5ConfigSkipLiteral(const char *pIn){ - const char *p = pIn; - if( p ){ - switch( *p ){ - case 'n': case 'N': - if( sqlite3_strnicmp("null", p, 4)==0 ){ - p = &p[4]; - }else{ - p = 0; - } - break; - - case 'x': case 'X': - p++; - if( *p=='\'' ){ - p++; - while( (*p>='a' && *p<='f') - || (*p>='A' && *p<='F') - || (*p>='0' && *p<='9') - ){ - p++; - } - if( *p=='\'' && 0==((p-pIn)%2) ){ - p++; - }else{ - p = 0; - } - }else{ - p = 0; - } - break; - - case '\'': - p++; - while( p ){ - if( *p=='\'' ){ - p++; - if( *p!='\'' ) break; - } - p++; - if( *p==0 ) p = 0; - } - break; - - default: - /* maybe a number */ - if( *p=='+' || *p=='-' ) p++; - while( fts5_isdigit(*p) ) p++; - - /* At this point, if the literal was an integer, the parse is - ** finished. Or, if it is a floating point value, it may continue - ** with either a decimal point or an 'E' character. */ - if( *p=='.' && fts5_isdigit(p[1]) ){ - p += 2; - while( fts5_isdigit(*p) ) p++; - } - if( p==pIn ) p = 0; - - break; - } - } - - return p; -} - -static int fts5Dequote(char *z){ - char q; - int iIn = 1; - int iOut = 0; - int bRet = 1; - q = z[0]; - - assert( q=='[' || q=='\'' || q=='"' || q=='`' ); - if( q=='[' ) q = ']'; - - while( z[iIn] ){ - if( z[iIn]==q ){ - if( z[iIn+1]!=q ){ - if( z[iIn+1]=='\0' ) bRet = 0; - break; - } - z[iOut++] = q; - iIn += 2; - }else{ - z[iOut++] = z[iIn++]; - } - } - z[iOut] = '\0'; - - return bRet; -} - -/* -** Convert an SQL-style quoted string into a normal string by removing -** the quote characters. The conversion is done in-place. If the -** input does not begin with a quote character, then this routine -** is a no-op. -** -** Examples: -** -** "abc" becomes abc -** 'xyz' becomes xyz -** [pqr] becomes pqr -** `mno` becomes mno -*/ -void sqlite3Fts5Dequote(char *z){ - char quote; /* Quote character (if any ) */ - - assert( 0==fts5_iswhitespace(z[0]) ); - quote = z[0]; - if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ - fts5Dequote(z); - } -} - -/* -** Trim any white-space from the right of nul-terminated string z. -*/ -static char *fts5TrimString(char *z){ - int n = strlen(z); - while( n>0 && fts5_iswhitespace(z[n-1]) ){ - z[--n] = '\0'; - } - while( fts5_iswhitespace(*z) ) z++; - return z; -} - -/* -** Duplicate the string passed as the only argument into a buffer allocated -** by sqlite3_malloc(). -** -** Return 0 if an OOM error is encountered. -*/ -static char *fts5Strdup(int *pRc, const char *z){ - char *pRet = 0; - if( *pRc==SQLITE_OK ){ - pRet = sqlite3_mprintf("%s", z); - if( pRet==0 ) *pRc = SQLITE_NOMEM; - } - return pRet; -} - -/* -** Argument z points to a nul-terminated string containing an SQL identifier. -** This function returns a copy of the identifier enclosed in backtick -** quotes. -*/ -static char *fts5EscapeName(int *pRc, const char *z){ - char *pRet = 0; - if( *pRc==SQLITE_OK ){ - int n = strlen(z); - pRet = (char*)sqlite3_malloc(2 + 2*n + 1); - if( pRet==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - int i; - char *p = pRet; - *p++ = '`'; - for(i=0; iaPrefix ){ - *pzErr = sqlite3_mprintf("multiple prefix=... directives"); - rc = SQLITE_ERROR; - }else{ - pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte); - } - p = zArg; - while( rc==SQLITE_OK && p[0] ){ - int nPre = 0; - while( p[0]==' ' ) p++; - while( p[0]>='0' && p[0]<='9' && nPre<1000 ){ - nPre = nPre*10 + (p[0] - '0'); - p++; - } - while( p[0]==' ' ) p++; - if( p[0]==',' ){ - p++; - }else if( p[0] ){ - *pzErr = sqlite3_mprintf("malformed prefix=... directive"); - rc = SQLITE_ERROR; - } - if( rc==SQLITE_OK && (nPre==0 || nPre>=1000) ){ - *pzErr = sqlite3_mprintf("prefix length out of range: %d", nPre); - rc = SQLITE_ERROR; - } - pConfig->aPrefix[pConfig->nPrefix] = nPre; - pConfig->nPrefix++; - } - return rc; - } - - if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){ - int rc = SQLITE_OK; - const char *p = (const char*)zArg; - int nArg = strlen(zArg) + 1; - char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); - char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2); - char *pSpace = pDel; - - if( azArg && pSpace ){ - if( pConfig->pTok ){ - *pzErr = sqlite3_mprintf("multiple tokenize=... directives"); - rc = SQLITE_ERROR; - }else{ - for(nArg=0; p && *p; nArg++){ - const char *p2 = fts5ConfigSkipWhitespace(p); - if( p2 && *p2=='\'' ){ - p = fts5ConfigSkipLiteral(p2); - }else{ - p = fts5ConfigSkipBareword(p2); - } - if( p ){ - memcpy(pSpace, p2, p-p2); - azArg[nArg] = pSpace; - sqlite3Fts5Dequote(pSpace); - pSpace += (p - p2) + 1; - p = fts5ConfigSkipWhitespace(p); - } - } - if( p==0 ){ - *pzErr = sqlite3_mprintf("parse error in tokenize directive"); - rc = SQLITE_ERROR; - }else{ - rc = sqlite3Fts5GetTokenizer(pGlobal, - (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi - ); - if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("error in tokenizer constructor"); - } - } - } - } - - sqlite3_free(azArg); - sqlite3_free(pDel); - return rc; - } - - if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){ - int rc = SQLITE_OK; - if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ - *pzErr = sqlite3_mprintf("multiple content=... directives"); - rc = SQLITE_ERROR; - }else{ - if( zArg[0] ){ - pConfig->eContent = FTS5_CONTENT_EXTERNAL; - pConfig->zContent = sqlite3_mprintf("%Q.%Q", pConfig->zDb, zArg); - }else{ - pConfig->eContent = FTS5_CONTENT_NONE; - pConfig->zContent = sqlite3_mprintf( - "%Q.'%q_docsize'", pConfig->zDb, pConfig->zName - ); - } - if( pConfig->zContent==0 ) rc = SQLITE_NOMEM; - } - return rc; - } - - if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){ - int rc = SQLITE_OK; - if( pConfig->zContentRowid ){ - *pzErr = sqlite3_mprintf("multiple content_rowid=... directives"); - rc = SQLITE_ERROR; - }else{ - pConfig->zContentRowid = fts5EscapeName(&rc, zArg); - } - return rc; - } - - *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); - return SQLITE_ERROR; -} - -/* -** Allocate an instance of the default tokenizer ("simple") at -** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error -** code if an error occurs. -*/ -static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){ - assert( pConfig->pTok==0 && pConfig->pTokApi==0 ); - return sqlite3Fts5GetTokenizer( - pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi - ); -} - -/* -** Arguments nArg/azArg contain the string arguments passed to the xCreate -** or xConnect method of the virtual table. This function attempts to -** allocate an instance of Fts5Config containing the results of parsing -** those arguments. -** -** If successful, SQLITE_OK is returned and *ppOut is set to point to the -** new Fts5Config object. If an error occurs, an SQLite error code is -** returned, *ppOut is set to NULL and an error message may be left in -** *pzErr. It is the responsibility of the caller to eventually free any -** such error message using sqlite3_free(). -*/ -int sqlite3Fts5ConfigParse( - Fts5Global *pGlobal, - sqlite3 *db, - int nArg, /* Number of arguments */ - const char **azArg, /* Array of nArg CREATE VIRTUAL TABLE args */ - Fts5Config **ppOut, /* OUT: Results of parse */ - char **pzErr /* OUT: Error message */ -){ - int rc = SQLITE_OK; /* Return code */ - Fts5Config *pRet; /* New object to return */ - int i; - - *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); - if( pRet==0 ) return SQLITE_NOMEM; - memset(pRet, 0, sizeof(Fts5Config)); - pRet->db = db; - pRet->iCookie = -1; - - pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); - pRet->zDb = fts5Strdup(&rc, azArg[1]); - pRet->zName = fts5Strdup(&rc, azArg[2]); - if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ - *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); - rc = SQLITE_ERROR; - } - - for(i=3; rc==SQLITE_OK && iazCol[pRet->nCol++] = zCol; - zDup = 0; - } - } - - sqlite3_free(zDup); - } - } - - /* If a tokenizer= option was successfully parsed, the tokenizer has - ** already been allocated. Otherwise, allocate an instance of the default - ** tokenizer (simple) now. */ - if( rc==SQLITE_OK && pRet->pTok==0 ){ - rc = fts5ConfigDefaultTokenizer(pGlobal, pRet); - } - - /* If no zContent option was specified, fill in the default values. */ - if( rc==SQLITE_OK && pRet->eContent==FTS5_CONTENT_NORMAL ){ - pRet->zContent = sqlite3_mprintf("%Q.'%q_content'", pRet->zDb, pRet->zName); - if( pRet->zContent==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_free(pRet->zContentRowid); - pRet->zContentRowid = 0; - } - } - if( rc==SQLITE_OK && pRet->zContentRowid==0 ){ - pRet->zContentRowid = fts5Strdup(&rc, "rowid"); - } - - if( rc!=SQLITE_OK ){ - sqlite3Fts5ConfigFree(pRet); - *ppOut = 0; - } - return rc; -} - -/* -** Free the configuration object passed as the only argument. -*/ -void sqlite3Fts5ConfigFree(Fts5Config *pConfig){ - if( pConfig ){ - int i; - if( pConfig->pTok && pConfig->pTokApi->xDelete ){ - pConfig->pTokApi->xDelete(pConfig->pTok); - } - sqlite3_free(pConfig->zDb); - sqlite3_free(pConfig->zName); - for(i=0; inCol; i++){ - sqlite3_free(pConfig->azCol[i]); - } - sqlite3_free(pConfig->azCol); - sqlite3_free(pConfig->aPrefix); - sqlite3_free(pConfig->zRank); - sqlite3_free(pConfig->zRankArgs); - sqlite3_free(pConfig->zContent); - sqlite3_free(pConfig->zContentRowid); - sqlite3_free(pConfig); - } -} - -/* -** Call sqlite3_declare_vtab() based on the contents of the configuration -** object passed as the only argument. Return SQLITE_OK if successful, or -** an SQLite error code if an error occurs. -*/ -int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){ - int i; - int rc; - char *zSql; - char *zOld; - - zSql = (char*)sqlite3_mprintf("CREATE TABLE x("); - for(i=0; zSql && inCol; i++){ - zOld = zSql; - zSql = sqlite3_mprintf("%s%s%Q", zOld, (i==0?"":", "), pConfig->azCol[i]); - sqlite3_free(zOld); - } - - if( zSql ){ - zOld = zSql; - zSql = sqlite3_mprintf("%s, %Q HIDDEN, %s HIDDEN)", - zOld, pConfig->zName, FTS5_RANK_NAME - ); - sqlite3_free(zOld); - } - - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_declare_vtab(pConfig->db, zSql); - sqlite3_free(zSql); - } - - return rc; -} - -/* -** Tokenize the text passed via the second and third arguments. -** -** The callback is invoked once for each token in the input text. The -** arguments passed to it are, in order: -** -** void *pCtx // Copy of 4th argument to sqlite3Fts5Tokenize() -** const char *pToken // Pointer to buffer containing token -** int nToken // Size of token in bytes -** int iStart // Byte offset of start of token within input text -** int iEnd // Byte offset of end of token within input text -** int iPos // Position of token in input (first token is 0) -** -** If the callback returns a non-zero value the tokenization is abandoned -** and no further callbacks are issued. -** -** This function returns SQLITE_OK if successful or an SQLite error code -** if an error occurs. If the tokenization was abandoned early because -** the callback returned SQLITE_DONE, this is not an error and this function -** still returns SQLITE_OK. Or, if the tokenization was abandoned early -** because the callback returned another non-zero value, it is assumed -** to be an SQLite error code and returned to the caller. -*/ -int sqlite3Fts5Tokenize( - Fts5Config *pConfig, /* FTS5 Configuration object */ - const char *pText, int nText, /* Text to tokenize */ - void *pCtx, /* Context passed to xToken() */ - int (*xToken)(void*, const char*, int, int, int) /* Callback */ -){ - return pConfig->pTokApi->xTokenize(pConfig->pTok, pCtx, pText, nText, xToken); -} - -/* -** Argument pIn points to the first character in what is expected to be -** a comma-separated list of SQL literals followed by a ')' character. -** If it actually is this, return a pointer to the ')'. Otherwise, return -** NULL to indicate a parse error. -*/ -static const char *fts5ConfigSkipArgs(const char *pIn){ - const char *p = pIn; - - while( 1 ){ - p = fts5ConfigSkipWhitespace(p); - p = fts5ConfigSkipLiteral(p); - p = fts5ConfigSkipWhitespace(p); - if( p==0 || *p==')' ) break; - if( *p!=',' ){ - p = 0; - break; - } - p++; - } - - return p; -} - -/* -** Parameter zIn contains a rank() function specification. The format of -** this is: -** -** + Bareword (function name) -** + Open parenthesis - "(" -** + Zero or more SQL literals in a comma separated list -** + Close parenthesis - ")" -*/ -int sqlite3Fts5ConfigParseRank( - const char *zIn, /* Input string */ - char **pzRank, /* OUT: Rank function name */ - char **pzRankArgs /* OUT: Rank function arguments */ -){ - const char *p = zIn; - const char *pRank; - char *zRank = 0; - char *zRankArgs = 0; - int rc = SQLITE_OK; - - *pzRank = 0; - *pzRankArgs = 0; - - p = fts5ConfigSkipWhitespace(p); - pRank = p; - p = fts5ConfigSkipBareword(p); - - if( p ){ - zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank); - if( zRank ) memcpy(zRank, pRank, p-pRank); - }else{ - rc = SQLITE_ERROR; - } - - if( rc==SQLITE_OK ){ - p = fts5ConfigSkipWhitespace(p); - if( *p!='(' ) rc = SQLITE_ERROR; - p++; - } - if( rc==SQLITE_OK ){ - const char *pArgs; - p = fts5ConfigSkipWhitespace(p); - pArgs = p; - if( *p!=')' ){ - p = fts5ConfigSkipArgs(p); - if( p==0 ){ - rc = SQLITE_ERROR; - }else if( p!=pArgs ){ - zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs); - if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs); - } - } - } - - if( rc!=SQLITE_OK ){ - sqlite3_free(zRank); - assert( zRankArgs==0 ); - }else{ - *pzRank = zRank; - *pzRankArgs = zRankArgs; - } - return rc; -} - -int sqlite3Fts5ConfigSetValue( - Fts5Config *pConfig, - const char *zKey, - sqlite3_value *pVal, - int *pbBadkey -){ - int rc = SQLITE_OK; - if( 0==sqlite3_stricmp(zKey, "cookie") ){ - pConfig->iCookie = sqlite3_value_int(pVal); - } - - else if( 0==sqlite3_stricmp(zKey, "pgsz") ){ - int pgsz = 0; - if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ - pgsz = sqlite3_value_int(pVal); - } - if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){ - if( pbBadkey ) *pbBadkey = 1; - }else{ - pConfig->pgsz = pgsz; - } - } - - else if( 0==sqlite3_stricmp(zKey, "automerge") ){ - int nAutomerge = -1; - if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){ - nAutomerge = sqlite3_value_int(pVal); - } - if( nAutomerge<0 || nAutomerge>64 ){ - if( pbBadkey ) *pbBadkey = 1; - }else{ - if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE; - pConfig->nAutomerge = nAutomerge; - } - } - - else if( 0==sqlite3_stricmp(zKey, "rank") ){ - const char *zIn = (const char*)sqlite3_value_text(pVal); - char *zRank; - char *zRankArgs; - rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs); - if( rc==SQLITE_OK ){ - sqlite3_free(pConfig->zRank); - sqlite3_free(pConfig->zRankArgs); - pConfig->zRank = zRank; - pConfig->zRankArgs = zRankArgs; - }else if( rc==SQLITE_ERROR ){ - rc = SQLITE_OK; - if( pbBadkey ) *pbBadkey = 1; - } - }else{ - if( pbBadkey ) *pbBadkey = 1; - } - return rc; -} - -/* -** Load the contents of the %_config table into memory. -*/ -int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){ - const char *zSelect = "SELECT k, v FROM %Q.'%q_config'"; - char *zSql; - sqlite3_stmt *p = 0; - int rc; - - /* Set default values */ - pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE; - pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE; - - zSql = sqlite3_mprintf(zSelect, pConfig->zDb, pConfig->zName); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p, 0); - sqlite3_free(zSql); - } - - assert( rc==SQLITE_OK || p==0 ); - if( rc==SQLITE_OK ){ - while( SQLITE_ROW==sqlite3_step(p) ){ - const char *zK = (const char*)sqlite3_column_text(p, 0); - sqlite3_value *pVal = sqlite3_column_value(p, 1); - sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, 0); - } - rc = sqlite3_finalize(p); - } - - if( rc==SQLITE_OK ){ - pConfig->iCookie = iCookie; - } - return rc; -} - DELETED ext/fts5/fts5_expr.c Index: ext/fts5/fts5_expr.c ================================================================== --- ext/fts5/fts5_expr.c +++ /dev/null @@ -1,1686 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -** -*/ - -#include "fts5Int.h" -#include "fts5parse.h" - -/* -** All token types in the generated fts5parse.h file are greater than 0. -*/ -#define FTS5_EOF 0 - -typedef struct Fts5ExprTerm Fts5ExprTerm; - -/* -** Functions generated by lemon from fts5parse.y. -*/ -void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(size_t)); -void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*)); -void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*); - -struct Fts5Expr { - Fts5Index *pIndex; - Fts5ExprNode *pRoot; - int bAsc; - int nPhrase; /* Number of phrases in expression */ - Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */ -}; - -/* -** eType: -** Expression node type. Always one of: -** -** FTS5_AND (pLeft, pRight valid) -** FTS5_OR (pLeft, pRight valid) -** FTS5_NOT (pLeft, pRight valid) -** FTS5_STRING (pNear valid) -*/ -struct Fts5ExprNode { - int eType; /* Node type */ - Fts5ExprNode *pLeft; /* Left hand child node */ - Fts5ExprNode *pRight; /* Right hand child node */ - Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ - int bEof; /* True at EOF */ - i64 iRowid; /* Current rowid */ -}; - -/* -** An instance of the following structure represents a single search term -** or term prefix. -*/ -struct Fts5ExprTerm { - int bPrefix; /* True for a prefix term */ - char *zTerm; /* nul-terminated term */ - Fts5IndexIter *pIter; /* Iterator for this term */ -}; - -/* -** A phrase. One or more terms that must appear in a contiguous sequence -** within a document for it to match. -*/ -struct Fts5ExprPhrase { - Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ - Fts5Buffer poslist; /* Current position list */ - int nTerm; /* Number of entries in aTerm[] */ - Fts5ExprTerm aTerm[0]; /* Terms that make up this phrase */ -}; - -/* -** One or more phrases that must appear within a certain token distance of -** each other within each matching document. -*/ -struct Fts5ExprNearset { - int nNear; /* NEAR parameter */ - int iCol; /* Column to search (-1 -> all columns) */ - int nPhrase; /* Number of entries in aPhrase[] array */ - Fts5ExprPhrase *apPhrase[0]; /* Array of phrase pointers */ -}; - - -/* -** Parse context. -*/ -struct Fts5Parse { - Fts5Config *pConfig; - char *zErr; - int rc; - int nPhrase; /* Size of apPhrase array */ - Fts5ExprPhrase **apPhrase; /* Array of all phrases */ - Fts5ExprNode *pExpr; /* Result of a successful parse */ -}; - -void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ - if( pParse->rc==SQLITE_OK ){ - va_list ap; - va_start(ap, zFmt); - pParse->zErr = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - pParse->rc = SQLITE_ERROR; - } -} - -static int fts5ExprIsspace(char t){ - return t==' ' || t=='\t' || t=='\n' || t=='\r'; -} - -static int fts5ExprIstoken(char t){ - return fts5ExprIsspace(t)==0 && t!='\0' - && t!=':' && t!='(' && t!=')' - && t!=',' && t!='+' && t!='*'; -} - -/* -** Read the first token from the nul-terminated string at *pz. -*/ -static int fts5ExprGetToken( - Fts5Parse *pParse, - const char **pz, /* IN/OUT: Pointer into buffer */ - Fts5Token *pToken -){ - const char *z = *pz; - int tok; - - /* Skip past any whitespace */ - while( fts5ExprIsspace(*z) ) z++; - - pToken->p = z; - pToken->n = 1; - switch( *z ){ - case '(': tok = FTS5_LP; break; - case ')': tok = FTS5_RP; break; - case ':': tok = FTS5_COLON; break; - case ',': tok = FTS5_COMMA; break; - case '+': tok = FTS5_PLUS; break; - case '*': tok = FTS5_STAR; break; - case '\0': tok = FTS5_EOF; break; - - case '"': { - const char *z2; - tok = FTS5_STRING; - - for(z2=&z[1]; 1; z2++){ - if( z2[0]=='"' ){ - z2++; - if( z2[0]!='"' ) break; - } - if( z2[0]=='\0' ){ - sqlite3Fts5ParseError(pParse, "unterminated string"); - return FTS5_EOF; - } - } - pToken->n = (z2 - z); - break; - } - - default: { - const char *z2; - tok = FTS5_STRING; - for(z2=&z[1]; fts5ExprIstoken(*z2); z2++); - pToken->n = (z2 - z); - if( pToken->n==2 && memcmp(pToken->p, "OR", 2)==0 ) tok = FTS5_OR; - if( pToken->n==3 && memcmp(pToken->p, "NOT", 3)==0 ) tok = FTS5_NOT; - if( pToken->n==3 && memcmp(pToken->p, "AND", 3)==0 ) tok = FTS5_AND; - break; - } - } - - *pz = &pToken->p[pToken->n]; - return tok; -} - -static void *fts5ParseAlloc(size_t t){ return sqlite3_malloc((int)t); } -static void fts5ParseFree(void *p){ sqlite3_free(p); } - -int sqlite3Fts5ExprNew( - Fts5Config *pConfig, /* FTS5 Configuration */ - const char *zExpr, /* Expression text */ - Fts5Expr **ppNew, - char **pzErr -){ - Fts5Parse sParse; - Fts5Token token; - const char *z = zExpr; - int t; /* Next token type */ - void *pEngine; - Fts5Expr *pNew; - - *ppNew = 0; - *pzErr = 0; - memset(&sParse, 0, sizeof(sParse)); - pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc); - if( pEngine==0 ){ return SQLITE_NOMEM; } - sParse.pConfig = pConfig; - - do { - t = fts5ExprGetToken(&sParse, &z, &token); - sqlite3Fts5Parser(pEngine, t, token, &sParse); - }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF ); - sqlite3Fts5ParserFree(pEngine, fts5ParseFree); - - assert( sParse.pExpr==0 || (sParse.rc==SQLITE_OK && sParse.zErr==0) ); - if( sParse.rc==SQLITE_OK ){ - *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr)); - if( pNew==0 ){ - sParse.rc = SQLITE_NOMEM; - sqlite3Fts5ParseNodeFree(sParse.pExpr); - }else{ - pNew->pRoot = sParse.pExpr; - pNew->pIndex = 0; - pNew->apExprPhrase = sParse.apPhrase; - pNew->nPhrase = sParse.nPhrase; - sParse.apPhrase = 0; - } - } - - sqlite3_free(sParse.apPhrase); - *pzErr = sParse.zErr; - return sParse.rc; -} - -static char *fts5ExprStrdup(int *pRc, const char *zIn){ - char *zRet = 0; - if( *pRc==SQLITE_OK ){ - int nByte = strlen(zIn) + 1; - zRet = sqlite3_malloc(nByte); - if( zRet ){ - memcpy(zRet, zIn, nByte); - }else{ - *pRc = SQLITE_NOMEM; - } - } - return zRet; -} - -static void *fts5ExprMalloc(int *pRc, int nByte){ - void *pRet = 0; - if( *pRc==SQLITE_OK ){ - pRet = sqlite3_malloc(nByte); - if( pRet ){ - memset(pRet, 0, nByte); - }else{ - *pRc = SQLITE_NOMEM; - } - } - return pRet; -} - -/* -** Create a new FTS5 expression by cloning phrase iPhrase of the -** expression passed as the second argument. -*/ -int sqlite3Fts5ExprPhraseExpr( - Fts5Config *pConfig, - Fts5Expr *pExpr, - int iPhrase, - Fts5Expr **ppNew -){ - int rc = SQLITE_OK; /* Return code */ - Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ - int i; /* Used to iterate through phrase terms */ - - /* Components of the new expression object */ - Fts5Expr *pNew; - Fts5ExprPhrase **apPhrase; - Fts5ExprNode *pNode; - Fts5ExprNearset *pNear; - Fts5ExprPhrase *pCopy; - - pOrig = pExpr->apExprPhrase[iPhrase]; - pNew = (Fts5Expr*)fts5ExprMalloc(&rc, sizeof(Fts5Expr)); - apPhrase = (Fts5ExprPhrase**)fts5ExprMalloc(&rc, sizeof(Fts5ExprPhrase*)); - pNode = (Fts5ExprNode*)fts5ExprMalloc(&rc, sizeof(Fts5ExprNode)); - pNear = (Fts5ExprNearset*)fts5ExprMalloc(&rc, - sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*) - ); - pCopy = (Fts5ExprPhrase*)fts5ExprMalloc(&rc, - sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * pOrig->nTerm - ); - - for(i=0; rc==SQLITE_OK && inTerm; i++){ - pCopy->aTerm[i].zTerm = fts5ExprStrdup(&rc, pOrig->aTerm[i].zTerm); - pCopy->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; - } - - if( rc==SQLITE_OK ){ - /* All the allocations succeeded. Put the expression object together. */ - pNew->pIndex = pExpr->pIndex; - pNew->pRoot = pNode; - pNew->nPhrase = 1; - pNew->apExprPhrase = apPhrase; - pNew->apExprPhrase[0] = pCopy; - - pNode->eType = FTS5_STRING; - pNode->pNear = pNear; - - pNear->iCol = -1; - pNear->nPhrase = 1; - pNear->apPhrase[0] = pCopy; - - pCopy->nTerm = pOrig->nTerm; - pCopy->pNode = pNode; - }else{ - /* At least one allocation failed. Free them all. */ - if( pCopy ){ - for(i=0; inTerm; i++){ - sqlite3_free(pCopy->aTerm[i].zTerm); - } - sqlite3_free(pCopy); - sqlite3_free(pNear); - sqlite3_free(pNode); - sqlite3_free(apPhrase); - sqlite3_free(pNew); - pNew = 0; - } - } - - *ppNew = pNew; - return rc; -} - -/* -** Free the expression node object passed as the only argument. -*/ -void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){ - if( p ){ - sqlite3Fts5ParseNodeFree(p->pLeft); - sqlite3Fts5ParseNodeFree(p->pRight); - sqlite3Fts5ParseNearsetFree(p->pNear); - sqlite3_free(p); - } -} - -/* -** Free the expression object passed as the only argument. -*/ -void sqlite3Fts5ExprFree(Fts5Expr *p){ - if( p ){ - sqlite3Fts5ParseNodeFree(p->pRoot); - sqlite3_free(p->apExprPhrase); - sqlite3_free(p); - } -} - -/* -** All individual term iterators in pPhrase are guaranteed to be valid and -** pointing to the same rowid when this function is called. This function -** checks if the current rowid really is a match, and if so populates -** the pPhrase->poslist buffer accordingly. Output parameter *pbMatch -** is set to true if this is really a match, or false otherwise. -** -** SQLITE_OK is returned if an error occurs, or an SQLite error code -** otherwise. It is not considered an error code if the current rowid is -** not a match. -*/ -static int fts5ExprPhraseIsMatch( - Fts5Expr *pExpr, /* Expression pPhrase belongs to */ - int iCol, /* If >=0, search for matches in iCol only */ - Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */ - int *pbMatch /* OUT: Set to true if really a match */ -){ - Fts5PoslistWriter writer = {0}; - Fts5PoslistReader aStatic[4]; - Fts5PoslistReader *aIter = aStatic; - int i; - int rc = SQLITE_OK; - - fts5BufferZero(&pPhrase->poslist); - - /* If the aStatic[] array is not large enough, allocate a large array - ** using sqlite3_malloc(). This approach could be improved upon. */ - if( pPhrase->nTerm>(sizeof(aStatic) / sizeof(aStatic[0])) ){ - int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm; - aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte); - if( !aIter ) return SQLITE_NOMEM; - } - - /* Initialize a term iterator for each term in the phrase */ - for(i=0; inTerm; i++){ - int n; - const u8 *a; - rc = sqlite3Fts5IterPoslist(pPhrase->aTerm[i].pIter, &a, &n); - if( rc || sqlite3Fts5PoslistReaderInit(iCol, a, n, &aIter[i]) ){ - goto ismatch_out; - } - } - - while( 1 ){ - int bMatch; - i64 iPos = aIter[0].iPos; - do { - bMatch = 1; - for(i=0; inTerm; i++){ - Fts5PoslistReader *pPos = &aIter[i]; - i64 iAdj = iPos + i; - if( pPos->iPos!=iAdj ){ - bMatch = 0; - while( pPos->iPosiPos>iAdj ) iPos = pPos->iPos-i; - } - } - }while( bMatch==0 ); - - /* Append position iPos to the output */ - rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos); - if( rc!=SQLITE_OK ) goto ismatch_out; - - for(i=0; inTerm; i++){ - if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out; - } - } - - ismatch_out: - *pbMatch = (pPhrase->poslist.n>0); - if( aIter!=aStatic ) sqlite3_free(aIter); - return rc; -} - -typedef struct Fts5LookaheadReader Fts5LookaheadReader; -struct Fts5LookaheadReader { - const u8 *a; /* Buffer containing position list */ - int n; /* Size of buffer a[] in bytes */ - int i; /* Current offset in position list */ - i64 iPos; /* Current position */ - i64 iLookahead; /* Next position */ -}; - -#define FTS5_LOOKAHEAD_EOF (((i64)1) << 62) - -static int fts5LookaheadReaderNext(Fts5LookaheadReader *p){ - p->iPos = p->iLookahead; - if( sqlite3Fts5PoslistNext64(p->a, p->n, &p->i, &p->iLookahead) ){ - p->iLookahead = FTS5_LOOKAHEAD_EOF; - } - return (p->iPos==FTS5_LOOKAHEAD_EOF); -} - -static int fts5LookaheadReaderInit( - const u8 *a, int n, /* Buffer to read position list from */ - Fts5LookaheadReader *p /* Iterator object to initialize */ -){ - memset(p, 0, sizeof(Fts5LookaheadReader)); - p->a = a; - p->n = n; - fts5LookaheadReaderNext(p); - return fts5LookaheadReaderNext(p); -} - -#if 0 -static int fts5LookaheadReaderEof(Fts5LookaheadReader *p){ - return (p->iPos==FTS5_LOOKAHEAD_EOF); -} -#endif - -typedef struct Fts5NearTrimmer Fts5NearTrimmer; -struct Fts5NearTrimmer { - Fts5LookaheadReader reader; /* Input iterator */ - Fts5PoslistWriter writer; /* Writer context */ - Fts5Buffer *pOut; /* Output poslist */ -}; - -/* -** The near-set object passed as the first argument contains more than -** one phrase. All phrases currently point to the same row. The -** Fts5ExprPhrase.poslist buffers are populated accordingly. This function -** tests if the current row contains instances of each phrase sufficiently -** close together to meet the NEAR constraint. Output variable *pbMatch -** is set to true if it does, or false otherwise. -** -** If no error occurs, SQLITE_OK is returned. Or, if an error does occur, -** an SQLite error code. If a value other than SQLITE_OK is returned, the -** final value of *pbMatch is undefined. -** -** TODO: This function should also edit the position lists associated -** with each phrase to remove any phrase instances that are not part of -** a set of intances that collectively matches the NEAR constraint. -*/ -static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){ - Fts5NearTrimmer aStatic[4]; - Fts5NearTrimmer *a = aStatic; - - Fts5ExprPhrase **apPhrase = pNear->apPhrase; - - int i; - int rc = SQLITE_OK; - int bMatch; - - assert( pNear->nPhrase>1 ); - - /* If the aStatic[] array is not large enough, allocate a large array - ** using sqlite3_malloc(). This approach could be improved upon. */ - if( pNear->nPhrase>(sizeof(aStatic) / sizeof(aStatic[0])) ){ - int nByte = sizeof(Fts5LookaheadReader) * pNear->nPhrase; - a = (Fts5NearTrimmer*)sqlite3_malloc(nByte); - if( !a ) return SQLITE_NOMEM; - memset(a, 0, nByte); - }else{ - memset(aStatic, 0, sizeof(aStatic)); - } - - /* Initialize a lookahead iterator for each phrase. After passing the - ** buffer and buffer size to the lookaside-reader init function, zero - ** the phrase poslist buffer. The new poslist for the phrase (containing - ** the same entries as the original with some entries removed on account - ** of the NEAR constraint) is written over the original even as it is - ** being read. This is safe as the entries for the new poslist are a - ** subset of the old, so it is not possible for data yet to be read to - ** be overwritten. */ - for(i=0; inPhrase; i++){ - Fts5Buffer *pPoslist = &apPhrase[i]->poslist; - fts5LookaheadReaderInit(pPoslist->p, pPoslist->n, &a[i].reader); - pPoslist->n = 0; - a[i].pOut = pPoslist; - } - - while( 1 ){ - int iAdv; - i64 iMin; - i64 iMax; - - /* This block advances the phrase iterators until they point to a set of - ** entries that together comprise a match. */ - iMax = a[0].reader.iPos; - do { - bMatch = 1; - for(i=0; inPhrase; i++){ - Fts5LookaheadReader *pPos = &a[i].reader; - iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear; - if( pPos->iPosiPos>iMax ){ - bMatch = 0; - while( pPos->iPosiPos>iMax ) iMax = pPos->iPos; - } - } - }while( bMatch==0 ); - - /* Add an entry to each output position list */ - for(i=0; inPhrase; i++){ - i64 iPos = a[i].reader.iPos; - Fts5PoslistWriter *pWriter = &a[i].writer; - if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){ - sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos); - } - } - - iAdv = 0; - iMin = a[0].reader.iLookahead; - for(i=0; inPhrase; i++){ - if( a[i].reader.iLookahead < iMin ){ - iMin = a[i].reader.iLookahead; - iAdv = i; - } - } - if( fts5LookaheadReaderNext(&a[iAdv].reader) ) goto ismatch_out; - } - - ismatch_out: - *pbMatch = (a[0].pOut->n>0); - if( a!=aStatic ) sqlite3_free(a); - return rc; -} - -/* -** Advance each term iterator in each phrase in pNear. If any reach EOF, -** set output variable *pbEof to true before returning. -*/ -static int fts5ExprNearAdvanceAll( - Fts5Expr *pExpr, /* Expression pPhrase belongs to */ - Fts5ExprNearset *pNear, /* Near object to advance iterators of */ - int *pbEof /* OUT: Set to true if phrase at EOF */ -){ - int i, j; /* Phrase and token index, respectively */ - - for(i=0; inPhrase; i++){ - Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; - for(j=0; jnTerm; j++){ - Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; - int rc = sqlite3Fts5IterNext(pIter); - if( rc || sqlite3Fts5IterEof(pIter) ){ - *pbEof = 1; - return rc; - } - } - } - - return SQLITE_OK; -} - -/* -** Advance iterator pIter until it points to a value equal to or smaller -** than the initial value of *piMin. If this means the iterator points -** to a value smaller than *piMin, update *piMin to the new smallest value. -** -** If the iterator reaches EOF, set *pbEof to true before returning. If -** an error occurs, set *pRc to an error code. If either *pbEof or *pRc -** are set, return a non-zero value. Otherwise, return zero. -*/ -static int fts5ExprAdvanceto( - Fts5IndexIter *pIter, /* Iterator to advance */ - int bAsc, /* True if iterator is "rowid ASC" */ - i64 *piLast, /* IN/OUT: Lastest rowid seen so far */ - int *pRc, /* OUT: Error code */ - int *pbEof /* OUT: Set to true if EOF */ -){ - i64 iLast = *piLast; - i64 iRowid; - - iRowid = sqlite3Fts5IterRowid(pIter); - if( (bAsc==0 && iRowid>iLast) || (bAsc && iRowid=iLast) ); - } - *piLast = iRowid; - - return 0; -} - -/* -** All individual term iterators in pNear are guaranteed to be valid when -** this function is called. This function checks if all term iterators -** point to the same rowid, and if not, advances them until they do. -** If an EOF is reached before this happens, *pbEof is set to true before -** returning. -** -** SQLITE_OK is returned if an error occurs, or an SQLite error code -** otherwise. It is not considered an error code if an iterator reaches -** EOF. -*/ -static int fts5ExprNearNextRowidMatch( - Fts5Expr *pExpr, /* Expression pPhrase belongs to */ - Fts5ExprNode *pNode, - int bFromValid, - i64 iFrom -){ - Fts5ExprNearset *pNear = pNode->pNear; - int rc = SQLITE_OK; - int i, j; /* Phrase and token index, respectively */ - i64 iLast; /* Lastest rowid any iterator points to */ - int bMatch; /* True if all terms are at the same rowid */ - - /* Set iLast, the lastest rowid any iterator points to. If the iterator - ** skips through rowids in the default descending order, this means the - ** minimum rowid. Or, if the iterator is "ORDER BY rowid ASC", then it - ** means the maximum rowid. */ - iLast = sqlite3Fts5IterRowid(pNear->apPhrase[0]->aTerm[0].pIter); - if( bFromValid && (iFrom>iLast)==(pExpr->bAsc!=0) ){ - iLast = iFrom; - } - - do { - bMatch = 1; - for(i=0; inPhrase; i++){ - Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; - for(j=0; jnTerm; j++){ - Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter; - i64 iRowid = sqlite3Fts5IterRowid(pIter); - if( iRowid!=iLast ) bMatch = 0; - if( fts5ExprAdvanceto(pIter, pExpr->bAsc, &iLast, &rc, &pNode->bEof) ){ - return rc; - } - } - } - }while( bMatch==0 ); - - pNode->iRowid = iLast; - return rc; -} - -/* -** Argument pNode points to a NEAR node. All individual term iterators -** point to valid entries (not EOF). -* -** This function tests if the term iterators currently all point to the -** same rowid, and if so, if the row matches the phrase and NEAR constraints. -** If so, the pPhrase->poslist buffers are populated and the pNode->iRowid -** variable set before returning. Or, if the current combination of -** iterators is not a match, they are advanced until they are. If one of -** the iterators reaches EOF before a match is found, *pbEof is set to -** true before returning. The final values of the pPhrase->poslist and -** iRowid fields are undefined in this case. -** -** SQLITE_OK is returned if an error occurs, or an SQLite error code -** otherwise. It is not considered an error code if an iterator reaches -** EOF. -*/ -static int fts5ExprNearNextMatch( - Fts5Expr *pExpr, /* Expression that pNear is a part of */ - Fts5ExprNode *pNode, /* The "NEAR" node (FTS5_STRING) */ - int bFromValid, - i64 iFrom -){ - int rc = SQLITE_OK; - Fts5ExprNearset *pNear = pNode->pNear; - while( 1 ){ - int i; - - /* Advance the iterators until they all point to the same rowid */ - rc = fts5ExprNearNextRowidMatch(pExpr, pNode, bFromValid, iFrom); - if( pNode->bEof || rc!=SQLITE_OK ) break; - - /* Check that each phrase in the nearset matches the current row. - ** Populate the pPhrase->poslist buffers at the same time. If any - ** phrase is not a match, break out of the loop early. */ - for(i=0; rc==SQLITE_OK && inPhrase; i++){ - Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; - if( pPhrase->nTerm>1 || pNear->iCol>=0 ){ - int bMatch = 0; - rc = fts5ExprPhraseIsMatch(pExpr, pNear->iCol, pPhrase, &bMatch); - if( bMatch==0 ) break; - }else{ - int n; - const u8 *a; - rc = sqlite3Fts5IterPoslist(pPhrase->aTerm[0].pIter, &a, &n); - fts5BufferSet(&rc, &pPhrase->poslist, n, a); - } - } - - if( rc==SQLITE_OK && i==pNear->nPhrase ){ - int bMatch = 1; - if( pNear->nPhrase>1 ){ - rc = fts5ExprNearIsMatch(pNear, &bMatch); - } - if( rc!=SQLITE_OK || bMatch ) break; - } - - /* If control flows to here, then the current rowid is not a match. - ** Advance all term iterators in all phrases to the next rowid. */ - if( rc==SQLITE_OK ){ - rc = fts5ExprNearAdvanceAll(pExpr, pNear, &pNode->bEof); - } - if( pNode->bEof || rc!=SQLITE_OK ) break; - } - - return rc; -} - -/* -** Initialize all term iterators in the pNear object. If any term is found -** to match no documents at all, set *pbEof to true and return immediately, -** without initializing any further iterators. -*/ -static int fts5ExprNearInitAll( - Fts5Expr *pExpr, - Fts5ExprNode *pNode -){ - Fts5ExprNearset *pNear = pNode->pNear; - Fts5ExprTerm *pTerm; - Fts5ExprPhrase *pPhrase; - int i, j; - int rc = SQLITE_OK; - - for(i=0; rc==SQLITE_OK && inPhrase; i++){ - pPhrase = pNear->apPhrase[i]; - for(j=0; jnTerm; j++){ - pTerm = &pPhrase->aTerm[j]; - rc = sqlite3Fts5IndexQuery( - pExpr->pIndex, pTerm->zTerm, strlen(pTerm->zTerm), - (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | - (pExpr->bAsc ? FTS5INDEX_QUERY_ASC : 0), - &pTerm->pIter - ); - assert( rc==SQLITE_OK || pTerm->pIter==0 ); - if( pTerm->pIter==0 || sqlite3Fts5IterEof(pTerm->pIter) ){ - pNode->bEof = 1; - break; - } - } - } - - return rc; -} - -/* fts5ExprNodeNext() calls fts5ExprNodeNextMatch(). And vice-versa. */ -static int fts5ExprNodeNextMatch(Fts5Expr*, Fts5ExprNode*, int, i64); - -/* -** Compare the values currently indicated by the two nodes as follows: -** -** res = (*p1) - (*p2) -** -** Nodes that point to values that come later in the iteration order are -** considered to be larger. Nodes at EOF are the largest of all. -** -** This means that if the iteration order is ASC, then numerically larger -** rowids are considered larger. Or if it is the default DESC, numerically -** smaller rowids are larger. -*/ -static int fts5NodeCompare( - Fts5Expr *pExpr, - Fts5ExprNode *p1, - Fts5ExprNode *p2 -){ - if( p2->bEof ) return -1; - if( p1->bEof ) return +1; - if( pExpr->bAsc ){ - if( p1->iRowidiRowid ) return -1; - return (p1->iRowid > p2->iRowid); - }else{ - if( p1->iRowid>p2->iRowid ) return -1; - return (p1->iRowid < p2->iRowid); - } -} - -/* -** Advance node iterator pNode, part of expression pExpr. If argument -** bFromValid is zero, then pNode is advanced exactly once. Or, if argument -** bFromValid is non-zero, then pNode is advanced until it is at or past -** rowid value iFrom. Whether "past" means "less than" or "greater than" -** depends on whether this is an ASC or DESC iterator. -*/ -static int fts5ExprNodeNext( - Fts5Expr *pExpr, - Fts5ExprNode *pNode, - int bFromValid, - i64 iFrom -){ - int rc = SQLITE_OK; - - if( pNode->bEof==0 ){ - switch( pNode->eType ){ - case FTS5_STRING: { - rc = fts5ExprNearAdvanceAll(pExpr, pNode->pNear, &pNode->bEof); - break; - }; - - case FTS5_AND: { - rc = fts5ExprNodeNext(pExpr, pNode->pLeft, bFromValid, iFrom); - if( rc==SQLITE_OK ){ - /* todo: update (iFrom/bFromValid) here */ - rc = fts5ExprNodeNext(pExpr, pNode->pRight, bFromValid, iFrom); - } - break; - } - - case FTS5_OR: { - Fts5ExprNode *p1 = pNode->pLeft; - Fts5ExprNode *p2 = pNode->pRight; - int cmp = fts5NodeCompare(pExpr, p1, p2); - - if( cmp==0 ){ - rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom); - if( rc==SQLITE_OK ){ - rc = fts5ExprNodeNext(pExpr, p2, bFromValid, iFrom); - } - }else{ - rc = fts5ExprNodeNext(pExpr, (cmp < 0) ? p1 : p2, bFromValid, iFrom); - } - - break; - } - - default: assert( pNode->eType==FTS5_NOT ); { - rc = fts5ExprNodeNext(pExpr, pNode->pLeft, bFromValid, iFrom); - break; - } - } - - if( rc==SQLITE_OK ){ - rc = fts5ExprNodeNextMatch(pExpr, pNode, bFromValid, iFrom); - } - } - - return rc; -} - -static void fts5ExprSetEof(Fts5ExprNode *pNode){ - if( pNode ){ - pNode->bEof = 1; - fts5ExprSetEof(pNode->pLeft); - fts5ExprSetEof(pNode->pRight); - } -} - -/* -** -*/ -static int fts5ExprNodeNextMatch( - Fts5Expr *pExpr, - Fts5ExprNode *pNode, - int bFromValid, - i64 iFrom -){ - int rc = SQLITE_OK; - if( pNode->bEof==0 ){ - switch( pNode->eType ){ - - case FTS5_STRING: { - rc = fts5ExprNearNextMatch(pExpr, pNode, bFromValid, iFrom); - break; - } - - case FTS5_AND: { - Fts5ExprNode *p1 = pNode->pLeft; - Fts5ExprNode *p2 = pNode->pRight; - - - while( p1->bEof==0 && p2->bEof==0 && p2->iRowid!=p1->iRowid ){ - Fts5ExprNode *pAdv; - assert( pExpr->bAsc==0 || pExpr->bAsc==1 ); - if( pExpr->bAsc==(p1->iRowid < p2->iRowid) ){ - pAdv = p1; - if( bFromValid==0 || pExpr->bAsc==(p2->iRowid > iFrom) ){ - iFrom = p2->iRowid; - } - }else{ - pAdv = p2; - if( bFromValid==0 || pExpr->bAsc==(p1->iRowid > iFrom) ){ - iFrom = p1->iRowid; - } - } - rc = fts5ExprNodeNext(pExpr, pAdv, 1, iFrom); - if( rc!=SQLITE_OK ) break; - } - if( p1->bEof || p2->bEof ){ - fts5ExprSetEof(pNode); - } - pNode->iRowid = p1->iRowid; - break; - } - - case FTS5_OR: { - Fts5ExprNode *p1 = pNode->pLeft; - Fts5ExprNode *p2 = pNode->pRight; - Fts5ExprNode *pNext = (fts5NodeCompare(pExpr, p1, p2) > 0 ? p2 : p1); - pNode->bEof = pNext->bEof; - pNode->iRowid = pNext->iRowid; - break; - } - - default: assert( pNode->eType==FTS5_NOT ); { - Fts5ExprNode *p1 = pNode->pLeft; - Fts5ExprNode *p2 = pNode->pRight; - while( rc==SQLITE_OK ){ - int cmp; - while( rc==SQLITE_OK && (cmp = fts5NodeCompare(pExpr, p1, p2))>0 ){ - rc = fts5ExprNodeNext(pExpr, p2, bFromValid, iFrom); - } - if( rc || cmp ) break; - rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom); - } - pNode->bEof = p1->bEof; - pNode->iRowid = p1->iRowid; - break; - } - } - } - return rc; -} - - -/* -** Set node pNode, which is part of expression pExpr, to point to the first -** match. If there are no matches, set the Node.bEof flag to indicate EOF. -** -** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise. -** It is not an error if there are no matches. -*/ -static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){ - int rc = SQLITE_OK; - pNode->bEof = 0; - - if( pNode->eType==FTS5_STRING ){ - - /* Initialize all term iterators in the NEAR object. */ - rc = fts5ExprNearInitAll(pExpr, pNode); - - /* Attempt to advance to the first match */ - if( rc==SQLITE_OK && pNode->bEof==0 ){ - rc = fts5ExprNearNextMatch(pExpr, pNode, 0, 0); - } - - }else{ - rc = fts5ExprNodeFirst(pExpr, pNode->pLeft); - if( rc==SQLITE_OK ){ - rc = fts5ExprNodeFirst(pExpr, pNode->pRight); - } - if( rc==SQLITE_OK ){ - rc = fts5ExprNodeNextMatch(pExpr, pNode, 0, 0); - } - } - return rc; -} - - - -/* -** Begin iterating through the set of documents in index pIdx matched by -** the MATCH expression passed as the first argument. If the "bAsc" parameter -** is passed a non-zero value, iteration is in ascending rowid order. Or, -** if it is zero, in descending order. -** -** Return SQLITE_OK if successful, or an SQLite error code otherwise. It -** is not considered an error if the query does not match any documents. -*/ -int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, int bAsc){ - int rc; - p->pIndex = pIdx; - p->bAsc = bAsc; - rc = fts5ExprNodeFirst(p, p->pRoot); - return rc; -} - -/* -** Move to the next document -** -** Return SQLITE_OK if successful, or an SQLite error code otherwise. It -** is not considered an error if the query does not match any documents. -*/ -int sqlite3Fts5ExprNext(Fts5Expr *p){ - int rc; - rc = fts5ExprNodeNext(p, p->pRoot, 0, 0); - return rc; -} - -int sqlite3Fts5ExprEof(Fts5Expr *p){ - return p->pRoot->bEof; -} - -i64 sqlite3Fts5ExprRowid(Fts5Expr *p){ - return p->pRoot->iRowid; -} - -/* -** Argument pIn points to a buffer of nIn bytes. This function allocates -** and returns a new buffer populated with a copy of (pIn/nIn) with a -** nul-terminator byte appended to it. -** -** It is the responsibility of the caller to eventually free the returned -** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned. -*/ -static char *fts5Strndup(int *pRc, const char *pIn, int nIn){ - char *zRet = 0; - if( *pRc==SQLITE_OK ){ - zRet = (char*)sqlite3_malloc(nIn+1); - if( zRet ){ - memcpy(zRet, pIn, nIn); - zRet[nIn] = '\0'; - }else{ - *pRc = SQLITE_NOMEM; - } - } - return zRet; -} - -static int fts5ParseStringFromToken(Fts5Token *pToken, char **pz){ - int rc = SQLITE_OK; - *pz = fts5Strndup(&rc, pToken->p, pToken->n); - return rc; -} - -/* -** Free the phrase object passed as the only argument. -*/ -static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ - if( pPhrase ){ - int i; - for(i=0; inTerm; i++){ - Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; - sqlite3_free(pTerm->zTerm); - if( pTerm->pIter ){ - sqlite3Fts5IterClose(pTerm->pIter); - } - } - fts5BufferFree(&pPhrase->poslist); - sqlite3_free(pPhrase); - } -} - -/* -** If argument pNear is NULL, then a new Fts5ExprNearset object is allocated -** and populated with pPhrase. Or, if pNear is not NULL, phrase pPhrase is -** appended to it and the results returned. -** -** If an OOM error occurs, both the pNear and pPhrase objects are freed and -** NULL returned. -*/ -Fts5ExprNearset *sqlite3Fts5ParseNearset( - Fts5Parse *pParse, /* Parse context */ - Fts5ExprNearset *pNear, /* Existing nearset, or NULL */ - Fts5ExprPhrase *pPhrase /* Recently parsed phrase */ -){ - const int SZALLOC = 8; - Fts5ExprNearset *pRet = 0; - - if( pParse->rc==SQLITE_OK ){ - if( pNear==0 ){ - int nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); - pRet = sqlite3_malloc(nByte); - if( pRet==0 ){ - pParse->rc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, nByte); - pRet->iCol = -1; - } - }else if( (pNear->nPhrase % SZALLOC)==0 ){ - int nNew = pRet->nPhrase + SZALLOC; - int nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); - - pRet = (Fts5ExprNearset*)sqlite3_realloc(pNear, nByte); - if( pRet==0 ){ - pParse->rc = SQLITE_NOMEM; - } - }else{ - pRet = pNear; - } - } - - if( pRet==0 ){ - assert( pParse->rc!=SQLITE_OK ); - sqlite3Fts5ParseNearsetFree(pNear); - sqlite3Fts5ParsePhraseFree(pPhrase); - }else{ - pRet->apPhrase[pRet->nPhrase++] = pPhrase; - } - return pRet; -} - -typedef struct TokenCtx TokenCtx; -struct TokenCtx { - Fts5ExprPhrase *pPhrase; -}; - -/* -** Callback for tokenizing terms used by ParseTerm(). -*/ -static int fts5ParseTokenize( - void *pContext, /* Pointer to Fts5InsertCtx object */ - const char *pToken, /* Buffer containing token */ - int nToken, /* Size of token in bytes */ - int iStart, /* Start offset of token */ - int iEnd /* End offset of token */ -){ - int rc = SQLITE_OK; - const int SZALLOC = 8; - TokenCtx *pCtx = (TokenCtx*)pContext; - Fts5ExprPhrase *pPhrase = pCtx->pPhrase; - Fts5ExprTerm *pTerm; - - if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ - Fts5ExprPhrase *pNew; - int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); - - pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase, - sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew - ); - if( pNew==0 ) return SQLITE_NOMEM; - if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); - pCtx->pPhrase = pPhrase = pNew; - pNew->nTerm = nNew - SZALLOC; - } - - pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; - memset(pTerm, 0, sizeof(Fts5ExprTerm)); - pTerm->zTerm = fts5Strndup(&rc, pToken, nToken); - - return rc; -} - - -/* -** Free the phrase object passed as the only argument. -*/ -void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase *pPhrase){ - fts5ExprPhraseFree(pPhrase); -} - -/* -** Free the phrase object passed as the second argument. -*/ -void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset *pNear){ - if( pNear ){ - int i; - for(i=0; inPhrase; i++){ - fts5ExprPhraseFree(pNear->apPhrase[i]); - } - sqlite3_free(pNear); - } -} - -void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){ - assert( pParse->pExpr==0 ); - pParse->pExpr = p; -} - -/* -** This function is called by the parser to process a string token. The -** string may or may not be quoted. In any case it is tokenized and a -** phrase object consisting of all tokens returned. -*/ -Fts5ExprPhrase *sqlite3Fts5ParseTerm( - Fts5Parse *pParse, /* Parse context */ - Fts5ExprPhrase *pPhrase, /* Phrase to append to */ - Fts5Token *pToken, /* String to tokenize */ - int bPrefix /* True if there is a trailing "*" */ -){ - Fts5Config *pConfig = pParse->pConfig; - TokenCtx sCtx; /* Context object passed to callback */ - int rc; /* Tokenize return code */ - char *z = 0; - - memset(&sCtx, 0, sizeof(TokenCtx)); - sCtx.pPhrase = pPhrase; - - if( pPhrase==0 ){ - if( (pParse->nPhrase % 8)==0 ){ - int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); - Fts5ExprPhrase **apNew; - apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte); - if( apNew==0 ){ - pParse->rc = SQLITE_NOMEM; - fts5ExprPhraseFree(pPhrase); - return 0; - } - pParse->apPhrase = apNew; - } - pParse->nPhrase++; - } - - rc = fts5ParseStringFromToken(pToken, &z); - if( rc==SQLITE_OK ){ - sqlite3Fts5Dequote(z); - rc = sqlite3Fts5Tokenize(pConfig, z, strlen(z), &sCtx, fts5ParseTokenize); - } - if( rc ){ - pParse->rc = rc; - fts5ExprPhraseFree(sCtx.pPhrase); - sCtx.pPhrase = 0; - }else if( sCtx.pPhrase->nTerm>0 ){ - sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix; - } - - - pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; - sqlite3_free(z); - return sCtx.pPhrase; -} - -/* -** Token pTok has appeared in a MATCH expression where the NEAR operator -** is expected. If token pTok does not contain "NEAR", store an error -** in the pParse object. -*/ -void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token *pTok){ - if( pParse->rc==SQLITE_OK ){ - if( pTok->n!=4 || memcmp("NEAR", pTok->p, 4) ){ - sqlite3Fts5ParseError( - pParse, "fts5: syntax error near \"%.*s\"", pTok->n, pTok->p - ); - } - } -} - -void sqlite3Fts5ParseSetDistance( - Fts5Parse *pParse, - Fts5ExprNearset *pNear, - Fts5Token *p -){ - int nNear = 0; - int i; - if( p->n ){ - for(i=0; in; i++){ - char c = (char)p->p[i]; - if( c<'0' || c>'9' ){ - sqlite3Fts5ParseError( - pParse, "expected integer, got \"%.*s\"", p->n, p->p - ); - return; - } - nNear = nNear * 10 + (p->p[i] - '0'); - } - }else{ - nNear = FTS5_DEFAULT_NEARDIST; - } - pNear->nNear = nNear; -} - -void sqlite3Fts5ParseSetColumn( - Fts5Parse *pParse, - Fts5ExprNearset *pNear, - Fts5Token *p -){ - char *z = 0; - int rc = fts5ParseStringFromToken(p, &z); - if( rc==SQLITE_OK ){ - Fts5Config *pConfig = pParse->pConfig; - int i; - for(i=0; inCol; i++){ - if( 0==sqlite3_stricmp(pConfig->azCol[i], z) ){ - pNear->iCol = i; - break; - } - } - if( i==pConfig->nCol ){ - sqlite3Fts5ParseError(pParse, "no such column: %s", z); - } - sqlite3_free(z); - }else{ - pParse->rc = rc; - } -} - -/* -** Allocate and return a new expression object. If anything goes wrong (i.e. -** OOM error), leave an error code in pParse and return NULL. -*/ -Fts5ExprNode *sqlite3Fts5ParseNode( - Fts5Parse *pParse, /* Parse context */ - int eType, /* FTS5_STRING, AND, OR or NOT */ - Fts5ExprNode *pLeft, /* Left hand child expression */ - Fts5ExprNode *pRight, /* Right hand child expression */ - Fts5ExprNearset *pNear /* For STRING expressions, the near cluster */ -){ - Fts5ExprNode *pRet = 0; - - if( pParse->rc==SQLITE_OK ){ - assert( (eType!=FTS5_STRING && pLeft && pRight && !pNear) - || (eType==FTS5_STRING && !pLeft && !pRight && pNear) - ); - pRet = (Fts5ExprNode*)sqlite3_malloc(sizeof(Fts5ExprNode)); - if( pRet==0 ){ - pParse->rc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, sizeof(*pRet)); - pRet->eType = eType; - pRet->pLeft = pLeft; - pRet->pRight = pRight; - pRet->pNear = pNear; - if( eType==FTS5_STRING ){ - int iPhrase; - for(iPhrase=0; iPhrasenPhrase; iPhrase++){ - pNear->apPhrase[iPhrase]->pNode = pRet; - } - } - } - } - - if( pRet==0 ){ - assert( pParse->rc!=SQLITE_OK ); - sqlite3Fts5ParseNodeFree(pLeft); - sqlite3Fts5ParseNodeFree(pRight); - sqlite3Fts5ParseNearsetFree(pNear); - } - return pRet; -} - -static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ - char *zQuoted = sqlite3_malloc(strlen(pTerm->zTerm) * 2 + 3 + 2); - if( zQuoted ){ - int i = 0; - char *zIn = pTerm->zTerm; - zQuoted[i++] = '"'; - while( *zIn ){ - if( *zIn=='"' ) zQuoted[i++] = '"'; - zQuoted[i++] = *zIn++; - } - zQuoted[i++] = '"'; - if( pTerm->bPrefix ){ - zQuoted[i++] = ' '; - zQuoted[i++] = '*'; - } - zQuoted[i++] = '\0'; - } - return zQuoted; -} - -static char *fts5PrintfAppend(char *zApp, const char *zFmt, ...){ - char *zNew; - va_list ap; - va_start(ap, zFmt); - zNew = sqlite3_vmprintf(zFmt, ap); - va_end(ap); - if( zApp ){ - char *zNew2 = sqlite3_mprintf("%s%s", zApp, zNew); - sqlite3_free(zNew); - zNew = zNew2; - } - sqlite3_free(zApp); - return zNew; -} - -/* -** Compose a tcl-readable representation of expression pExpr. Return a -** pointer to a buffer containing that representation. It is the -** responsibility of the caller to at some point free the buffer using -** sqlite3_free(). -*/ -static char *fts5ExprPrintTcl( - Fts5Config *pConfig, - const char *zNearsetCmd, - Fts5ExprNode *pExpr -){ - char *zRet = 0; - if( pExpr->eType==FTS5_STRING ){ - Fts5ExprNearset *pNear = pExpr->pNear; - int i; - int iTerm; - - zRet = fts5PrintfAppend(zRet, "[%s ", zNearsetCmd); - if( pNear->iCol>=0 ){ - zRet = fts5PrintfAppend(zRet, "-col %d ", pNear->iCol); - if( zRet==0 ) return 0; - } - - if( pNear->nPhrase>1 ){ - zRet = fts5PrintfAppend(zRet, "-near %d ", pNear->nNear); - if( zRet==0 ) return 0; - } - - zRet = fts5PrintfAppend(zRet, "--"); - if( zRet==0 ) return 0; - - for(i=0; inPhrase; i++){ - Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; - - zRet = fts5PrintfAppend(zRet, " {"); - for(iTerm=0; zRet && iTermnTerm; iTerm++){ - char *zTerm = pPhrase->aTerm[iTerm].zTerm; - zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); - } - - if( zRet ) zRet = fts5PrintfAppend(zRet, "}"); - if( zRet==0 ) return 0; - } - - if( zRet ) zRet = fts5PrintfAppend(zRet, "]"); - if( zRet==0 ) return 0; - - }else{ - char *zOp = 0; - char *z1 = 0; - char *z2 = 0; - switch( pExpr->eType ){ - case FTS5_AND: zOp = "&&"; break; - case FTS5_NOT: zOp = "&& !"; break; - case FTS5_OR: zOp = "||"; break; - default: assert( 0 ); - } - - z1 = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pLeft); - z2 = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRight); - if( z1 && z2 ){ - int b1 = pExpr->pLeft->eType!=FTS5_STRING; - int b2 = pExpr->pRight->eType!=FTS5_STRING; - zRet = sqlite3_mprintf("%s%s%s %s %s%s%s", - b1 ? "(" : "", z1, b1 ? ")" : "", - zOp, - b2 ? "(" : "", z2, b2 ? ")" : "" - ); - } - sqlite3_free(z1); - sqlite3_free(z2); - } - - return zRet; -} - -static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){ - char *zRet = 0; - if( pExpr->eType==FTS5_STRING ){ - Fts5ExprNearset *pNear = pExpr->pNear; - int i; - int iTerm; - - if( pNear->iCol>=0 ){ - zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[pNear->iCol]); - if( zRet==0 ) return 0; - } - - if( pNear->nPhrase>1 ){ - zRet = fts5PrintfAppend(zRet, "NEAR("); - if( zRet==0 ) return 0; - } - - for(i=0; inPhrase; i++){ - Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; - if( i!=0 ){ - zRet = fts5PrintfAppend(zRet, " "); - if( zRet==0 ) return 0; - } - for(iTerm=0; iTermnTerm; iTerm++){ - char *zTerm = fts5ExprTermPrint(&pPhrase->aTerm[iTerm]); - if( zTerm ){ - zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" + ", zTerm); - sqlite3_free(zTerm); - } - if( zTerm==0 || zRet==0 ){ - sqlite3_free(zRet); - return 0; - } - } - } - - if( pNear->nPhrase>1 ){ - zRet = fts5PrintfAppend(zRet, ", %d)", pNear->nNear); - if( zRet==0 ) return 0; - } - - }else{ - char *zOp = 0; - char *z1 = 0; - char *z2 = 0; - switch( pExpr->eType ){ - case FTS5_AND: zOp = "AND"; break; - case FTS5_NOT: zOp = "NOT"; break; - case FTS5_OR: zOp = "OR"; break; - default: assert( 0 ); - } - - z1 = fts5ExprPrint(pConfig, pExpr->pLeft); - z2 = fts5ExprPrint(pConfig, pExpr->pRight); - if( z1 && z2 ){ - int b1 = pExpr->pLeft->eType!=FTS5_STRING; - int b2 = pExpr->pRight->eType!=FTS5_STRING; - zRet = sqlite3_mprintf("%s%s%s %s %s%s%s", - b1 ? "(" : "", z1, b1 ? ")" : "", - zOp, - b2 ? "(" : "", z2, b2 ? ")" : "" - ); - } - sqlite3_free(z1); - sqlite3_free(z2); - } - - return zRet; -} - -/* -** The implementation of user-defined scalar functions fts5_expr() (bTcl==0) -** and fts5_expr_tcl() (bTcl!=0). -*/ -static void fts5ExprFunction( - sqlite3_context *pCtx, /* Function call context */ - int nArg, /* Number of args */ - sqlite3_value **apVal, /* Function arguments */ - int bTcl -){ - Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); - sqlite3 *db = sqlite3_context_db_handle(pCtx); - const char *zExpr = 0; - char *zErr = 0; - Fts5Expr *pExpr = 0; - int rc; - int i; - - const char **azConfig; /* Array of arguments for Fts5Config */ - const char *zNearsetCmd = "nearset"; - int nConfig; /* Size of azConfig[] */ - Fts5Config *pConfig = 0; - - if( bTcl && nArg>1 ){ - zNearsetCmd = (const char*)sqlite3_value_text(apVal[1]); - } - - nConfig = nArg + 2 - bTcl; - azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig); - if( azConfig==0 ){ - sqlite3_result_error_nomem(pCtx); - return; - } - azConfig[0] = 0; - azConfig[1] = "main"; - azConfig[2] = "tbl"; - for(i=1+bTcl; ipRoot); - }else{ - zText = fts5ExprPrint(pConfig, pExpr->pRoot); - } - if( rc==SQLITE_OK ){ - sqlite3_result_text(pCtx, zText, -1, SQLITE_TRANSIENT); - sqlite3_free(zText); - } - } - - if( rc!=SQLITE_OK ){ - if( zErr ){ - sqlite3_result_error(pCtx, zErr, -1); - sqlite3_free(zErr); - }else{ - sqlite3_result_error_code(pCtx, rc); - } - } - sqlite3_free(azConfig); - sqlite3Fts5ConfigFree(pConfig); - sqlite3Fts5ExprFree(pExpr); -} - -static void fts5ExprFunctionHr( - sqlite3_context *pCtx, /* Function call context */ - int nArg, /* Number of args */ - sqlite3_value **apVal /* Function arguments */ -){ - fts5ExprFunction(pCtx, nArg, apVal, 0); -} -static void fts5ExprFunctionTcl( - sqlite3_context *pCtx, /* Function call context */ - int nArg, /* Number of args */ - sqlite3_value **apVal /* Function arguments */ -){ - fts5ExprFunction(pCtx, nArg, apVal, 1); -} - -/* -** This is called during initialization to register the fts5_expr() scalar -** UDF with the SQLite handle passed as the only argument. -*/ -int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){ - struct Fts5ExprFunc { - const char *z; - void (*x)(sqlite3_context*,int,sqlite3_value**); - } aFunc[] = { - { "fts5_expr", fts5ExprFunctionHr }, - { "fts5_expr_tcl", fts5ExprFunctionTcl }, - }; - int i; - int rc = SQLITE_OK; - void *pCtx = (void*)pGlobal; - - for(i=0; rc==SQLITE_OK && i<(sizeof(aFunc) / sizeof(aFunc[0])); i++){ - struct Fts5ExprFunc *p = &aFunc[i]; - rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0); - } - - return rc; -} - -/* -** Return the number of phrases in expression pExpr. -*/ -int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){ - return pExpr->nPhrase; -} - -/* -** Return the number of terms in the iPhrase'th phrase in pExpr. -*/ -int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){ - if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0; - return pExpr->apExprPhrase[iPhrase]->nTerm; -} - -/* -** This function is used to access the current position list for phrase -** iPhrase. -*/ -int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){ - if( iPhrase>=0 && iPhrasenPhrase ){ - Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase]; - Fts5ExprNode *pNode = pPhrase->pNode; - if( pNode->bEof==0 && pNode->iRowid==pExpr->pRoot->iRowid ){ - *pa = pPhrase->poslist.p; - return pPhrase->poslist.n; - } - } - *pa = 0; - return 0; -} - DELETED ext/fts5/fts5_hash.c Index: ext/fts5/fts5_hash.c ================================================================== --- ext/fts5/fts5_hash.c +++ /dev/null @@ -1,410 +0,0 @@ -/* -** 2014 August 11 -** -** 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. -** -****************************************************************************** -** -*/ - -#include "fts5Int.h" - -typedef struct Fts5HashEntry Fts5HashEntry; - -/* -** This file contains the implementation of an in-memory hash table used -** to accumuluate "term -> doclist" content before it is flused to a level-0 -** segment. -*/ - - -struct Fts5Hash { - int *pnByte; /* Pointer to bytes counter */ - int nEntry; /* Number of entries currently in hash */ - int nSlot; /* Size of aSlot[] array */ - Fts5HashEntry **aSlot; /* Array of hash slots */ -}; - -/* -** Each entry in the hash table is represented by an object of the -** following type. Each object, its key (zKey[]) and its current data -** are stored in a single memory allocation. The position list data -** immediately follows the key data in memory. -** -** The data that follows the key is in a similar, but not identical format -** to the doclist data stored in the database. It is: -** -** * Rowid, as a varint -** * Position list, without 0x00 terminator. -** * Size of previous position list and rowid, as a 4 byte -** big-endian integer. -** -** iRowidOff: -** Offset of last rowid written to data area. Relative to first byte of -** structure. -** -** nData: -** Bytes of data written since iRowidOff. -*/ -struct Fts5HashEntry { - Fts5HashEntry *pNext; /* Next hash entry with same hash-key */ - - int nAlloc; /* Total size of allocation */ - int iRowidOff; /* Offset of last rowid written */ - int nData; /* Total bytes of data (incl. structure) */ - - int iCol; /* Column of last value written */ - int iPos; /* Position of last value written */ - i64 iRowid; /* Rowid of last value written */ - char zKey[0]; /* Nul-terminated entry key */ -}; - - -/* -** Allocate a new hash table. -*/ -int sqlite3Fts5HashNew(Fts5Hash **ppNew, int *pnByte){ - int rc = SQLITE_OK; - Fts5Hash *pNew; - - *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash)); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - int nByte; - memset(pNew, 0, sizeof(Fts5Hash)); - pNew->pnByte = pnByte; - - pNew->nSlot = 1024; - nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; - pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte); - if( pNew->aSlot==0 ){ - sqlite3_free(pNew); - *ppNew = 0; - rc = SQLITE_NOMEM; - }else{ - memset(pNew->aSlot, 0, nByte); - } - } - return rc; -} - -/* -** Free a hash table object. -*/ -void sqlite3Fts5HashFree(Fts5Hash *pHash){ - if( pHash ){ - sqlite3Fts5HashClear(pHash); - sqlite3_free(pHash->aSlot); - sqlite3_free(pHash); - } -} - -/* -** Empty (but do not delete) a hash table. -*/ -void sqlite3Fts5HashClear(Fts5Hash *pHash){ - int i; - for(i=0; inSlot; i++){ - if( pHash->aSlot[i] ){ - sqlite3_free(pHash->aSlot[i]); - pHash->aSlot[i] = 0; - } - } - pHash->nEntry = 0; -} - -static unsigned int fts5HashKey(int nSlot, const char *p, int n){ - int i; - unsigned int h = 13; - for(i=n-1; i>=0; i--){ - h = (h << 3) ^ h ^ p[i]; - } - return (h % nSlot); -} - -/* -** Resize the hash table by doubling the number of slots. -*/ -static int fts5HashResize(Fts5Hash *pHash){ - int nNew = pHash->nSlot*2; - int i; - Fts5HashEntry **apNew; - Fts5HashEntry **apOld = pHash->aSlot; - - apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*)); - if( !apNew ) return SQLITE_NOMEM; - memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); - - for(i=0; inSlot; i++){ - while( apOld[i] ){ - int iHash; - Fts5HashEntry *p = apOld[i]; - apOld[i] = p->pNext; - iHash = fts5HashKey(nNew, p->zKey, strlen(p->zKey)); - p->pNext = apNew[iHash]; - apNew[iHash] = p; - } - } - - sqlite3_free(apOld); - pHash->nSlot = nNew; - pHash->aSlot = apNew; - return SQLITE_OK; -} - -/* -** Store the 32-bit integer passed as the second argument in buffer p. -*/ -static int fts5PutNativeInt(u8 *p, int i){ - assert( sizeof(i)==4 ); - memcpy(p, &i, sizeof(i)); - return sizeof(i); -} - -/* -** Read and return the 32-bit integer stored in buffer p. -*/ -static int fts5GetNativeU32(u8 *p){ - int i; - assert( sizeof(i)==4 ); - memcpy(&i, p, sizeof(i)); - return i; -} - -int sqlite3Fts5HashWrite( - Fts5Hash *pHash, - i64 iRowid, /* Rowid for this entry */ - int iCol, /* Column token appears in (-ve -> delete) */ - int iPos, /* Position of token within column */ - const char *pToken, int nToken /* Token to add or remove to or from index */ -){ - unsigned int iHash = fts5HashKey(pHash->nSlot, pToken, nToken); - Fts5HashEntry *p; - u8 *pPtr; - int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */ - - /* Attempt to locate an existing hash object */ - for(p=pHash->aSlot[iHash]; p; p=p->pNext){ - if( memcmp(p->zKey, pToken, nToken)==0 && p->zKey[nToken]==0 ) break; - } - - /* If an existing hash entry cannot be found, create a new one. */ - if( p==0 ){ - int nByte = sizeof(Fts5HashEntry) + nToken + 1 + 64; - if( nByte<128 ) nByte = 128; - - if( (pHash->nEntry*2)>=pHash->nSlot ){ - int rc = fts5HashResize(pHash); - if( rc!=SQLITE_OK ) return rc; - iHash = fts5HashKey(pHash->nSlot, pToken, nToken); - } - - p = (Fts5HashEntry*)sqlite3_malloc(nByte); - if( !p ) return SQLITE_NOMEM; - memset(p, 0, sizeof(Fts5HashEntry)); - p->nAlloc = nByte; - memcpy(p->zKey, pToken, nToken); - p->zKey[nToken] = '\0'; - p->iRowidOff = p->nData = nToken + 1 + sizeof(Fts5HashEntry); - p->nData += sqlite3PutVarint(&((u8*)p)[p->nData], iRowid); - p->iRowid = iRowid; - p->pNext = pHash->aSlot[iHash]; - pHash->aSlot[iHash] = p; - pHash->nEntry++; - - nIncr += p->nData; - } - - /* Check there is enough space to append a new entry. Worst case scenario - ** is: - ** - ** + 4 bytes for the previous entry size field, - ** + 9 bytes for a new rowid, - ** + 1 byte for a "new column" byte, - ** + 3 bytes for a new column number (16-bit max) as a varint, - ** + 5 bytes for the new position offset (32-bit max). - */ - if( (p->nAlloc - p->nData) < (4 + 9 + 1 + 3 + 5) ){ - int nNew = p->nAlloc * 2; - Fts5HashEntry *pNew; - Fts5HashEntry **pp; - pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); - if( pNew==0 ) return SQLITE_NOMEM; - pNew->nAlloc = nNew; - for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pNext); - *pp = pNew; - p = pNew; - } - pPtr = (u8*)p; - nIncr -= p->nData; - - /* If this is a new rowid, append the 4-byte size field for the previous - ** entry, and the new rowid for this entry. */ - if( iRowid!=p->iRowid ){ - p->nData += fts5PutNativeInt(&pPtr[p->nData], p->nData - p->iRowidOff); - p->iRowidOff = p->nData; - p->nData += sqlite3PutVarint(&pPtr[p->nData], iRowid); - p->iCol = 0; - p->iPos = 0; - p->iRowid = iRowid; - } - - if( iCol>=0 ){ - /* Append a new column value, if necessary */ - assert( iCol>=p->iCol ); - if( iCol!=p->iCol ){ - pPtr[p->nData++] = 0x01; - p->nData += sqlite3PutVarint(&pPtr[p->nData], iCol); - p->iCol = iCol; - p->iPos = 0; - } - - /* Append the new position offset */ - p->nData += sqlite3PutVarint(&pPtr[p->nData], iPos - p->iPos + 2); - p->iPos = iPos; - } - nIncr += p->nData; - - *pHash->pnByte += nIncr; - return SQLITE_OK; -} - - -/* -** Arguments pLeft and pRight point to linked-lists of hash-entry objects, -** each sorted in key order. This function merges the two lists into a -** single list and returns a pointer to its first element. -*/ -static Fts5HashEntry *fts5HashEntryMerge( - Fts5HashEntry *pLeft, - Fts5HashEntry *pRight -){ - Fts5HashEntry *p1 = pLeft; - Fts5HashEntry *p2 = pRight; - Fts5HashEntry *pRet = 0; - Fts5HashEntry **ppOut = &pRet; - - while( p1 || p2 ){ - if( p1==0 ){ - *ppOut = p2; - p2 = 0; - }else if( p2==0 ){ - *ppOut = p1; - p1 = 0; - }else{ - int i = 0; - while( p1->zKey[i]==p2->zKey[i] ) i++; - - if( ((u8)p1->zKey[i])>((u8)p2->zKey[i]) ){ - /* p2 is smaller */ - *ppOut = p2; - ppOut = &p2->pNext; - p2 = p2->pNext; - }else{ - /* p1 is smaller */ - *ppOut = p1; - ppOut = &p1->pNext; - p1 = p1->pNext; - } - *ppOut = 0; - } - } - - return pRet; -} - -/* -** Extract all tokens from hash table iHash and link them into a list -** in sorted order. The hash table is cleared before returning. It is -** the responsibility of the caller to free the elements of the returned -** list. -*/ -static int fts5HashEntrySort(Fts5Hash *pHash, Fts5HashEntry **ppSorted){ - const int nMergeSlot = 32; - Fts5HashEntry **ap; - Fts5HashEntry *pList; - int iSlot; - int i; - - *ppSorted = 0; - ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot); - if( !ap ) return SQLITE_NOMEM; - memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot); - - for(iSlot=0; iSlotnSlot; iSlot++){ - while( pHash->aSlot[iSlot] ){ - Fts5HashEntry *pEntry = pHash->aSlot[iSlot]; - pHash->aSlot[iSlot] = pEntry->pNext; - pEntry->pNext = 0; - for(i=0; ap[i]; i++){ - pEntry = fts5HashEntryMerge(pEntry, ap[i]); - ap[i] = 0; - } - ap[i] = pEntry; - } - } - - pList = 0; - for(i=0; inEntry = 0; - sqlite3_free(ap); - *ppSorted = pList; - return SQLITE_OK; -} - -int sqlite3Fts5HashIterate( - Fts5Hash *pHash, - void *pCtx, - int (*xTerm)(void*, const char*, int), - int (*xEntry)(void*, i64, const u8*, int), - int (*xTermDone)(void*) -){ - Fts5HashEntry *pList; - int rc; - - rc = fts5HashEntrySort(pHash, &pList); - if( rc==SQLITE_OK ){ - while( pList ){ - Fts5HashEntry *pNext = pList->pNext; - if( rc==SQLITE_OK ){ - u8 *pPtr = (u8*)pList; - int nKey = strlen(pList->zKey); - int iOff = pList->iRowidOff; - int iEnd = sizeof(Fts5HashEntry) + nKey + 1; - int nByte = pList->nData - pList->iRowidOff; - - rc = xTerm(pCtx, pList->zKey, nKey); - while( rc==SQLITE_OK && iOff ){ - int nVarint; - i64 iRowid; - nVarint = getVarint(&pPtr[iOff], (u64*)&iRowid); - rc = xEntry(pCtx, iRowid, &pPtr[iOff+nVarint], nByte-nVarint); - if( iOff==iEnd ){ - iOff = 0; - }else{ - nByte = fts5GetNativeU32(&pPtr[iOff-sizeof(int)]); - iOff = iOff - sizeof(int) - nByte; - } - } - if( rc==SQLITE_OK ){ - rc = xTermDone(pCtx); - } - } - sqlite3_free(pList); - pList = pNext; - } - } - return rc; -} - - - DELETED ext/fts5/fts5_index.c Index: ext/fts5/fts5_index.c ================================================================== --- ext/fts5/fts5_index.c +++ /dev/null @@ -1,4711 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -** -** Low level access to the FTS index stored in the database file. The -** routines in this file file implement all read and write access to the -** %_data table. Other parts of the system access this functionality via -** the interface defined in fts5Int.h. -*/ - -#include "fts5Int.h" - -/* -** Overview: -** -** The %_data table contains all the FTS indexes for an FTS5 virtual table. -** As well as the main term index, there may be up to 31 prefix indexes. -** The format is similar to FTS3/4, except that: -** -** * all segment b-tree leaf data is stored in fixed size page records -** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is -** taken to ensure it is possible to iterate in either direction through -** the entries in a doclist, or to seek to a specific entry within a -** doclist, without loading it into memory. -** -** * large doclists that span many pages have associated "doclist index" -** records that contain a copy of the first docid on each page spanned by -** the doclist. This is used to speed up seek operations, and merges of -** large doclists with very small doclists. -** -** * extra fields in the "structure record" record the state of ongoing -** incremental merge operations. -** -*/ - -#define FTS5_OPT_WORK_UNIT 1000 /* Number of leaf pages per optimize step */ -#define FTS5_WORK_UNIT 64 /* Number of leaf pages in unit of work */ -#define FTS5_CRISIS_MERGE 16 /* Maximum number of segments to merge */ - -#define FTS5_MIN_DLIDX_SIZE 4 /* Add dlidx if this many empty pages */ - -/* -** Details: -** -** The %_data table managed by this module, -** -** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB); -** -** , contains the following 5 types of records. See the comments surrounding -** the FTS5_*_ROWID macros below for a description of how %_data rowids are -** assigned to each fo them. -** -** 1. Structure Records: -** -** The set of segments that make up an index - the index structure - are -** recorded in a single record within the %_data table. The record consists -** of a single 32-bit configuration cookie value followed by a list of -** SQLite varints. If the FTS table features more than one index (because -** there are one or more prefix indexes), it is guaranteed that all share -** the same cookie value. -** -** Immediately following the configuration cookie, the record begins with -** three varints: -** -** + number of levels, -** + total number of segments on all levels, -** + value of write counter. -** -** Then, for each level from 0 to nMax: -** -** + number of input segments in ongoing merge. -** + total number of segments in level. -** + for each segment from oldest to newest: -** + segment id (always > 0) -** + b-tree height (1 -> root is leaf, 2 -> root is parent of leaf etc.) -** + first leaf page number (often 1, always greater than 0) -** + final leaf page number -** -** 2. The Averages Record: -** -** A single record within the %_data table. The data is a list of varints. -** The first value is the number of rows in the index. Then, for each column -** from left to right, the total number of tokens in the column for all -** rows of the table. -** -** 3. Segment leaves: -** -** TERM DOCLIST FORMAT: -** -** Most of each segment leaf is taken up by term/doclist data. The -** general format of the term/doclist data is: -** -** varint : size of first term -** blob: first term data -** doclist: first doclist -** zero-or-more { -** varint: number of bytes in common with previous term -** varint: number of bytes of new term data (nNew) -** blob: nNew bytes of new term data -** doclist: next doclist -** } -** -** doclist format: -** -** varint: first rowid -** poslist: first poslist -** zero-or-more { -** varint: rowid delta (always > 0) -** poslist: first poslist -** } -** 0x00 byte -** -** poslist format: -** -** varint: size of poslist in bytes. not including this field. -** collist: collist for column 0 -** zero-or-more { -** 0x01 byte -** varint: column number (I) -** collist: collist for column I -** } -** -** collist format: -** -** varint: first offset + 2 -** zero-or-more { -** varint: offset delta + 2 -** } -** -** PAGINATION -** -** The format described above is only accurate if the entire term/doclist -** data fits on a single leaf page. If this is not the case, the format -** is changed in two ways: -** -** + if the first rowid on a page occurs before the first term, it -** is stored as a literal value: -** -** varint: first rowid -** -** + the first term on each page is stored in the same way as the -** very first term of the segment: -** -** varint : size of first term -** blob: first term data -** -** Each leaf page begins with: -** -** + 2-byte unsigned containing offset to first rowid (or 0). -** + 2-byte unsigned containing offset to first term (or 0). -** -** Followed by term/doclist data. -** -** 4. Segment interior nodes: -** -** The interior nodes turn the list of leaves into a b+tree. -** -** Each interior node begins with a varint - the page number of the left -** most child node. Following this, for each leaf page except the first, -** the interior nodes contain: -** -** a) If the leaf page contains at least one term, then a term-prefix that -** is greater than all previous terms, and less than or equal to the -** first term on the leaf page. -** -** b) If the leaf page no terms, a record indicating how many consecutive -** leaves contain no terms, and whether or not there is an associated -** by-rowid index record. -** -** By definition, there is never more than one type (b) record in a row. -** Type (b) records only ever appear on height=1 pages - immediate parents -** of leaves. Only type (a) records are pushed to higher levels. -** -** Term format: -** -** * Number of bytes in common with previous term plus 2, as a varint. -** * Number of bytes of new term data, as a varint. -** * new term data. -** -** No-term format: -** -** * either an 0x00 or 0x01 byte. If the value 0x01 is used, then there -** is an associated index-by-rowid record. -** * the number of zero-term leaves as a varint. -** -** 5. Segment doclist indexes: -** -** A list of varints - the first docid on each page (starting with the -** first termless page) of the doclist. First element in the list is a -** literal docid. Each docid thereafter is a (negative) delta. If there -** are no docids at all on a page, a 0x00 byte takes the place of the -** delta value. -*/ - -/* -** Rowids for the averages and structure records in the %_data table. -*/ -#define FTS5_AVERAGES_ROWID 1 /* Rowid used for the averages record */ -#define FTS5_STRUCTURE_ROWID(iIdx) (10 + (iIdx)) /* For structure records */ - -/* -** Macros determining the rowids used by segment nodes. All nodes in all -** segments for all indexes (the regular FTS index and any prefix indexes) -** are stored in the %_data table with large positive rowids. -** -** The %_data table may contain up to (1< ((1<=? AND id<=?" */ - int nRead; /* Total number of blocks read */ -}; - -struct Fts5DoclistIter { - int bAsc; - u8 *a; - int n; - int i; - - /* Output variables. aPoslist==0 at EOF */ - i64 iRowid; - u8 *aPoslist; - int nPoslist; -}; - -/* -** Each iterator used by external modules is an instance of this type. -*/ -struct Fts5IndexIter { - Fts5Index *pIndex; - Fts5Structure *pStruct; - Fts5MultiSegIter *pMulti; - Fts5DoclistIter *pDoclist; - Fts5Buffer poslist; /* Buffer containing current poslist */ -}; - -/* -** A single record read from the %_data table. -*/ -struct Fts5Data { - u8 *p; /* Pointer to buffer containing record */ - int n; /* Size of record in bytes */ - int nRef; /* Ref count */ -}; - -/* -** The contents of the "structure" record for each index are represented -** using an Fts5Structure record in memory. Which uses instances of the -** other Fts5StructureXXX types as components. -*/ -struct Fts5StructureSegment { - int iSegid; /* Segment id */ - int nHeight; /* Height of segment b-tree */ - int pgnoFirst; /* First leaf page number in segment */ - int pgnoLast; /* Last leaf page number in segment */ -}; -struct Fts5StructureLevel { - int nMerge; /* Number of segments in incr-merge */ - int nSeg; /* Total number of segments on level */ - Fts5StructureSegment *aSeg; /* Array of segments. aSeg[0] is oldest. */ -}; -struct Fts5Structure { - u64 nWriteCounter; /* Total leaves written to level 0 */ - int nLevel; /* Number of levels in this index */ - Fts5StructureLevel aLevel[0]; /* Array of nLevel level objects */ -}; - -/* -** An object of type Fts5SegWriter is used to write to segments. -*/ -struct Fts5PageWriter { - int pgno; /* Page number for this page */ - Fts5Buffer buf; /* Buffer containing page data */ - Fts5Buffer term; /* Buffer containing previous term on page */ -}; -struct Fts5SegWriter { - int iIdx; /* Index to write to */ - int iSegid; /* Segid to write to */ - int nWriter; /* Number of entries in aWriter */ - Fts5PageWriter *aWriter; /* Array of PageWriter objects */ - i64 iPrevRowid; /* Previous docid written to current leaf */ - u8 bFirstRowidInDoclist; /* True if next rowid is first in doclist */ - u8 bFirstRowidInPage; /* True if next rowid is first in page */ - int nLeafWritten; /* Number of leaf pages written */ - int nEmpty; /* Number of contiguous term-less nodes */ - Fts5Buffer dlidx; /* Doclist index */ - i64 iDlidxPrev; /* Previous rowid appended to dlidx */ - int bDlidxPrevValid; /* True if iDlidxPrev is valid */ -}; - -/* -** Object for iterating through the merged results of one or more segments, -** visiting each term/docid pair in the merged data. -** -** nSeg is always a power of two greater than or equal to the number of -** segments that this object is merging data from. Both the aSeg[] and -** aFirst[] arrays are sized at nSeg entries. The aSeg[] array is padded -** with zeroed objects - these are handled as if they were iterators opened -** on empty segments. -** -** The results of comparing segments aSeg[N] and aSeg[N+1], where N is an -** even number, is stored in aFirst[(nSeg+N)/2]. The "result" of the -** comparison in this context is the index of the iterator that currently -** points to the smaller term/rowid combination. Iterators at EOF are -** considered to be greater than all other iterators. -** -** aFirst[1] contains the index in aSeg[] of the iterator that points to -** the smallest key overall. aFirst[0] is unused. -*/ -struct Fts5MultiSegIter { - int nSeg; /* Size of aSeg[] array */ - int bRev; /* True to iterate in reverse order */ - int bSkipEmpty; /* True to skip deleted entries */ - Fts5SegIter *aSeg; /* Array of segment iterators */ - u16 *aFirst; /* Current merge state (see above) */ -}; - -/* -** Object for iterating through a single segment, visiting each term/docid -** pair in the segment. -** -** pSeg: -** The segment to iterate through. -** -** iLeafPgno: -** Current leaf page number within segment. -** -** iLeafOffset: -** Byte offset within the current leaf that is one byte past the end of the -** rowid field of the current entry. Usually this is the size field of the -** position list data. The exception is if the rowid for the current entry -** is the last thing on the leaf page. -** -** pLeaf: -** Buffer containing current leaf page data. Set to NULL at EOF. -** -** iTermLeafPgno, iTermLeafOffset: -** Leaf page number containing the last term read from the segment. And -** the offset immediately following the term data. -** -** flags: -** Mask of FTS5_SEGITER_XXX values. Interpreted as follows: -** -** FTS5_SEGITER_ONETERM: -** If set, set the iterator to point to EOF after the current doclist -** has been exhausted. Do not proceed to the next term in the segment. -** -** FTS5_SEGITER_REVERSE: -** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If -** it is set, iterate through docids in ascending order instead of the -** default descending order. -** -** iRowidOffset/nRowidOffset/aRowidOffset: -** These are used if the FTS5_SEGITER_REVERSE flag is set. -** -** Each time a new page is loaded, the iterator is set to point to the -** final rowid. Additionally, the aRowidOffset[] array is populated -** with the byte offsets of all relevant rowid fields on the page. -*/ -struct Fts5SegIter { - Fts5StructureSegment *pSeg; /* Segment to iterate through */ - int iIdx; /* Byte offset within current leaf */ - int flags; /* Mask of configuration flags */ - int iLeafPgno; /* Current leaf page number */ - Fts5Data *pLeaf; /* Current leaf data */ - int iLeafOffset; /* Byte offset within current leaf */ - - /* The page and offset from which the current term was read. The offset - ** is the offset of the first rowid in the current doclist. */ - int iTermLeafPgno; - int iTermLeafOffset; - - /* The following are only used if the FTS5_SEGITER_REVERSE flag is set. */ - int iRowidOffset; /* Current entry in aRowidOffset[] */ - int nRowidOffset; /* Allocated size of aRowidOffset[] array */ - int *aRowidOffset; /* Array of offset to rowid fields */ - - Fts5DlidxIter *pDlidx; /* If there is a doclist-index */ - - /* Variables populated based on current entry. */ - Fts5Buffer term; /* Current term */ - i64 iRowid; /* Current rowid */ -}; - -#define FTS5_SEGITER_ONETERM 0x01 -#define FTS5_SEGITER_REVERSE 0x02 - - -/* -** Object for iterating through paginated data. -*/ -struct Fts5ChunkIter { - Fts5Data *pLeaf; /* Current leaf data. NULL -> EOF. */ - i64 iLeafRowid; /* Absolute rowid of current leaf */ - int nRem; /* Remaining bytes of data to read */ - - /* Output parameters */ - u8 *p; /* Pointer to chunk of data */ - int n; /* Size of buffer p in bytes */ -}; - -/* -** Object for iterating through a single position list on disk. -*/ -struct Fts5PosIter { - Fts5ChunkIter chunk; /* Current chunk of data */ - int iOff; /* Offset within chunk data */ - - int iCol; - int iPos; -}; - -/* -** Object for iterating through the conents of a single internal node in -** memory. -*/ -struct Fts5NodeIter { - /* Internal. Set and managed by fts5NodeIterXXX() functions. Except, - ** the EOF test for the iterator is (Fts5NodeIter.aData==0). */ - const u8 *aData; - int nData; - int iOff; - - /* Output variables */ - Fts5Buffer term; - int nEmpty; - int iChild; - int bDlidx; -}; - -/* -** An instance of the following type is used to iterate through the contents -** of a doclist-index record. -** -** pData: -** Record containing the doclist-index data. -** -** bEof: -** Set to true once iterator has reached EOF. -** -** iOff: -** Set to the current offset within record pData. -*/ -struct Fts5DlidxIter { - Fts5Data *pData; /* Data for doclist index, if any */ - int iOff; /* Current offset into pDlidx */ - int bEof; /* At EOF already */ - int iFirstOff; /* Used by reverse iterators only */ - - /* Output variables */ - int iLeafPgno; /* Page number of current leaf page */ - i64 iRowid; /* First rowid on leaf iLeafPgno */ -}; - - -/* -** An Fts5BtreeIter object is used to iterate through all entries in the -** b-tree hierarchy belonging to a single fts5 segment. In this case the -** "b-tree hierarchy" is all b-tree nodes except leaves. Each entry in the -** b-tree hierarchy consists of the following: -** -** iLeaf: The page number of the leaf page the entry points to. -** -** term: A split-key that all terms on leaf page $iLeaf must be greater -** than or equal to. The "term" associated with the first b-tree -** hierarchy entry (the one that points to leaf page 1) is always -** an empty string. -** -** nEmpty: The number of empty (termless) leaf pages that immediately -** following iLeaf. -** -** The Fts5BtreeIter object is only used as part of the integrity-check code. -*/ -struct Fts5BtreeIterLevel { - Fts5NodeIter s; /* Iterator for the current node */ - Fts5Data *pData; /* Data for the current node */ -}; -struct Fts5BtreeIter { - Fts5Index *p; /* FTS5 backend object */ - Fts5StructureSegment *pSeg; /* Iterate through this segment's b-tree */ - int iIdx; /* Index pSeg belongs to */ - int nLvl; /* Size of aLvl[] array */ - Fts5BtreeIterLevel *aLvl; /* Level for each tier of b-tree */ - - /* Output variables */ - Fts5Buffer term; /* Current term */ - int iLeaf; /* Leaf containing terms >= current term */ - int nEmpty; /* Number of "empty" leaves following iLeaf */ - int bEof; /* Set to true at EOF */ - int bDlidx; /* True if there exists a dlidx */ -}; - - -static void fts5PutU16(u8 *aOut, u16 iVal){ - aOut[0] = (iVal>>8); - aOut[1] = (iVal&0xFF); -} - -static u16 fts5GetU16(const u8 *aIn){ - return ((u16)aIn[0] << 8) + aIn[1]; -} - -/* -** Allocate and return a buffer at least nByte bytes in size. -** -** If an OOM error is encountered, return NULL and set the error code in -** the Fts5Index handle passed as the first argument. -*/ -static void *fts5IdxMalloc(Fts5Index *p, int nByte){ - void *pRet = 0; - if( p->rc==SQLITE_OK ){ - pRet = sqlite3_malloc(nByte); - if( pRet==0 ){ - p->rc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, nByte); - } - } - return pRet; -} - -/* -** Compare the contents of the pLeft buffer with the pRight/nRight blob. -** -** Return -ve if pLeft is smaller than pRight, 0 if they are equal or -** +ve if pRight is smaller than pLeft. In other words: -** -** res = *pLeft - *pRight -*/ -static int fts5BufferCompareBlob( - Fts5Buffer *pLeft, /* Left hand side of comparison */ - const u8 *pRight, int nRight /* Right hand side of comparison */ -){ - int nCmp = MIN(pLeft->n, nRight); - int res = memcmp(pLeft->p, pRight, nCmp); - return (res==0 ? (pLeft->n - nRight) : res); -} - -#if 0 -static int fts5CompareBlob( - const u8 *pLeft, int nLeft, - const u8 *pRight, int nRight -){ - int nCmp = MIN(nLeft, nRight); - int res = memcmp(pLeft, pRight, nCmp); - return (res==0 ? (nLeft - nRight) : res); -} -#endif - -/* -** Compare the contents of the two buffers using memcmp(). If one buffer -** is a prefix of the other, it is considered the lesser. -** -** Return -ve if pLeft is smaller than pRight, 0 if they are equal or -** +ve if pRight is smaller than pLeft. In other words: -** -** res = *pLeft - *pRight -*/ -static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ - int nCmp = MIN(pLeft->n, pRight->n); - int res = memcmp(pLeft->p, pRight->p, nCmp); - return (res==0 ? (pLeft->n - pRight->n) : res); -} - - -/* -** Close the read-only blob handle, if it is open. -*/ -static void fts5CloseReader(Fts5Index *p){ - if( p->pReader ){ - sqlite3_blob *pReader = p->pReader; - p->pReader = 0; - sqlite3_blob_close(pReader); - } -} - -static Fts5Data *fts5DataReadOrBuffer( - Fts5Index *p, - Fts5Buffer *pBuf, - i64 iRowid -){ - Fts5Data *pRet = 0; - if( p->rc==SQLITE_OK ){ - int rc = SQLITE_OK; - -#if 0 -Fts5Buffer buf = {0,0,0}; -fts5DebugRowid(&rc, &buf, iRowid); -fprintf(stdout, "read: %s\n", buf.p); -fflush(stdout); -sqlite3_free(buf.p); -#endif - if( p->pReader ){ - /* This call may return SQLITE_ABORT if there has been a savepoint - ** rollback since it was last used. In this case a new blob handle - ** is required. */ - rc = sqlite3_blob_reopen(p->pReader, iRowid); - if( rc==SQLITE_ABORT ){ - fts5CloseReader(p); - rc = SQLITE_OK; - } - } - - /* If the blob handle is not yet open, open and seek it. Otherwise, use - ** the blob_reopen() API to reseek the existing blob handle. */ - if( p->pReader==0 ){ - Fts5Config *pConfig = p->pConfig; - rc = sqlite3_blob_open(pConfig->db, - pConfig->zDb, p->zDataTbl, "block", iRowid, 0, &p->pReader - ); - } - - if( rc==SQLITE_OK ){ - int nByte = sqlite3_blob_bytes(p->pReader); - if( pBuf ){ - fts5BufferZero(pBuf); - if( SQLITE_OK==fts5BufferGrow(&rc, pBuf, nByte) ){ - rc = sqlite3_blob_read(p->pReader, pBuf->p, nByte, 0); - if( rc==SQLITE_OK ) pBuf->n = nByte; - } - }else{ - pRet = (Fts5Data*)fts5IdxMalloc(p, sizeof(Fts5Data) + nByte); - if( !pRet ) return 0; - - pRet->n = nByte; - pRet->p = (u8*)&pRet[1]; - pRet->nRef = 1; - rc = sqlite3_blob_read(p->pReader, pRet->p, nByte, 0); - if( rc!=SQLITE_OK ){ - sqlite3_free(pRet); - pRet = 0; - } - } - } - p->rc = rc; - p->nRead++; - } - - return pRet; -} - -/* -** Retrieve a record from the %_data table. -** -** If an error occurs, NULL is returned and an error left in the -** Fts5Index object. -*/ -static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ - Fts5Data *pRet = fts5DataReadOrBuffer(p, 0, iRowid); - assert( (pRet==0)==(p->rc!=SQLITE_OK) ); - return pRet; -} - -/* -** Read a record from the %_data table into the buffer supplied as the -** second argument. -** -** If an error occurs, an error is left in the Fts5Index object. If an -** error has already occurred when this function is called, it is a -** no-op. -*/ -static void fts5DataBuffer(Fts5Index *p, Fts5Buffer *pBuf, i64 iRowid){ - (void)fts5DataReadOrBuffer(p, pBuf, iRowid); -} - -/* -** Release a reference to data record returned by an earlier call to -** fts5DataRead(). -*/ -static void fts5DataRelease(Fts5Data *pData){ - if( pData ){ - assert( pData->nRef>0 ); - pData->nRef--; - if( pData->nRef==0 ) sqlite3_free(pData); - } -} - -static void fts5DataReference(Fts5Data *pData){ - pData->nRef++; -} - -/* -** INSERT OR REPLACE a record into the %_data table. -*/ -static void fts5DataWrite(Fts5Index *p, i64 iRowid, const u8 *pData, int nData){ - if( p->rc!=SQLITE_OK ) return; - - if( p->pWriter==0 ){ - int rc; - Fts5Config *pConfig = p->pConfig; - char *zSql = sqlite3_mprintf( - "REPLACE INTO '%q'.%Q(id, block) VALUES(?,?)", pConfig->zDb, p->zDataTbl - ); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p->pWriter, 0); - sqlite3_free(zSql); - } - if( rc!=SQLITE_OK ){ - p->rc = rc; - return; - } - } - - sqlite3_bind_int64(p->pWriter, 1, iRowid); - sqlite3_bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC); - sqlite3_step(p->pWriter); - p->rc = sqlite3_reset(p->pWriter); -} - -/* -** Execute the following SQL: -** -** DELETE FROM %_data WHERE id BETWEEN $iFirst AND $iLast -*/ -static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ - if( p->rc!=SQLITE_OK ) return; - - if( p->pDeleter==0 ){ - int rc; - Fts5Config *pConfig = p->pConfig; - char *zSql = sqlite3_mprintf( - "DELETE FROM '%q'.%Q WHERE id>=? AND id<=?", pConfig->zDb, p->zDataTbl - ); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p->pDeleter, 0); - sqlite3_free(zSql); - } - if( rc!=SQLITE_OK ){ - p->rc = rc; - return; - } - } - - sqlite3_bind_int64(p->pDeleter, 1, iFirst); - sqlite3_bind_int64(p->pDeleter, 2, iLast); - sqlite3_step(p->pDeleter); - p->rc = sqlite3_reset(p->pDeleter); -} - -/* -** Close the sqlite3_blob handle used to read records from the %_data table. -** And discard any cached reads. This function is called at the end of -** a read transaction or when any sub-transaction is rolled back. -*/ -#if 0 -static void fts5DataReset(Fts5Index *p){ - if( p->pReader ){ - sqlite3_blob_close(p->pReader); - p->pReader = 0; - } -} -#endif - -/* -** Remove all records associated with segment iSegid in index iIdx. -*/ -static void fts5DataRemoveSegment(Fts5Index *p, int iIdx, int iSegid){ - i64 iFirst = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, 0); - i64 iLast = FTS5_SEGMENT_ROWID(iIdx, iSegid+1, 0, 0)-1; - fts5DataDelete(p, iFirst, iLast); -} - -/* -** Release a reference to an Fts5Structure object returned by an earlier -** call to fts5StructureRead() or fts5StructureDecode(). -*/ -static void fts5StructureRelease(Fts5Structure *pStruct){ - if( pStruct ){ - int i; - for(i=0; inLevel; i++){ - sqlite3_free(pStruct->aLevel[i].aSeg); - } - sqlite3_free(pStruct); - } -} - -/* -** Deserialize and return the structure record currently stored in serialized -** form within buffer pData/nData. -** -** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array -** are over-allocated by one slot. This allows the structure contents -** to be more easily edited. -** -** If an error occurs, *ppOut is set to NULL and an SQLite error code -** returned. Otherwise, *ppOut is set to point to the new object and -** SQLITE_OK returned. -*/ -static int fts5StructureDecode( - const u8 *pData, /* Buffer containing serialized structure */ - int nData, /* Size of buffer pData in bytes */ - int *piCookie, /* Configuration cookie value */ - Fts5Structure **ppOut /* OUT: Deserialized object */ -){ - int rc = SQLITE_OK; - int i = 0; - int iLvl; - int nLevel = 0; - int nSegment = 0; - int nByte; /* Bytes of space to allocate at pRet */ - Fts5Structure *pRet = 0; /* Structure object to return */ - - /* Grab the cookie value */ - if( piCookie ) *piCookie = sqlite3Fts5Get32(pData); - i = 4; - - /* Read the total number of levels and segments from the start of the - ** structure record. */ - i += getVarint32(&pData[i], nLevel); - i += getVarint32(&pData[i], nSegment); - nByte = ( - sizeof(Fts5Structure) + /* Main structure */ - sizeof(Fts5StructureLevel) * (nLevel) /* aLevel[] array */ - ); - pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); - - if( pRet ){ - pRet->nLevel = nLevel; - i += sqlite3GetVarint(&pData[i], &pRet->nWriteCounter); - - for(iLvl=0; rc==SQLITE_OK && iLvlaLevel[iLvl]; - int nTotal; - int iSeg; - - i += getVarint32(&pData[i], pLvl->nMerge); - i += getVarint32(&pData[i], nTotal); - assert( nTotal>=pLvl->nMerge ); - pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, - nTotal * sizeof(Fts5StructureSegment) - ); - - if( rc==SQLITE_OK ){ - pLvl->nSeg = nTotal; - for(iSeg=0; iSegaSeg[iSeg].iSegid); - i += getVarint32(&pData[i], pLvl->aSeg[iSeg].nHeight); - i += getVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst); - i += getVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast); - } - }else{ - fts5StructureRelease(pRet); - pRet = 0; - } - } - } - - *ppOut = pRet; - return rc; -} - -/* -** -*/ -static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ - if( *pRc==SQLITE_OK ){ - Fts5Structure *pStruct = *ppStruct; - int nLevel = pStruct->nLevel; - int nByte = ( - sizeof(Fts5Structure) + /* Main structure */ - sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ - ); - - pStruct = sqlite3_realloc(pStruct, nByte); - if( pStruct ){ - memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); - pStruct->nLevel++; - *ppStruct = pStruct; - }else{ - *pRc = SQLITE_NOMEM; - } - } -} - -/* -** Extend level iLvl so that there is room for at least nExtra more -** segments. -*/ -static void fts5StructureExtendLevel( - int *pRc, - Fts5Structure *pStruct, - int iLvl, - int nExtra, - int bInsert -){ - if( *pRc==SQLITE_OK ){ - Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; - Fts5StructureSegment *aNew; - int nByte; - - nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment); - aNew = sqlite3_realloc(pLvl->aSeg, nByte); - if( aNew ){ - if( bInsert==0 ){ - memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra); - }else{ - int nMove = pLvl->nSeg * sizeof(Fts5StructureSegment); - memmove(&aNew[nExtra], aNew, nMove); - memset(aNew, 0, sizeof(Fts5StructureSegment) * nExtra); - } - pLvl->aSeg = aNew; - }else{ - *pRc = SQLITE_NOMEM; - } - } -} - -/* -** Read, deserialize and return the structure record for index iIdx. -** -** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array -** are over-allocated as described for function fts5StructureDecode() -** above. -** -** If an error occurs, NULL is returned and an error code left in the -** Fts5Index handle. If an error has already occurred when this function -** is called, it is a no-op. -*/ -static Fts5Structure *fts5StructureRead(Fts5Index *p, int iIdx){ - Fts5Config *pConfig = p->pConfig; - Fts5Structure *pRet = 0; /* Object to return */ - Fts5Data *pData; /* %_data entry containing structure record */ - int iCookie; /* Configuration cookie */ - - assert( iIdx<=pConfig->nPrefix ); - pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID(iIdx)); - if( !pData ) return 0; - p->rc = fts5StructureDecode(pData->p, pData->n, &iCookie, &pRet); - - if( p->rc==SQLITE_OK && p->pConfig->iCookie!=iCookie ){ - p->rc = sqlite3Fts5ConfigLoad(p->pConfig, iCookie); - } - - fts5DataRelease(pData); - if( p->rc!=SQLITE_OK ){ - fts5StructureRelease(pRet); - pRet = 0; - } - return pRet; -} - -/* -** Return the total number of segments in index structure pStruct. -*/ -static int fts5StructureCountSegments(Fts5Structure *pStruct){ - int nSegment = 0; /* Total number of segments */ - int iLvl; /* Used to iterate through levels */ - - for(iLvl=0; iLvlnLevel; iLvl++){ - nSegment += pStruct->aLevel[iLvl].nSeg; - } - - return nSegment; -} - -/* -** Serialize and store the "structure" record for index iIdx. -** -** If an error occurs, leave an error code in the Fts5Index object. If an -** error has already occurred, this function is a no-op. -*/ -static void fts5StructureWrite(Fts5Index *p, int iIdx, Fts5Structure *pStruct){ - if( p->rc==SQLITE_OK ){ - int nSegment; /* Total number of segments */ - Fts5Buffer buf; /* Buffer to serialize record into */ - int iLvl; /* Used to iterate through levels */ - int iCookie; /* Cookie value to store */ - - nSegment = fts5StructureCountSegments(pStruct); - memset(&buf, 0, sizeof(Fts5Buffer)); - - /* Append the current configuration cookie */ - iCookie = p->pConfig->iCookie; - if( iCookie<0 ) iCookie = 0; - fts5BufferAppend32(&p->rc, &buf, iCookie); - - fts5BufferAppendVarint(&p->rc, &buf, pStruct->nLevel); - fts5BufferAppendVarint(&p->rc, &buf, nSegment); - fts5BufferAppendVarint(&p->rc, &buf, (i64)pStruct->nWriteCounter); - - for(iLvl=0; iLvlnLevel; iLvl++){ - int iSeg; /* Used to iterate through segments */ - Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; - fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge); - fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg); - assert( pLvl->nMerge<=pLvl->nSeg ); - - for(iSeg=0; iSegnSeg; iSeg++){ - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid); - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].nHeight); - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst); - fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast); - } - } - - fts5DataWrite(p, FTS5_STRUCTURE_ROWID(iIdx), buf.p, buf.n); - fts5BufferFree(&buf); - } -} - -#if 0 -static void fts5PrintStructure(const char *zCaption, Fts5Structure *pStruct){ - int rc = SQLITE_OK; - Fts5Buffer buf; - memset(&buf, 0, sizeof(buf)); - fts5DebugStructure(&rc, &buf, pStruct); - fprintf(stdout, "%s: %s\n", zCaption, buf.p); - fflush(stdout); - fts5BufferFree(&buf); -} -#else -# define fts5PrintStructure(x,y) -#endif - -static int fts5SegmentSize(Fts5StructureSegment *pSeg){ - return 1 + pSeg->pgnoLast - pSeg->pgnoFirst; -} - -/* -** Return a copy of index structure pStruct. Except, promote as many segments -** as possible to level iPromote. If an OOM occurs, NULL is returned. -*/ -static void fts5StructurePromoteTo( - Fts5Index *p, - int iPromote, - int szPromote, - Fts5Structure *pStruct -){ - int il, is; - Fts5StructureLevel *pOut = &pStruct->aLevel[iPromote]; - - for(il=iPromote+1; ilnLevel; il++){ - Fts5StructureLevel *pLvl = &pStruct->aLevel[il]; - for(is=pLvl->nSeg-1; is>=0; is--){ - int sz = fts5SegmentSize(&pLvl->aSeg[is]); - if( sz>szPromote ) return; - fts5StructureExtendLevel(&p->rc, pStruct, iPromote, 1, 1); - if( p->rc ) return; - memcpy(pOut->aSeg, &pLvl->aSeg[is], sizeof(Fts5StructureSegment)); - pOut->nSeg++; - pLvl->nSeg--; - } - } -} - -/* -** A new segment has just been written to level iLvl of index structure -** pStruct. This function determines if any segments should be promoted -** as a result. Segments are promoted in two scenarios: -** -** a) If the segment just written is smaller than one or more segments -** within the previous populated level, it is promoted to the previous -** populated level. -** -** b) If the segment just written is larger than the newest segment on -** the next populated level, then that segment, and any other adjacent -** segments that are also smaller than the one just written, are -** promoted. -** -** If one or more segments are promoted, the structure object is updated -** to reflect this. -*/ -static void fts5StructurePromote( - Fts5Index *p, /* FTS5 backend object */ - int iLvl, /* Index level just updated */ - Fts5Structure *pStruct /* Index structure */ -){ - if( p->rc==SQLITE_OK ){ - int iTst; - int iPromote = -1; - int szPromote; /* Promote anything this size or smaller */ - Fts5StructureSegment *pSeg; /* Segment just written */ - Fts5StructureLevel *pTst; - int szSeg; /* Size of segment just written */ - - - pSeg = &pStruct->aLevel[iLvl].aSeg[pStruct->aLevel[iLvl].nSeg-1]; - szSeg = (1 + pSeg->pgnoLast - pSeg->pgnoFirst); - - /* Check for condition (a) */ - for(iTst=iLvl-1; iTst>=0 && pStruct->aLevel[iTst].nSeg==0; iTst--); - pTst = &pStruct->aLevel[iTst]; - if( iTst>=0 && pTst->nMerge==0 ){ - int i; - int szMax = 0; - for(i=0; inSeg; i++){ - int sz = pTst->aSeg[i].pgnoLast - pTst->aSeg[i].pgnoFirst + 1; - if( sz>szMax ) szMax = sz; - } - if( szMax>=szSeg ){ - /* Condition (a) is true. Promote the newest segment on level - ** iLvl to level iTst. */ - iPromote = iTst; - szPromote = szMax; - } - } - - /* Check for condition (b) */ - if( iPromote<0 ){ - Fts5StructureLevel *pTst; - for(iTst=iLvl+1; iTstnLevel; iTst++){ - pTst = &pStruct->aLevel[iTst]; - if( pTst->nSeg ) break; - } - if( iTstnLevel && pTst->nMerge==0 ){ - Fts5StructureSegment *pSeg2 = &pTst->aSeg[pTst->nSeg-1]; - int sz = pSeg2->pgnoLast - pSeg2->pgnoFirst + 1; - if( sz<=szSeg ){ - iPromote = iLvl; - szPromote = szSeg; - } - } - } - - /* If iPromote is greater than or equal to zero at this point, then it - ** is the level number of a level to which segments that consist of - ** szPromote or fewer pages should be promoted. */ - if( iPromote>=0 ){ - fts5PrintStructure("BEFORE", pStruct); - fts5StructurePromoteTo(p, iPromote, szPromote, pStruct); - fts5PrintStructure("AFTER", pStruct); - } - } -} - - -/* -** If the pIter->iOff offset currently points to an entry indicating one -** or more term-less nodes, advance past it and set pIter->nEmpty to -** the number of empty child nodes. -*/ -static void fts5NodeIterGobbleNEmpty(Fts5NodeIter *pIter){ - if( pIter->iOffnData && 0==(pIter->aData[pIter->iOff] & 0xfe) ){ - pIter->bDlidx = pIter->aData[pIter->iOff] & 0x01; - pIter->iOff++; - pIter->iOff += getVarint32(&pIter->aData[pIter->iOff], pIter->nEmpty); - }else{ - pIter->nEmpty = 0; - pIter->bDlidx = 0; - } -} - -/* -** Advance to the next entry within the node. -*/ -static void fts5NodeIterNext(int *pRc, Fts5NodeIter *pIter){ - if( pIter->iOff>=pIter->nData ){ - pIter->aData = 0; - pIter->iChild += pIter->nEmpty; - }else{ - int nPre, nNew; - pIter->iOff += getVarint32(&pIter->aData[pIter->iOff], nPre); - pIter->iOff += getVarint32(&pIter->aData[pIter->iOff], nNew); - pIter->term.n = nPre-2; - fts5BufferAppendBlob(pRc, &pIter->term, nNew, pIter->aData+pIter->iOff); - pIter->iOff += nNew; - pIter->iChild += (1 + pIter->nEmpty); - fts5NodeIterGobbleNEmpty(pIter); - if( *pRc ) pIter->aData = 0; - } -} - - -/* -** Initialize the iterator object pIter to iterate through the internal -** segment node in pData. -*/ -static void fts5NodeIterInit(const u8 *aData, int nData, Fts5NodeIter *pIter){ - memset(pIter, 0, sizeof(*pIter)); - pIter->aData = aData; - pIter->nData = nData; - pIter->iOff = getVarint32(aData, pIter->iChild); - fts5NodeIterGobbleNEmpty(pIter); -} - -/* -** Free any memory allocated by the iterator object. -*/ -static void fts5NodeIterFree(Fts5NodeIter *pIter){ - fts5BufferFree(&pIter->term); -} - -/* -** The iterator passed as the first argument has the following fields set -** as follows. This function sets up the rest of the iterator so that it -** points to the first rowid in the doclist-index. -** -** pData: pointer to doclist-index record, -** iLeafPgno: page number that this doclist-index is associated with. -*/ -static int fts5DlidxIterFirst(Fts5DlidxIter *pIter){ - Fts5Data *pData = pIter->pData; - int i; - - assert( pIter->pData ); - assert( pIter->iLeafPgno>0 ); - - /* Count the number of leading 0x00 bytes. Then set iLeafPgno. */ - for(i=0; in; i++){ - if( pData->p[i] ) break; - } - pIter->iLeafPgno += (i+1); - pIter->iOff = i; - - /* Unless we are already at the end of the doclist-index, load the first - ** rowid value. */ - if( pIter->iOffn ){ - pIter->iOff += getVarint(&pData->p[pIter->iOff], (u64*)&pIter->iRowid); - }else{ - pIter->bEof = 1; - } - pIter->iFirstOff = pIter->iOff; - return pIter->bEof; -} - -/* -** Advance the iterator passed as the only argument. -*/ -static int fts5DlidxIterNext(Fts5DlidxIter *pIter){ - Fts5Data *pData = pIter->pData; - int iOff; - - for(iOff=pIter->iOff; iOffn; iOff++){ - if( pData->p[iOff] ) break; - } - - if( iOffn ){ - i64 iVal; - pIter->iLeafPgno += (iOff - pIter->iOff) + 1; - iOff += getVarint(&pData->p[iOff], (u64*)&iVal); - pIter->iRowid -= iVal; - pIter->iOff = iOff; - }else{ - pIter->bEof = 1; - } - - return pIter->bEof; -} - -static int fts5DlidxIterEof(Fts5Index *p, Fts5DlidxIter *pIter){ - return (p->rc!=SQLITE_OK || pIter->bEof); -} - -static void fts5DlidxIterLast(Fts5DlidxIter *pIter){ - if( fts5DlidxIterFirst(pIter)==0 ){ - while( 0==fts5DlidxIterNext(pIter) ); - pIter->bEof = 0; - } -} - -static int fts5DlidxIterPrev(Fts5DlidxIter *pIter){ - int iOff = pIter->iOff; - - assert( pIter->bEof==0 ); - if( iOff<=pIter->iFirstOff ){ - pIter->bEof = 1; - }else{ - u8 *a = pIter->pData->p; - i64 iVal; - int iLimit; - - /* Currently iOff points to the first byte of a varint. This block - ** decrements iOff until it points to the first byte of the previous - ** varint. Taking care not to read any memory locations that occur - ** before the buffer in memory. */ - iLimit = (iOff>9 ? iOff-9 : 0); - for(iOff--; iOff>iLimit; iOff--){ - if( (a[iOff-1] & 0x80)==0 ) break; - } - - getVarint(&a[iOff], (u64*)&iVal); - pIter->iRowid += iVal; - pIter->iLeafPgno--; - - while( iOff>pIter->iFirstOff - && a[iOff-1]==0x00 && (a[iOff-2] & 0x80)==0 - ){ - iOff--; - pIter->iLeafPgno--; - } - pIter->iOff = iOff; - } - - return pIter->bEof; -} - -static void fts5DlidxIterInit( - Fts5Index *p, /* Fts5 Backend to iterate within */ - int bRev, /* True for ORDER BY ASC */ - int iIdx, int iSegid, /* Segment iSegid within index iIdx */ - int iLeafPgno, /* Leaf page number to load dlidx for */ - Fts5DlidxIter **ppIter /* OUT: Populated iterator */ -){ - Fts5DlidxIter *pIter = *ppIter; - Fts5Data *pDlidx; - - pDlidx = fts5DataRead(p, FTS5_DOCLIST_IDX_ROWID(iIdx, iSegid, iLeafPgno)); - if( pDlidx==0 ) return; - if( pIter==0 ){ - *ppIter = pIter = (Fts5DlidxIter*)fts5IdxMalloc(p, sizeof(Fts5DlidxIter)); - if( pIter==0 ){ - fts5DataRelease(pDlidx); - return; - } - }else{ - memset(pIter, 0, sizeof(Fts5DlidxIter)); - } - - pIter->pData = pDlidx; - pIter->iLeafPgno = iLeafPgno; - if( bRev==0 ){ - fts5DlidxIterFirst(pIter); - }else{ - fts5DlidxIterLast(pIter); - } -} - -/* -** Free a doclist-index iterator object allocated by fts5DlidxIterInit(). -*/ -static void fts5DlidxIterFree(Fts5DlidxIter *pIter){ - if( pIter ){ - fts5DataRelease(pIter->pData); - sqlite3_free(pIter); - } -} - -/* -** Load the next leaf page into the segment iterator. -*/ -static void fts5SegIterNextPage( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegIter *pIter /* Iterator to advance to next page */ -){ - Fts5StructureSegment *pSeg = pIter->pSeg; - if( pIter->pLeaf ) fts5DataRelease(pIter->pLeaf); - pIter->iLeafPgno++; - if( pIter->iLeafPgno<=pSeg->pgnoLast ){ - pIter->pLeaf = fts5DataRead(p, - FTS5_SEGMENT_ROWID(pIter->iIdx, pSeg->iSegid, 0, pIter->iLeafPgno) - ); - }else{ - pIter->pLeaf = 0; - } -} - -/* -** Leave pIter->iLeafOffset as the offset to the size field of the first -** position list. The position list belonging to document pIter->iRowid. -*/ -static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ - u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ - int iOff = pIter->iLeafOffset; /* Offset to read at */ - int nNew; /* Bytes of new data */ - - iOff += getVarint32(&a[iOff], nNew); - pIter->term.n = nKeep; - fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]); - iOff += nNew; - pIter->iTermLeafOffset = iOff; - pIter->iTermLeafPgno = pIter->iLeafPgno; - if( iOff>=pIter->pLeaf->n ){ - fts5SegIterNextPage(p, pIter); - if( pIter->pLeaf==0 ){ - if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT; - return; - } - iOff = 4; - a = pIter->pLeaf->p; - } - iOff += sqlite3GetVarint(&a[iOff], (u64*)&pIter->iRowid); - pIter->iLeafOffset = iOff; -} - -/* -** Initialize the iterator object pIter to iterate through the entries in -** segment pSeg within index iIdx. The iterator is left pointing to the -** first entry when this function returns. -** -** If an error occurs, Fts5Index.rc is set to an appropriate error code. If -** an error has already occurred when this function is called, it is a no-op. -*/ -static void fts5SegIterInit( - Fts5Index *p, - int iIdx, /* Config.aHash[] index of FTS index */ - Fts5StructureSegment *pSeg, /* Description of segment */ - Fts5SegIter *pIter /* Object to populate */ -){ - if( pSeg->pgnoFirst==0 ){ - /* This happens if the segment is being used as an input to an incremental - ** merge and all data has already been "trimmed". See function - ** fts5TrimSegments() for details. In this case leave the iterator empty. - ** The caller will see the (pIter->pLeaf==0) and assume the iterator is - ** at EOF already. */ - assert( pIter->pLeaf==0 ); - return; - } - - if( p->rc==SQLITE_OK ){ - memset(pIter, 0, sizeof(*pIter)); - pIter->pSeg = pSeg; - pIter->iIdx = iIdx; - pIter->iLeafPgno = pSeg->pgnoFirst-1; - fts5SegIterNextPage(p, pIter); - } - - if( p->rc==SQLITE_OK ){ - u8 *a = pIter->pLeaf->p; - pIter->iLeafOffset = fts5GetU16(&a[2]); - fts5SegIterLoadTerm(p, pIter, 0); - } -} - -static void fts5LeafHeader(Fts5Data *pLeaf, int *piRowid, int *piTerm){ - *piRowid = (int)fts5GetU16(&pLeaf->p[0]); - *piTerm = (int)fts5GetU16(&pLeaf->p[2]); -} - -/* -** This function is only ever called on iterators created by calls to -** Fts5IndexQuery() with the FTS5INDEX_QUERY_ASC flag set. -** -** When this function is called, iterator pIter points to the first rowid -** on the current leaf associated with the term being queried. This function -** advances it to point to the last such rowid and, if necessary, initializes -** the aRowidOffset[] and iRowidOffset variables. -*/ -static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ - int n = pIter->pLeaf->n; - int i = pIter->iLeafOffset; - u8 *a = pIter->pLeaf->p; - int iRowidOffset = 0; - - while( p->rc==SQLITE_OK && i=n ) break; - i += getVarint(&a[i], (u64*)&iDelta); - if( iDelta==0 ) break; - pIter->iRowid -= iDelta; - - if( iRowidOffset>=pIter->nRowidOffset ){ - int nNew = pIter->nRowidOffset + 8; - int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); - if( aNew==0 ){ - p->rc = SQLITE_NOMEM; - break; - } - pIter->aRowidOffset = aNew; - pIter->nRowidOffset = nNew; - } - - pIter->aRowidOffset[iRowidOffset++] = pIter->iLeafOffset; - pIter->iLeafOffset = i; - } - pIter->iRowidOffset = iRowidOffset; -} - -/* -** -*/ -static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ - assert( pIter->flags & FTS5_SEGITER_REVERSE ); - assert( pIter->flags & FTS5_SEGITER_ONETERM ); - - fts5DataRelease(pIter->pLeaf); - pIter->pLeaf = 0; - while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){ - Fts5Data *pNew; - pIter->iLeafPgno--; - pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID( - pIter->iIdx, pIter->pSeg->iSegid, 0, pIter->iLeafPgno - )); - if( pNew ){ - if( pIter->iLeafPgno==pIter->iTermLeafPgno ){ - if( pIter->iTermLeafOffsetn ){ - pIter->pLeaf = pNew; - pIter->iLeafOffset = pIter->iTermLeafOffset; - } - }else{ - int iRowidOff, dummy; - fts5LeafHeader(pNew, &iRowidOff, &dummy); - if( iRowidOff ){ - pIter->pLeaf = pNew; - pIter->iLeafOffset = iRowidOff; - } - } - - if( pIter->pLeaf ){ - u8 *a = &pIter->pLeaf->p[pIter->iLeafOffset]; - pIter->iLeafOffset += getVarint(a, (u64*)&pIter->iRowid); - break; - }else{ - fts5DataRelease(pNew); - } - } - } - - if( pIter->pLeaf ){ - fts5SegIterReverseInitPage(p, pIter); - } -} - -/* -** Return true if the iterator passed as the second argument currently -** points to a delete marker. A delete marker is an entry with a 0 byte -** position-list. -*/ -static int fts5SegIterIsDelete( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegIter *pIter /* Iterator to advance */ -){ - int bRet = 0; - Fts5Data *pLeaf = pIter->pLeaf; - if( p->rc==SQLITE_OK && pLeaf ){ - if( pIter->iLeafOffsetn ){ - bRet = (pLeaf->p[pIter->iLeafOffset]==0x00); - }else{ - Fts5Data *pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID( - pIter->iIdx, pIter->pSeg->iSegid, 0, pIter->iLeafPgno - )); - if( pNew ){ - bRet = (pNew->p[4]==0x00); - fts5DataRelease(pNew); - } - } - } - return bRet; -} - -/* -** Advance iterator pIter to the next entry. -** -** If an error occurs, Fts5Index.rc is set to an appropriate error code. It -** is not considered an error if the iterator reaches EOF. If an error has -** already occurred when this function is called, it is a no-op. -*/ -static void fts5SegIterNext( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegIter *pIter /* Iterator to advance */ -){ - if( p->rc==SQLITE_OK ){ - if( pIter->flags & FTS5_SEGITER_REVERSE ){ - if( pIter->iRowidOffset>0 ){ - u8 *a = pIter->pLeaf->p; - int iOff; - int nPos; - i64 iDelta; - pIter->iRowidOffset--; - - pIter->iLeafOffset = iOff = pIter->aRowidOffset[pIter->iRowidOffset]; - iOff += getVarint32(&a[iOff], nPos); - iOff += nPos; - getVarint(&a[iOff], (u64*)&iDelta); - pIter->iRowid += iDelta; - }else{ - fts5SegIterReverseNewPage(p, pIter); - } - }else{ - Fts5Data *pLeaf = pIter->pLeaf; - int iOff; - int bNewTerm = 0; - int nKeep = 0; - - /* Search for the end of the position list within the current page. */ - u8 *a = pLeaf->p; - int n = pLeaf->n; - - iOff = pIter->iLeafOffset; - if( iOffiLeafOffset = iOff; - if( iDelta==0 ){ - bNewTerm = 1; - if( iOff>=n ){ - fts5SegIterNextPage(p, pIter); - pIter->iLeafOffset = 4; - }else if( iOff!=fts5GetU16(&a[2]) ){ - pIter->iLeafOffset += getVarint32(&a[iOff], nKeep); - } - }else{ - pIter->iRowid -= iDelta; - } - }else{ - iOff = 0; - /* Next entry is not on the current page */ - while( iOff==0 ){ - fts5SegIterNextPage(p, pIter); - pLeaf = pIter->pLeaf; - if( pLeaf==0 ) break; - if( (iOff = fts5GetU16(&pLeaf->p[0])) ){ - iOff += sqlite3GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); - pIter->iLeafOffset = iOff; - } - else if( (iOff = fts5GetU16(&pLeaf->p[2])) ){ - pIter->iLeafOffset = iOff; - bNewTerm = 1; - } - } - } - - /* Check if the iterator is now at EOF. If so, return early. */ - if( pIter->pLeaf && bNewTerm ){ - if( pIter->flags & FTS5_SEGITER_ONETERM ){ - fts5DataRelease(pIter->pLeaf); - pIter->pLeaf = 0; - }else{ - fts5SegIterLoadTerm(p, pIter, nKeep); - } - } - } - } -} - -/* -** Iterator pIter currently points to the first rowid in a doclist. This -** function sets the iterator up so that iterates in reverse order through -** the doclist. -*/ -static void fts5SegIterReverse(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ - Fts5Data *pLeaf; /* Current leaf data */ - int iOff = pIter->iLeafOffset; /* Byte offset within current leaf */ - Fts5Data *pLast = 0; - int pgnoLast = 0; - - /* Move to the page that contains the last rowid in this doclist. */ - pLeaf = pIter->pLeaf; - - if( pIter->pDlidx ){ - int iSegid = pIter->pSeg->iSegid; - pgnoLast = pIter->pDlidx->iLeafPgno; - pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, pgnoLast)); - }else{ - while( iOffn ){ - int nPos; - i64 iDelta; - - /* Position list size in bytes */ - iOff += getVarint32(&pLeaf->p[iOff], nPos); - iOff += nPos; - if( iOff>=pLeaf->n ) break; - - /* Rowid delta. Or, if 0x00, the end of doclist marker. */ - nPos = getVarint(&pLeaf->p[iOff], (u64*)&iDelta); - if( iDelta==0 ) break; - iOff += nPos; - } - - if( iOff>=pLeaf->n ){ - Fts5StructureSegment *pSeg = pIter->pSeg; - i64 iAbs = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, pIter->iLeafPgno); - i64 iLast = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, pSeg->pgnoLast); - - /* The last rowid in the doclist may not be on the current page. Search - ** forward to find the page containing the last rowid. */ - for(iAbs++; p->rc==SQLITE_OK && iAbs<=iLast; iAbs++){ - Fts5Data *pNew = fts5DataRead(p, iAbs); - if( pNew ){ - int iRowid, iTerm; - fts5LeafHeader(pNew, &iRowid, &iTerm); - if( iRowid ){ - Fts5Data *pTmp = pLast; - pLast = pNew; - pNew = pTmp; - pgnoLast = iAbs & (((i64)1 << FTS5_DATA_PAGE_B) - 1); - } - if( iTerm ){ - iAbs = iLast; - } - fts5DataRelease(pNew); - } - } - } - } - - /* If pLast is NULL at this point, then the last rowid for this doclist - ** lies on the page currently indicated by the iterator. In this case - ** iLastOff is set to the value that pIter->iLeafOffset will take when - ** the iterator points to that rowid. - ** - ** Or, if pLast is non-NULL, then it is the page that contains the last - ** rowid. - */ - if( pLast ){ - int dummy; - fts5DataRelease(pIter->pLeaf); - pIter->pLeaf = pLast; - pIter->iLeafPgno = pgnoLast; - fts5LeafHeader(pLast, &iOff, &dummy); - iOff += getVarint(&pLast->p[iOff], (u64*)&pIter->iRowid); - pIter->iLeafOffset = iOff; - } - - fts5SegIterReverseInitPage(p, pIter); -} - -/* -** Iterator pIter currently points to the first rowid of a doclist within -** index iIdx. There is a doclist-index associated with the final term on -** the current page. If the current term is the last term on the page, -** load the doclist-index from disk and initialize an iterator at -** (pIter->pDlidx). -*/ -static void fts5SegIterLoadDlidx(Fts5Index *p, int iIdx, Fts5SegIter *pIter){ - int iSegid = pIter->pSeg->iSegid; - int bRev = (pIter->flags & FTS5_SEGITER_REVERSE); - Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ - int iOff = pIter->iLeafOffset; /* Byte offset within current leaf */ - - assert( pIter->flags & FTS5_SEGITER_ONETERM ); - assert( pIter->pDlidx==0 ); - - /* Check if the current doclist ends on this page. If it does, return - ** early without loading the doclist-index (as it belongs to a different - ** term. */ - while( iOffn ){ - i64 iDelta; - int nPoslist; - - /* iOff is currently the offset of the size field of a position list. */ - iOff += getVarint32(&pLeaf->p[iOff], nPoslist); - iOff += nPoslist; - - if( iOffn ){ - iOff += getVarint(&pLeaf->p[iOff], (u64*)&iDelta); - if( iDelta==0 ) return; - } - } - - fts5DlidxIterInit(p, bRev, iIdx, iSegid, pIter->iLeafPgno, &pIter->pDlidx); -} - -/* -** Initialize the object pIter to point to term pTerm/nTerm within segment -** pSeg, index iIdx. If there is no such term in the index, the iterator -** is set to EOF. -** -** If an error occurs, Fts5Index.rc is set to an appropriate error code. If -** an error has already occurred when this function is called, it is a no-op. -*/ -static void fts5SegIterSeekInit( - Fts5Index *p, /* FTS5 backend */ - int iIdx, /* Config.aHash[] index of FTS index */ - const u8 *pTerm, int nTerm, /* Term to seek to */ - int flags, /* Mask of FTS5INDEX_XXX flags */ - Fts5StructureSegment *pSeg, /* Description of segment */ - Fts5SegIter *pIter /* Object to populate */ -){ - int iPg = 1; - int h; - int bGe = ((flags & FTS5INDEX_QUERY_PREFIX) && iIdx==0); - int bDlidx = 0; /* True if there is a doclist-index */ - - assert( bGe==0 || (flags & FTS5INDEX_QUERY_ASC)==0 ); - assert( pTerm && nTerm ); - memset(pIter, 0, sizeof(*pIter)); - pIter->pSeg = pSeg; - pIter->iIdx = iIdx; - - /* This block sets stack variable iPg to the leaf page number that may - ** contain term (pTerm/nTerm), if it is present in the segment. */ - for(h=pSeg->nHeight-1; h>0; h--){ - Fts5NodeIter node; /* For iterating through internal nodes */ - i64 iRowid = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, h, iPg); - Fts5Data *pNode = fts5DataRead(p, iRowid); - if( pNode==0 ) break; - - fts5NodeIterInit(pNode->p, pNode->n, &node); - assert( node.term.n==0 ); - - iPg = node.iChild; - bDlidx = node.bDlidx; - for(fts5NodeIterNext(&p->rc, &node); - node.aData && fts5BufferCompareBlob(&node.term, pTerm, nTerm)<=0; - fts5NodeIterNext(&p->rc, &node) - ){ - iPg = node.iChild; - bDlidx = node.bDlidx; - } - fts5NodeIterFree(&node); - fts5DataRelease(pNode); - } - - if( iPgpgnoFirst ){ - iPg = pSeg->pgnoFirst; - bDlidx = 0; - } - - pIter->iLeafPgno = iPg - 1; - fts5SegIterNextPage(p, pIter); - - if( pIter->pLeaf ){ - int res; - pIter->iLeafOffset = fts5GetU16(&pIter->pLeaf->p[2]); - fts5SegIterLoadTerm(p, pIter, 0); - do { - res = fts5BufferCompareBlob(&pIter->term, pTerm, nTerm); - if( res>=0 ) break; - fts5SegIterNext(p, pIter); - }while( pIter->pLeaf && p->rc==SQLITE_OK ); - - if( bGe==0 && res ){ - /* Set iterator to point to EOF */ - fts5DataRelease(pIter->pLeaf); - pIter->pLeaf = 0; - } - } - - if( p->rc==SQLITE_OK && bGe==0 ){ - pIter->flags |= FTS5_SEGITER_ONETERM; - if( pIter->pLeaf ){ - if( flags & FTS5INDEX_QUERY_ASC ){ - pIter->flags |= FTS5_SEGITER_REVERSE; - } - if( bDlidx ){ - fts5SegIterLoadDlidx(p, iIdx, pIter); - } - if( flags & FTS5INDEX_QUERY_ASC ){ - fts5SegIterReverse(p, iIdx, pIter); - } - } - } -} - -/* -** Zero the iterator passed as the only argument. -*/ -static void fts5SegIterClear(Fts5SegIter *pIter){ - fts5BufferFree(&pIter->term); - fts5DataRelease(pIter->pLeaf); - fts5DlidxIterFree(pIter->pDlidx); - sqlite3_free(pIter->aRowidOffset); - memset(pIter, 0, sizeof(Fts5SegIter)); -} - -/* -** Do the comparison necessary to populate pIter->aFirst[iOut]. -** -** If the returned value is non-zero, then it is the index of an entry -** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing -** to a key that is a duplicate of another, higher priority, -** segment-iterator in the pSeg->aSeg[] array. -*/ -static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){ - int i1; /* Index of left-hand Fts5SegIter */ - int i2; /* Index of right-hand Fts5SegIter */ - int iRes; - Fts5SegIter *p1; /* Left-hand Fts5SegIter */ - Fts5SegIter *p2; /* Right-hand Fts5SegIter */ - - assert( iOutnSeg && iOut>0 ); - assert( pIter->bRev==0 || pIter->bRev==1 ); - - if( iOut>=(pIter->nSeg/2) ){ - i1 = (iOut - pIter->nSeg/2) * 2; - i2 = i1 + 1; - }else{ - i1 = pIter->aFirst[iOut*2]; - i2 = pIter->aFirst[iOut*2+1]; - } - p1 = &pIter->aSeg[i1]; - p2 = &pIter->aSeg[i2]; - - if( p1->pLeaf==0 ){ /* If p1 is at EOF */ - iRes = i2; - }else if( p2->pLeaf==0 ){ /* If p2 is at EOF */ - iRes = i1; - }else{ - int res = fts5BufferCompare(&p1->term, &p2->term); - if( res==0 ){ - assert( i2>i1 ); - assert( i2!=0 ); - if( p1->iRowid==p2->iRowid ) return i2; - res = ((p1->iRowid < p2->iRowid)==pIter->bRev) ? -1 : +1; - } - assert( res!=0 ); - if( res<0 ){ - iRes = i1; - }else{ - iRes = i2; - } - } - - pIter->aFirst[iOut] = iRes; - return 0; -} - -/* -** Free the iterator object passed as the second argument. -*/ -static void fts5MultiIterFree(Fts5Index *p, Fts5MultiSegIter *pIter){ - if( pIter ){ - int i; - for(i=0; inSeg; i++){ - fts5SegIterClear(&pIter->aSeg[i]); - } - sqlite3_free(pIter); - } -} - -static void fts5MultiIterAdvanced( - Fts5Index *p, /* FTS5 backend to iterate within */ - Fts5MultiSegIter *pIter, /* Iterator to update aFirst[] array for */ - int iChanged, /* Index of sub-iterator just advanced */ - int iMinset /* Minimum entry in aFirst[] to set */ -){ - int i; - for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){ - int iEq; - if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){ - fts5SegIterNext(p, &pIter->aSeg[iEq]); - i = pIter->nSeg + iEq; - } - } -} - -/* -** Move the seg-iter so that it points to the first rowid on page iLeafPgno. -** It is an error if leaf iLeafPgno contains no rowid. -*/ -static void fts5SegIterGotoPage( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegIter *pIter, /* Iterator to advance */ - int iLeafPgno -){ - assert( iLeafPgno>pIter->iLeafPgno ); - if( p->rc==SQLITE_OK ){ - pIter->iLeafPgno = iLeafPgno-1; - fts5SegIterNextPage(p, pIter); - assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno ); - } - - if( p->rc==SQLITE_OK ){ - int iOff; - u8 *a = pIter->pLeaf->p; - int n = pIter->pLeaf->n; - - iOff = fts5GetU16(&a[0]); - if( iOff<4 || iOff>=n ){ - p->rc = FTS5_CORRUPT; - }else{ - iOff += getVarint(&a[iOff], (u64*)&pIter->iRowid); - pIter->iLeafOffset = iOff; - } - } -} - -/* -** Advance the iterator passed as the second argument until it is at or -** past rowid iFrom. Regardless of the value of iFrom, the iterator is -** always advanced at least once. -*/ -static void fts5SegIterNextFrom( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegIter *pIter, /* Iterator to advance */ - i64 iMatch /* Advance iterator at least this far */ -){ - int bRev = (pIter->flags & FTS5_SEGITER_REVERSE); - Fts5DlidxIter *pDlidx = pIter->pDlidx; - int iLeafPgno = pIter->iLeafPgno; - int bMove = 1; - - assert( pIter->flags & FTS5_SEGITER_ONETERM ); - assert( pIter->pDlidx ); - assert( pIter->pLeaf ); - - if( bRev==0 ){ - while( fts5DlidxIterEof(p, pDlidx)==0 && iMatchiRowid ){ - iLeafPgno = pDlidx->iLeafPgno; - fts5DlidxIterNext(pDlidx); - } - assert( iLeafPgno>=pIter->iLeafPgno || p->rc ); - if( iLeafPgno>pIter->iLeafPgno ){ - fts5SegIterGotoPage(p, pIter, iLeafPgno); - bMove = 0; - } - }else{ - assert( iMatch>pIter->iRowid ); - while( fts5DlidxIterEof(p, pDlidx)==0 && iMatch>pDlidx->iRowid ){ - fts5DlidxIterPrev(pDlidx); - } - iLeafPgno = pDlidx->iLeafPgno; - - assert( fts5DlidxIterEof(p, pDlidx) || iLeafPgno<=pIter->iLeafPgno ); - - if( iLeafPgnoiLeafPgno ){ - pIter->iLeafPgno = iLeafPgno+1; - fts5SegIterReverseNewPage(p, pIter); - bMove = 0; - } - } - - while( 1 ){ - if( bMove ) fts5SegIterNext(p, pIter); - if( pIter->pLeaf==0 ) break; - if( bRev==0 && pIter->iRowid<=iMatch ) break; - if( bRev!=0 && pIter->iRowid>=iMatch ) break; - bMove = 1; - } -} - -/* -** Move the iterator to the next entry. -** -** If an error occurs, an error code is left in Fts5Index.rc. It is not -** considered an error if the iterator reaches EOF, or if it is already at -** EOF when this function is called. -*/ -static void fts5MultiIterNext( - Fts5Index *p, - Fts5MultiSegIter *pIter, - int bFrom, /* True if argument iFrom is valid */ - i64 iFrom /* Advance at least as far as this */ -){ - if( p->rc==SQLITE_OK ){ - int bUseFrom = bFrom; - do { - int iFirst = pIter->aFirst[1]; - Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; - if( bUseFrom && pSeg->pDlidx ){ - fts5SegIterNextFrom(p, pSeg, iFrom); - }else{ - fts5SegIterNext(p, pSeg); - } - fts5MultiIterAdvanced(p, pIter, iFirst, 1); - bUseFrom = 0; - }while( pIter->bSkipEmpty - && fts5SegIterIsDelete(p, &pIter->aSeg[pIter->aFirst[1]]) - ); - } -} - -/* -** Allocate a new Fts5MultiSegIter object. -** -** The new object will be used to iterate through data in structure pStruct. -** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel -** is zero or greater, data from the first nSegment segments on level iLevel -** is merged. -** -** The iterator initially points to the first term/rowid entry in the -** iterated data. -*/ -static void fts5MultiIterNew( - Fts5Index *p, /* FTS5 backend to iterate within */ - Fts5Structure *pStruct, /* Structure of specific index */ - int iIdx, /* Config.aHash[] index of FTS index */ - int bSkipEmpty, - int flags, /* True for >= */ - const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */ - int iLevel, /* Level to iterate (-1 for all) */ - int nSegment, /* Number of segments to merge (iLevel>=0) */ - Fts5MultiSegIter **ppOut /* New object */ -){ - int nSeg; /* Number of segments merged */ - int nSlot; /* Power of two >= nSeg */ - int iIter = 0; /* */ - int iSeg; /* Used to iterate through segments */ - Fts5StructureLevel *pLvl; - Fts5MultiSegIter *pNew; - - assert( (pTerm==0 && nTerm==0) || iLevel<0 ); - - /* Allocate space for the new multi-seg-iterator. */ - if( iLevel<0 ){ - nSeg = fts5StructureCountSegments(pStruct); - }else{ - nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment); - } - for(nSlot=2; nSlotaSeg[] */ - sizeof(u16) * nSlot /* pNew->aFirst[] */ - ); - if( pNew==0 ) return; - pNew->nSeg = nSlot; - pNew->aSeg = (Fts5SegIter*)&pNew[1]; - pNew->aFirst = (u16*)&pNew->aSeg[nSlot]; - pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_ASC)); - pNew->bSkipEmpty = bSkipEmpty; - - /* Initialize each of the component segment iterators. */ - if( iLevel<0 ){ - Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel]; - for(pLvl=&pStruct->aLevel[0]; pLvlnSeg-1; iSeg>=0; iSeg--){ - Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; - Fts5SegIter *pIter = &pNew->aSeg[iIter++]; - if( pTerm==0 ){ - fts5SegIterInit(p, iIdx, pSeg, pIter); - }else{ - fts5SegIterSeekInit(p, iIdx, pTerm, nTerm, flags, pSeg, pIter); - } - } - } - }else{ - pLvl = &pStruct->aLevel[iLevel]; - for(iSeg=nSeg-1; iSeg>=0; iSeg--){ - fts5SegIterInit(p, iIdx, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]); - } - } - assert( iIter==nSeg ); - - /* If the above was successful, each component iterators now points - ** to the first entry in its segment. In this case initialize the - ** aFirst[] array. Or, if an error has occurred, free the iterator - ** object and set the output variable to NULL. */ - if( p->rc==SQLITE_OK ){ - for(iIter=nSlot-1; iIter>0; iIter--){ - int iEq; - if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ - fts5SegIterNext(p, &pNew->aSeg[iEq]); - fts5MultiIterAdvanced(p, pNew, iEq, iIter); - } - } - - if( pNew->bSkipEmpty - && fts5SegIterIsDelete(p, &pNew->aSeg[pNew->aFirst[1]]) - ){ - fts5MultiIterNext(p, pNew, 0, 0); - } - }else{ - fts5MultiIterFree(p, pNew); - *ppOut = 0; - } -} - -/* -** Return true if the iterator is at EOF or if an error has occurred. -** False otherwise. -*/ -static int fts5MultiIterEof(Fts5Index *p, Fts5MultiSegIter *pIter){ - return (p->rc || pIter->aSeg[ pIter->aFirst[1] ].pLeaf==0); -} - -/* -** Return the rowid of the entry that the iterator currently points -** to. If the iterator points to EOF when this function is called the -** results are undefined. -*/ -static i64 fts5MultiIterRowid(Fts5MultiSegIter *pIter){ - assert( pIter->aSeg[ pIter->aFirst[1] ].pLeaf ); - return pIter->aSeg[ pIter->aFirst[1] ].iRowid; -} - -/* -** Move the iterator to the next entry at or following iMatch. -*/ -static void fts5MultiIterNextFrom( - Fts5Index *p, - Fts5MultiSegIter *pIter, - i64 iMatch -){ - while( 1 ){ - i64 iRowid; - fts5MultiIterNext(p, pIter, 1, iMatch); - if( fts5MultiIterEof(p, pIter) ) break; - iRowid = fts5MultiIterRowid(pIter); - if( pIter->bRev==0 && iRowid<=iMatch ) break; - if( pIter->bRev!=0 && iRowid>=iMatch ) break; - } -} - -/* -** Return a pointer to a buffer containing the term associated with the -** entry that the iterator currently points to. -*/ -static const u8 *fts5MultiIterTerm(Fts5MultiSegIter *pIter, int *pn){ - Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1] ]; - *pn = p->term.n; - return p->term.p; -} - -/* -** Return true if the chunk iterator passed as the second argument is -** at EOF. Or if an error has already occurred. Otherwise, return false. -*/ -static int fts5ChunkIterEof(Fts5Index *p, Fts5ChunkIter *pIter){ - return (p->rc || pIter->pLeaf==0); -} - -/* -** Advance the chunk-iterator to the next chunk of data to read. -*/ -static void fts5ChunkIterNext(Fts5Index *p, Fts5ChunkIter *pIter){ - assert( pIter->nRem>=pIter->n ); - pIter->nRem -= pIter->n; - fts5DataRelease(pIter->pLeaf); - pIter->pLeaf = 0; - pIter->p = 0; - if( pIter->nRem>0 ){ - Fts5Data *pLeaf; - pIter->iLeafRowid++; - pLeaf = pIter->pLeaf = fts5DataRead(p, pIter->iLeafRowid); - if( pLeaf ){ - pIter->n = MIN(pIter->nRem, pLeaf->n-4); - pIter->p = pLeaf->p+4; - } - } -} - -/* -** Intialize the chunk iterator to read the position list data for which -** the size field is at offset iOff of leaf pLeaf. -*/ -static void fts5ChunkIterInit( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegIter *pSeg, /* Segment iterator to read poslist from */ - Fts5ChunkIter *pIter /* Initialize this object */ -){ - int iId = pSeg->pSeg->iSegid; - i64 rowid = FTS5_SEGMENT_ROWID(pSeg->iIdx, iId, 0, pSeg->iLeafPgno); - Fts5Data *pLeaf = pSeg->pLeaf; - int iOff = pSeg->iLeafOffset; - - memset(pIter, 0, sizeof(*pIter)); - pIter->iLeafRowid = rowid; - if( iOffn ){ - fts5DataReference(pLeaf); - pIter->pLeaf = pLeaf; - }else{ - pIter->nRem = 1; - fts5ChunkIterNext(p, pIter); - if( p->rc ) return; - iOff = 4; - pLeaf = pIter->pLeaf; - } - - iOff += getVarint32(&pLeaf->p[iOff], pIter->nRem); - pIter->n = MIN(pLeaf->n - iOff, pIter->nRem); - pIter->p = pLeaf->p + iOff; - - if( pIter->n==0 ){ - fts5ChunkIterNext(p, pIter); - } -} - -static void fts5ChunkIterRelease(Fts5ChunkIter *pIter){ - fts5DataRelease(pIter->pLeaf); - pIter->pLeaf = 0; -} - -/* -** Read and return the next 32-bit varint from the position-list iterator -** passed as the second argument. -** -** If an error occurs, zero is returned an an error code left in -** Fts5Index.rc. If an error has already occurred when this function is -** called, it is a no-op. -*/ -static int fts5PosIterReadVarint(Fts5Index *p, Fts5PosIter *pIter){ - int iVal = 0; - if( p->rc==SQLITE_OK ){ - if( pIter->iOff>=pIter->chunk.n ){ - fts5ChunkIterNext(p, &pIter->chunk); - if( fts5ChunkIterEof(p, &pIter->chunk) ) return 0; - pIter->iOff = 0; - } - pIter->iOff += getVarint32(&pIter->chunk.p[pIter->iOff], iVal); - } - return iVal; -} - -/* -** Advance the position list iterator to the next entry. -*/ -static void fts5PosIterNext(Fts5Index *p, Fts5PosIter *pIter){ - int iVal; - assert( fts5ChunkIterEof(p, &pIter->chunk)==0 ); - iVal = fts5PosIterReadVarint(p, pIter); - if( fts5ChunkIterEof(p, &pIter->chunk)==0 ){ - if( iVal==1 ){ - pIter->iCol = fts5PosIterReadVarint(p, pIter); - pIter->iPos = fts5PosIterReadVarint(p, pIter) - 2; - }else{ - pIter->iPos += (iVal - 2); - } - } -} - -/* -** Initialize the Fts5PosIter object passed as the final argument to iterate -** through the position-list associated with the index entry that iterator -** pMulti currently points to. -*/ -static void fts5PosIterInit( - Fts5Index *p, /* FTS5 backend object */ - Fts5MultiSegIter *pMulti, /* Multi-seg iterator to read pos-list from */ - Fts5PosIter *pIter /* Initialize this object */ -){ - if( p->rc==SQLITE_OK ){ - Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ]; - memset(pIter, 0, sizeof(*pIter)); - fts5ChunkIterInit(p, pSeg, &pIter->chunk); - if( fts5ChunkIterEof(p, &pIter->chunk)==0 ){ - fts5PosIterNext(p, pIter); - } - } -} - -/* -** Return true if the position iterator passed as the second argument is -** at EOF. Or if an error has already occurred. Otherwise, return false. -*/ -static int fts5PosIterEof(Fts5Index *p, Fts5PosIter *pIter){ - return (p->rc || pIter->chunk.pLeaf==0); -} - -/* -** Add an entry for (iRowid/iCol/iPos) to the doclist for (pToken/nToken) -** in hash table for index iIdx. If iIdx is zero, this is the main terms -** index. Values of 1 and greater for iIdx are prefix indexes. -** -** If an OOM error is encountered, set the Fts5Index.rc error code -** accordingly. -*/ -static void fts5AddTermToHash( - Fts5Index *p, /* Index object to write to */ - int iIdx, /* Entry in p->aHash[] to update */ - int iCol, /* Column token appears in (-ve -> delete) */ - int iPos, /* Position of token within column */ - const char *pToken, int nToken /* Token to add or remove to or from index */ -){ - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3Fts5HashWrite( - p->apHash[iIdx], p->iWriteRowid, iCol, iPos, pToken, nToken - ); - } -} - -/* -** Allocate a new segment-id for the structure pStruct. -** -** If an error has already occurred, this function is a no-op. 0 is -** returned in this case. -*/ -static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ - int i; - if( p->rc!=SQLITE_OK ) return 0; - - for(i=0; i<100; i++){ - int iSegid; - sqlite3_randomness(sizeof(int), (void*)&iSegid); - iSegid = iSegid & ((1 << FTS5_DATA_ID_B)-1); - if( iSegid ){ - int iLvl, iSeg; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - if( iSegid==pStruct->aLevel[iLvl].aSeg[iSeg].iSegid ){ - iSegid = 0; - } - } - } - } - if( iSegid ) return iSegid; - } - - p->rc = SQLITE_ERROR; - return 0; -} - -/* -** Discard all data currently cached in the hash-tables. -*/ -static void fts5IndexDiscardData(Fts5Index *p){ - assert( p->apHash || p->nPendingData==0 ); - if( p->apHash ){ - Fts5Config *pConfig = p->pConfig; - int i; - for(i=0; i<=pConfig->nPrefix; i++){ - if( p->apHash[i] ) sqlite3Fts5HashClear(p->apHash[i]); - } - p->nPendingData = 0; - } -} - -/* -** Return the size of the prefix, in bytes, that buffer (nNew/pNew) shares -** with buffer (nOld/pOld). -*/ -static int fts5PrefixCompress( - int nOld, const u8 *pOld, - int nNew, const u8 *pNew -){ - int i; - for(i=0; inEmpty ){ - int bFlag = 0; - Fts5PageWriter *pPg; - pPg = &pWriter->aWriter[1]; - if( pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){ - i64 iKey = FTS5_DOCLIST_IDX_ROWID( - pWriter->iIdx, pWriter->iSegid, - pWriter->aWriter[0].pgno - 1 - pWriter->nEmpty - ); - assert( pWriter->dlidx.n>0 ); - fts5DataWrite(p, iKey, pWriter->dlidx.p, pWriter->dlidx.n); - bFlag = 1; - } - fts5BufferAppendVarint(&p->rc, &pPg->buf, bFlag); - fts5BufferAppendVarint(&p->rc, &pPg->buf, pWriter->nEmpty); - pWriter->nEmpty = 0; - } - - /* Whether or not it was written to disk, zero the doclist index at this - ** point */ - sqlite3Fts5BufferZero(&pWriter->dlidx); - pWriter->bDlidxPrevValid = 0; -} - -static void fts5WriteBtreeGrow(Fts5Index *p, Fts5SegWriter *pWriter){ - Fts5PageWriter *aNew; - Fts5PageWriter *pNew; - int nNew = sizeof(Fts5PageWriter) * (pWriter->nWriter+1); - - aNew = (Fts5PageWriter*)sqlite3_realloc(pWriter->aWriter, nNew); - if( aNew==0 ) return; - - pNew = &aNew[pWriter->nWriter]; - memset(pNew, 0, sizeof(Fts5PageWriter)); - pNew->pgno = 1; - fts5BufferAppendVarint(&p->rc, &pNew->buf, 1); - - pWriter->nWriter++; - pWriter->aWriter = aNew; -} - -/* -** This is called once for each leaf page except the first that contains -** at least one term. Argument (nTerm/pTerm) is the split-key - a term that -** is larger than all terms written to earlier leaves, and equal to or -** smaller than the first term on the new leaf. -** -** If an error occurs, an error code is left in Fts5Index.rc. If an error -** has already occurred when this function is called, it is a no-op. -*/ -static void fts5WriteBtreeTerm( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegWriter *pWriter, /* Writer object */ - int nTerm, const u8 *pTerm /* First term on new page */ -){ - int iHeight; - for(iHeight=1; 1; iHeight++){ - Fts5PageWriter *pPage; - - if( iHeight>=pWriter->nWriter ){ - fts5WriteBtreeGrow(p, pWriter); - if( p->rc ) return; - } - pPage = &pWriter->aWriter[iHeight]; - - fts5WriteBtreeNEmpty(p, pWriter); - - if( pPage->buf.n>=p->pConfig->pgsz ){ - /* pPage will be written to disk. The term will be written into the - ** parent of pPage. */ - i64 iRowid = FTS5_SEGMENT_ROWID( - pWriter->iIdx, pWriter->iSegid, iHeight, pPage->pgno - ); - fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n); - fts5BufferZero(&pPage->buf); - fts5BufferZero(&pPage->term); - fts5BufferAppendVarint(&p->rc, &pPage->buf, pPage[-1].pgno); - pPage->pgno++; - }else{ - int nPre = fts5PrefixCompress(pPage->term.n, pPage->term.p, nTerm, pTerm); - fts5BufferAppendVarint(&p->rc, &pPage->buf, nPre+2); - fts5BufferAppendVarint(&p->rc, &pPage->buf, nTerm-nPre); - fts5BufferAppendBlob(&p->rc, &pPage->buf, nTerm-nPre, pTerm+nPre); - fts5BufferSet(&p->rc, &pPage->term, nTerm, pTerm); - break; - } - } -} - -static void fts5WriteBtreeNoTerm( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegWriter *pWriter /* Writer object */ -){ - if( pWriter->bFirstRowidInPage ){ - /* No rowids on this page. Append an 0x00 byte to the current - ** doclist-index */ - sqlite3Fts5BufferAppendVarint(&p->rc, &pWriter->dlidx, 0); - } - pWriter->nEmpty++; -} - -/* -** Rowid iRowid has just been appended to the current leaf page. As it is -** the first on its page, append an entry to the current doclist-index. -*/ -static void fts5WriteDlidxAppend( - Fts5Index *p, - Fts5SegWriter *pWriter, - i64 iRowid -){ - i64 iVal; - if( pWriter->bDlidxPrevValid ){ - iVal = pWriter->iDlidxPrev - iRowid; - }else{ - iVal = iRowid; - } - sqlite3Fts5BufferAppendVarint(&p->rc, &pWriter->dlidx, iVal); - pWriter->bDlidxPrevValid = 1; - pWriter->iDlidxPrev = iRowid; -} - -static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){ - static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 }; - Fts5PageWriter *pPage = &pWriter->aWriter[0]; - i64 iRowid; - - if( pPage->term.n==0 ){ - /* No term was written to this page. */ - assert( 0==fts5GetU16(&pPage->buf.p[2]) ); - fts5WriteBtreeNoTerm(p, pWriter); - } - - /* Write the current page to the db. */ - iRowid = FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, 0, pPage->pgno); - fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n); - - /* Initialize the next page. */ - fts5BufferZero(&pPage->buf); - fts5BufferZero(&pPage->term); - fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero); - pPage->pgno++; - - /* Increase the leaves written counter */ - pWriter->nLeafWritten++; -} - -/* -** Append term pTerm/nTerm to the segment being written by the writer passed -** as the second argument. -** -** If an error occurs, set the Fts5Index.rc error code. If an error has -** already occurred, this function is a no-op. -*/ -static void fts5WriteAppendTerm( - Fts5Index *p, - Fts5SegWriter *pWriter, - int nTerm, const u8 *pTerm -){ - int nPrefix; /* Bytes of prefix compression for term */ - Fts5PageWriter *pPage = &pWriter->aWriter[0]; - - assert( pPage==0 || pPage->buf.n==0 || pPage->buf.n>4 ); - if( pPage && pPage->buf.n==0 ){ - /* Zero the first term and first docid fields */ - static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 }; - fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero); - assert( pPage->term.n==0 ); - } - if( p->rc ) return; - - if( pPage->term.n==0 ){ - /* Update the "first term" field of the page header. */ - assert( pPage->buf.p[2]==0 && pPage->buf.p[3]==0 ); - fts5PutU16(&pPage->buf.p[2], pPage->buf.n); - nPrefix = 0; - if( pWriter->aWriter[0].pgno!=1 ){ - fts5WriteBtreeTerm(p, pWriter, nTerm, pTerm); - pPage = &pWriter->aWriter[0]; - } - }else{ - nPrefix = fts5PrefixCompress( - pPage->term.n, pPage->term.p, nTerm, pTerm - ); - fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix); - } - - /* Append the number of bytes of new data, then the term data itself - ** to the page. */ - fts5BufferAppendVarint(&p->rc, &pPage->buf, nTerm - nPrefix); - fts5BufferAppendBlob(&p->rc, &pPage->buf, nTerm - nPrefix, &pTerm[nPrefix]); - - /* Update the Fts5PageWriter.term field. */ - fts5BufferSet(&p->rc, &pPage->term, nTerm, pTerm); - - pWriter->bFirstRowidInPage = 0; - pWriter->bFirstRowidInDoclist = 1; - - /* If the current leaf page is full, flush it to disk. */ - if( pPage->buf.n>=p->pConfig->pgsz ){ - fts5WriteFlushLeaf(p, pWriter); - pWriter->bFirstRowidInPage = 1; - } -} - -/* -** Append a docid to the writers output. -*/ -static void fts5WriteAppendRowid( - Fts5Index *p, - Fts5SegWriter *pWriter, - i64 iRowid -){ - if( p->rc==SQLITE_OK ){ - Fts5PageWriter *pPage = &pWriter->aWriter[0]; - - /* If this is to be the first docid written to the page, set the - ** docid-pointer in the page-header. Also append a value to the dlidx - ** buffer, in case a doclist-index is required. */ - if( pWriter->bFirstRowidInPage ){ - fts5PutU16(pPage->buf.p, pPage->buf.n); - fts5WriteDlidxAppend(p, pWriter, iRowid); - } - - /* Write the docid. */ - if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){ - fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); - }else{ - assert( p->rc || iRowidiPrevRowid ); - fts5BufferAppendVarint(&p->rc, &pPage->buf, pWriter->iPrevRowid - iRowid); - } - pWriter->iPrevRowid = iRowid; - pWriter->bFirstRowidInDoclist = 0; - pWriter->bFirstRowidInPage = 0; - - if( pPage->buf.n>=p->pConfig->pgsz ){ - fts5WriteFlushLeaf(p, pWriter); - pWriter->bFirstRowidInPage = 1; - } - } -} - -static void fts5WriteAppendPoslistInt( - Fts5Index *p, - Fts5SegWriter *pWriter, - int iVal -){ - if( p->rc==SQLITE_OK ){ - Fts5PageWriter *pPage = &pWriter->aWriter[0]; - fts5BufferAppendVarint(&p->rc, &pPage->buf, iVal); - if( pPage->buf.n>=p->pConfig->pgsz ){ - fts5WriteFlushLeaf(p, pWriter); - pWriter->bFirstRowidInPage = 1; - } - } -} - -static void fts5WriteAppendPoslistData( - Fts5Index *p, - Fts5SegWriter *pWriter, - const u8 *aData, - int nData -){ - Fts5PageWriter *pPage = &pWriter->aWriter[0]; - const u8 *a = aData; - int n = nData; - - assert( p->pConfig->pgsz>0 ); - while( p->rc==SQLITE_OK && (pPage->buf.n + n)>=p->pConfig->pgsz ){ - int nReq = p->pConfig->pgsz - pPage->buf.n; - int nCopy = 0; - while( nCopyrc, &pPage->buf, nCopy, a); - a += nCopy; - n -= nCopy; - fts5WriteFlushLeaf(p, pWriter); - pWriter->bFirstRowidInPage = 1; - } - if( n>0 ){ - fts5BufferAppendBlob(&p->rc, &pPage->buf, n, a); - } -} - -static void fts5WriteAppendZerobyte(Fts5Index *p, Fts5SegWriter *pWriter){ - fts5BufferAppendVarint(&p->rc, &pWriter->aWriter[0].buf, 0); -} - -/* -** Flush any data cached by the writer object to the database. Free any -** allocations associated with the writer. -*/ -static void fts5WriteFinish( - Fts5Index *p, - Fts5SegWriter *pWriter, /* Writer object */ - int *pnHeight, /* OUT: Height of the b-tree */ - int *pnLeaf /* OUT: Number of leaf pages in b-tree */ -){ - int i; - if( p->rc==SQLITE_OK ){ - *pnLeaf = pWriter->aWriter[0].pgno; - if( *pnLeaf==1 && pWriter->aWriter[0].buf.n==0 ){ - *pnLeaf = 0; - *pnHeight = 0; - }else{ - fts5WriteFlushLeaf(p, pWriter); - if( pWriter->nWriter==1 && pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){ - fts5WriteBtreeGrow(p, pWriter); - } - if( pWriter->nWriter>1 ){ - fts5WriteBtreeNEmpty(p, pWriter); - } - *pnHeight = pWriter->nWriter; - - for(i=1; inWriter; i++){ - Fts5PageWriter *pPg = &pWriter->aWriter[i]; - fts5DataWrite(p, - FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, i, pPg->pgno), - pPg->buf.p, pPg->buf.n - ); - } - } - } - for(i=0; inWriter; i++){ - Fts5PageWriter *pPg = &pWriter->aWriter[i]; - assert( pPg || p->rc!=SQLITE_OK ); - if( pPg ){ - fts5BufferFree(&pPg->term); - fts5BufferFree(&pPg->buf); - } - } - sqlite3_free(pWriter->aWriter); - sqlite3Fts5BufferFree(&pWriter->dlidx); -} - -static void fts5WriteInit( - Fts5Index *p, - Fts5SegWriter *pWriter, - int iIdx, int iSegid -){ - memset(pWriter, 0, sizeof(Fts5SegWriter)); - pWriter->iIdx = iIdx; - pWriter->iSegid = iSegid; - - pWriter->aWriter = (Fts5PageWriter*)fts5IdxMalloc(p,sizeof(Fts5PageWriter)); - if( pWriter->aWriter==0 ) return; - pWriter->nWriter = 1; - pWriter->aWriter[0].pgno = 1; -} - -static void fts5WriteInitForAppend( - Fts5Index *p, /* FTS5 backend object */ - Fts5SegWriter *pWriter, /* Writer to initialize */ - int iIdx, /* Index segment is a part of */ - Fts5StructureSegment *pSeg /* Segment object to append to */ -){ - int nByte = pSeg->nHeight * sizeof(Fts5PageWriter); - memset(pWriter, 0, sizeof(Fts5SegWriter)); - pWriter->iIdx = iIdx; - pWriter->iSegid = pSeg->iSegid; - pWriter->aWriter = (Fts5PageWriter*)fts5IdxMalloc(p, nByte); - pWriter->nWriter = pSeg->nHeight; - - if( p->rc==SQLITE_OK ){ - int pgno = 1; - int i; - pWriter->aWriter[0].pgno = pSeg->pgnoLast+1; - for(i=pSeg->nHeight-1; i>0; i--){ - i64 iRowid = FTS5_SEGMENT_ROWID(pWriter->iIdx, pWriter->iSegid, i, pgno); - Fts5PageWriter *pPg = &pWriter->aWriter[i]; - pPg->pgno = pgno; - fts5DataBuffer(p, &pPg->buf, iRowid); - if( p->rc==SQLITE_OK ){ - Fts5NodeIter ss; - fts5NodeIterInit(pPg->buf.p, pPg->buf.n, &ss); - while( ss.aData ) fts5NodeIterNext(&p->rc, &ss); - fts5BufferSet(&p->rc, &pPg->term, ss.term.n, ss.term.p); - pgno = ss.iChild; - fts5NodeIterFree(&ss); - } - } - if( pSeg->nHeight==1 ){ - pWriter->nEmpty = pSeg->pgnoLast-1; - } - assert( (pgno+pWriter->nEmpty)==pSeg->pgnoLast ); - } -} - -/* -** Iterator pIter was used to iterate through the input segments of on an -** incremental merge operation. This function is called if the incremental -** merge step has finished but the input has not been completely exhausted. -*/ -static void fts5TrimSegments(Fts5Index *p, Fts5MultiSegIter *pIter){ - int i; - Fts5Buffer buf; - memset(&buf, 0, sizeof(Fts5Buffer)); - for(i=0; inSeg; i++){ - Fts5SegIter *pSeg = &pIter->aSeg[i]; - if( pSeg->pSeg==0 ){ - /* no-op */ - }else if( pSeg->pLeaf==0 ){ - pSeg->pSeg->pgnoLast = 0; - pSeg->pSeg->pgnoFirst = 0; - }else{ - int iOff = pSeg->iTermLeafOffset; /* Offset on new first leaf page */ - i64 iLeafRowid; - Fts5Data *pData; - int iId = pSeg->pSeg->iSegid; - u8 aHdr[4] = {0x00, 0x00, 0x00, 0x04}; - - iLeafRowid = FTS5_SEGMENT_ROWID(pSeg->iIdx, iId, 0, pSeg->iTermLeafPgno); - pData = fts5DataRead(p, iLeafRowid); - if( pData ){ - fts5BufferZero(&buf); - fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); - fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); - fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); - fts5BufferAppendBlob(&p->rc, &buf, pData->n - iOff, &pData->p[iOff]); - fts5DataRelease(pData); - pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; - fts5DataDelete(p, FTS5_SEGMENT_ROWID(pSeg->iIdx, iId, 0, 1),iLeafRowid); - fts5DataWrite(p, iLeafRowid, buf.p, buf.n); - } - } - } - fts5BufferFree(&buf); -} - -/* -** -*/ -static void fts5IndexMergeLevel( - Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ - Fts5Structure **ppStruct, /* IN/OUT: Stucture of index iIdx */ - int iLvl, /* Level to read input from */ - int *pnRem /* Write up to this many output leaves */ -){ - Fts5Structure *pStruct = *ppStruct; - Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; - Fts5StructureLevel *pLvlOut; - Fts5MultiSegIter *pIter = 0; /* Iterator to read input data */ - int nRem = pnRem ? *pnRem : 0; /* Output leaf pages left to write */ - int nInput; /* Number of input segments */ - Fts5SegWriter writer; /* Writer object */ - Fts5StructureSegment *pSeg; /* Output segment */ - Fts5Buffer term; - int bRequireDoclistTerm = 0; /* Doclist terminator (0x00) required */ - int bOldest; /* True if the output segment is the oldest */ - - assert( iLvlnLevel ); - assert( pLvl->nMerge<=pLvl->nSeg ); - - memset(&writer, 0, sizeof(Fts5SegWriter)); - memset(&term, 0, sizeof(Fts5Buffer)); - writer.iIdx = iIdx; - if( pLvl->nMerge ){ - pLvlOut = &pStruct->aLevel[iLvl+1]; - assert( pLvlOut->nSeg>0 ); - nInput = pLvl->nMerge; - fts5WriteInitForAppend(p, &writer, iIdx, &pLvlOut->aSeg[pLvlOut->nSeg-1]); - pSeg = &pLvlOut->aSeg[pLvlOut->nSeg-1]; - }else{ - int iSegid = fts5AllocateSegid(p, pStruct); - - /* Extend the Fts5Structure object as required to ensure the output - ** segment exists. */ - if( iLvl==pStruct->nLevel-1 ){ - fts5StructureAddLevel(&p->rc, ppStruct); - pStruct = *ppStruct; - } - fts5StructureExtendLevel(&p->rc, pStruct, iLvl+1, 1, 0); - pLvl = &pStruct->aLevel[iLvl]; - pLvlOut = &pStruct->aLevel[iLvl+1]; - - fts5WriteInit(p, &writer, iIdx, iSegid); - - /* Add the new segment to the output level */ - if( iLvl+1==pStruct->nLevel ) pStruct->nLevel++; - pSeg = &pLvlOut->aSeg[pLvlOut->nSeg]; - pLvlOut->nSeg++; - pSeg->pgnoFirst = 1; - pSeg->iSegid = iSegid; - - /* Read input from all segments in the input level */ - nInput = pLvl->nSeg; - } - bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2); - -#if 0 -fprintf(stdout, "merging %d segments from level %d!", nInput, iLvl); -fflush(stdout); -#endif - - for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, 0, iLvl, nInput, &pIter); - fts5MultiIterEof(p, pIter)==0; - fts5MultiIterNext(p, pIter, 0, 0) - ){ - Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1] ]; - Fts5ChunkIter sPos; /* Used to iterate through position list */ - - /* If the segment being written is the oldest in the entire index and - ** the position list is empty (i.e. the entry is a delete marker), no - ** entry need be written to the output. */ - fts5ChunkIterInit(p, pSeg, &sPos); - if( bOldest==0 || sPos.nRem>0 ){ - int nTerm; - const u8 *pTerm = fts5MultiIterTerm(pIter, &nTerm); - if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){ - if( pnRem && writer.nLeafWritten>nRem ){ - fts5ChunkIterRelease(&sPos); - break; - } - - /* This is a new term. Append a term to the output segment. */ - if( bRequireDoclistTerm ){ - fts5WriteAppendZerobyte(p, &writer); - } - fts5WriteAppendTerm(p, &writer, nTerm, pTerm); - fts5BufferSet(&p->rc, &term, nTerm, pTerm); - bRequireDoclistTerm = 1; - } - - /* Append the rowid to the output */ - fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter)); - - /* Copy the position list from input to output */ - fts5WriteAppendPoslistInt(p, &writer, sPos.nRem); - for(/* noop */; !fts5ChunkIterEof(p, &sPos); fts5ChunkIterNext(p, &sPos)){ - fts5WriteAppendPoslistData(p, &writer, sPos.p, sPos.n); - } - } - - fts5ChunkIterRelease(&sPos); - } - - /* Flush the last leaf page to disk. Set the output segment b-tree height - ** and last leaf page number at the same time. */ - fts5WriteFinish(p, &writer, &pSeg->nHeight, &pSeg->pgnoLast); - - if( fts5MultiIterEof(p, pIter) ){ - int i; - - /* Remove the redundant segments from the %_data table */ - for(i=0; iaSeg[i].iSegid); - } - - /* Remove the redundant segments from the input level */ - if( pLvl->nSeg!=nInput ){ - int nMove = (pLvl->nSeg - nInput) * sizeof(Fts5StructureSegment); - memmove(pLvl->aSeg, &pLvl->aSeg[nInput], nMove); - } - pLvl->nSeg -= nInput; - pLvl->nMerge = 0; - if( pSeg->pgnoLast==0 ){ - pLvlOut->nSeg--; - } - }else{ - assert( pSeg->nHeight>0 && pSeg->pgnoLast>0 ); - fts5TrimSegments(p, pIter); - pLvl->nMerge = nInput; - } - - fts5MultiIterFree(p, pIter); - fts5BufferFree(&term); - if( pnRem ) *pnRem -= writer.nLeafWritten; -} - -/* -** A total of nLeaf leaf pages of data has just been flushed to a level-0 -** segments in index iIdx with structure pStruct. This function updates the -** write-counter accordingly and, if necessary, performs incremental merge -** work. -** -** If an error occurs, set the Fts5Index.rc error code. If an error has -** already occurred, this function is a no-op. -*/ -static void fts5IndexWork( - Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ - Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */ - int nLeaf /* Number of output leaves just written */ -){ - if( p->rc==SQLITE_OK ){ - Fts5Structure *pStruct = *ppStruct; - i64 nWrite; /* Initial value of write-counter */ - int nWork; /* Number of work-quanta to perform */ - int nRem; /* Number of leaf pages left to write */ - - /* Update the write-counter. While doing so, set nWork. */ - nWrite = pStruct->nWriteCounter; - nWork = ((nWrite + nLeaf) / p->nWorkUnit) - (nWrite / p->nWorkUnit); - pStruct->nWriteCounter += nLeaf; - nRem = p->nWorkUnit * nWork * pStruct->nLevel; - - while( nRem>0 ){ - int iLvl; /* To iterate through levels */ - int iBestLvl = 0; /* Level offering the most input segments */ - int nBest = 0; /* Number of input segments on best level */ - - /* Set iBestLvl to the level to read input segments from. */ - assert( pStruct->nLevel>0 ); - for(iLvl=0; iLvlnLevel; iLvl++){ - Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; - if( pLvl->nMerge ){ - if( pLvl->nMerge>nBest ){ - iBestLvl = iLvl; - nBest = pLvl->nMerge; - } - break; - } - if( pLvl->nSeg>nBest ){ - nBest = pLvl->nSeg; - iBestLvl = iLvl; - } - } - - /* If nBest is still 0, then the index must be empty. */ -#ifdef SQLITE_DEBUG - for(iLvl=0; nBest==0 && iLvlnLevel; iLvl++){ - assert( pStruct->aLevel[iLvl].nSeg==0 ); - } -#endif - - if( nBestpConfig->nAutomerge - && pStruct->aLevel[iBestLvl].nMerge==0 - ){ - break; - } - fts5IndexMergeLevel(p, iIdx, &pStruct, iBestLvl, &nRem); - fts5StructurePromote(p, iBestLvl+1, pStruct); - assert( nRem==0 || p->rc==SQLITE_OK ); - *ppStruct = pStruct; - } - } -} - -static void fts5IndexCrisisMerge( - Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index to work on */ - Fts5Structure **ppStruct /* IN/OUT: Current structure of index */ -){ - Fts5Structure *pStruct = *ppStruct; - int iLvl = 0; - while( p->rc==SQLITE_OK - && iLvlnLevel - && pStruct->aLevel[iLvl].nSeg>=p->nCrisisMerge - ){ - fts5IndexMergeLevel(p, iIdx, &pStruct, iLvl, 0); - fts5StructurePromote(p, iLvl+1, pStruct); - iLvl++; - } - *ppStruct = pStruct; -} - -static int fts5IndexReturn(Fts5Index *p){ - int rc = p->rc; - p->rc = SQLITE_OK; - return rc; -} - -typedef struct Fts5FlushCtx Fts5FlushCtx; -struct Fts5FlushCtx { - Fts5Index *pIdx; - Fts5SegWriter writer; -}; - -static int fts5FlushNewTerm(void *pCtx, const char *zTerm, int nTerm){ - Fts5FlushCtx *p = (Fts5FlushCtx*)pCtx; - int rc = SQLITE_OK; - fts5WriteAppendTerm(p->pIdx, &p->writer, nTerm, (const u8*)zTerm); - return rc; -} - -static int fts5FlushTermDone(void *pCtx){ - Fts5FlushCtx *p = (Fts5FlushCtx*)pCtx; - int rc = SQLITE_OK; - /* Write the doclist terminator */ - fts5WriteAppendZerobyte(p->pIdx, &p->writer); - return rc; -} - -static int fts5FlushNewEntry( - void *pCtx, - i64 iRowid, - const u8 *aPoslist, - int nPoslist -){ - Fts5FlushCtx *p = (Fts5FlushCtx*)pCtx; - Fts5Index *pIdx = p->pIdx; - - /* Append the rowid itself */ - fts5WriteAppendRowid(pIdx, &p->writer, iRowid); - - /* Append the size of the position list in bytes */ - fts5WriteAppendPoslistInt(pIdx, &p->writer, nPoslist); - - /* And the poslist data */ - fts5WriteAppendPoslistData(pIdx, &p->writer, aPoslist, nPoslist); - return pIdx->rc; -} - -/* -** Flush the contents of in-memory hash table iHash to a new level-0 -** segment on disk. Also update the corresponding structure record. -** -** If an error occurs, set the Fts5Index.rc error code. If an error has -** already occurred, this function is a no-op. -*/ -static void fts5FlushOneHash(Fts5Index *p, int iHash, int *pnLeaf){ - Fts5Structure *pStruct; - int iSegid; - int pgnoLast = 0; /* Last leaf page number in segment */ - - /* Obtain a reference to the index structure and allocate a new segment-id - ** for the new level-0 segment. */ - pStruct = fts5StructureRead(p, iHash); - iSegid = fts5AllocateSegid(p, pStruct); - - if( iSegid ){ - Fts5StructureSegment *pSeg; /* New segment within pStruct */ - int nHeight; /* Height of new segment b-tree */ - int rc; - Fts5FlushCtx ctx; - - fts5WriteInit(p, &ctx.writer, iHash, iSegid); - ctx.pIdx = p; - - rc = sqlite3Fts5HashIterate( p->apHash[iHash], (void*)&ctx, - fts5FlushNewTerm, fts5FlushNewEntry, fts5FlushTermDone - ); - if( p->rc==SQLITE_OK ) p->rc = rc; - fts5WriteFinish(p, &ctx.writer, &nHeight, &pgnoLast); - - /* Update the Fts5Structure. It is written back to the database by the - ** fts5StructureRelease() call below. */ - if( pStruct->nLevel==0 ){ - fts5StructureAddLevel(&p->rc, &pStruct); - } - fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0); - if( p->rc==SQLITE_OK ){ - pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ]; - pSeg->iSegid = iSegid; - pSeg->nHeight = nHeight; - pSeg->pgnoFirst = 1; - pSeg->pgnoLast = pgnoLast; - } - } - - if( p->pConfig->nAutomerge>0 ) fts5IndexWork(p, iHash, &pStruct, pgnoLast); - fts5IndexCrisisMerge(p, iHash, &pStruct); - fts5StructureWrite(p, iHash, pStruct); - fts5StructureRelease(pStruct); -} - -/* -** Flush any data stored in the in-memory hash tables to the database. -*/ -static void fts5IndexFlush(Fts5Index *p){ - Fts5Config *pConfig = p->pConfig; - int i; /* Used to iterate through indexes */ - int nLeaf = 0; /* Number of leaves written */ - - /* If an error has already occured this call is a no-op. */ - if( p->rc!=SQLITE_OK || p->nPendingData==0 ) return; - assert( p->apHash ); - - /* Flush the terms and each prefix index to disk */ - for(i=0; i<=pConfig->nPrefix; i++){ - fts5FlushOneHash(p, i, &nLeaf); - } - p->nPendingData = 0; -} - - -int sqlite3Fts5IndexOptimize(Fts5Index *p){ - Fts5Config *pConfig = p->pConfig; - int i; - - fts5IndexFlush(p); - for(i=0; i<=pConfig->nPrefix; i++){ - Fts5Structure *pStruct = fts5StructureRead(p, i); - Fts5Structure *pNew = 0; - int nSeg = 0; - if( pStruct ){ - nSeg = fts5StructureCountSegments(pStruct); - if( nSeg>1 ){ - int nByte = sizeof(Fts5Structure); - nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel); - pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); - } - } - if( pNew ){ - Fts5StructureLevel *pLvl; - int nByte = nSeg * sizeof(Fts5StructureSegment); - pNew->nLevel = pStruct->nLevel+1; - pNew->nWriteCounter = pStruct->nWriteCounter; - pLvl = &pNew->aLevel[pStruct->nLevel]; - pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte); - if( pLvl->aSeg ){ - int iLvl, iSeg; - int iSegOut = 0; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg]; - iSegOut++; - } - } - pLvl->nSeg = nSeg; - }else{ - sqlite3_free(pNew); - pNew = 0; - } - } - - if( pNew ){ - int iLvl = pNew->nLevel-1; - while( p->rc==SQLITE_OK && pNew->aLevel[iLvl].nSeg>0 ){ - int nRem = FTS5_OPT_WORK_UNIT; - fts5IndexMergeLevel(p, i, &pNew, iLvl, &nRem); - } - - fts5StructureWrite(p, i, pNew); - fts5StructureRelease(pNew); - } - - fts5StructureRelease(pStruct); - } - - return fts5IndexReturn(p); -} - - - -/* -** Return a simple checksum value based on the arguments. -*/ -static u64 fts5IndexEntryCksum( - i64 iRowid, - int iCol, - int iPos, - const char *pTerm, - int nTerm -){ - int i; - u64 ret = iRowid; - ret += (ret<<3) + iCol; - ret += (ret<<3) + iPos; - for(i=0; iaLvl[0]) * (pSeg->nHeight-1); - memset(pIter, 0, sizeof(*pIter)); - pIter->nLvl = pSeg->nHeight-1; - pIter->iIdx = iIdx; - pIter->p = p; - pIter->pSeg = pSeg; - if( nByte && p->rc==SQLITE_OK ){ - pIter->aLvl = (Fts5BtreeIterLevel*)fts5IdxMalloc(p, nByte); - } - for(i=0; p->rc==SQLITE_OK && inLvl; i++){ - i64 iRowid = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, i+1, 1); - Fts5Data *pData; - pIter->aLvl[i].pData = pData = fts5DataRead(p, iRowid); - if( pData ){ - fts5NodeIterInit(pData->p, pData->n, &pIter->aLvl[i].s); - } - } - - if( pIter->nLvl==0 || p->rc ){ - pIter->bEof = 1; - pIter->iLeaf = pSeg->pgnoLast; - }else{ - pIter->nEmpty = pIter->aLvl[0].s.nEmpty; - pIter->iLeaf = pIter->aLvl[0].s.iChild; - pIter->bDlidx = pIter->aLvl[0].s.bDlidx; - } -} - -static void fts5BtreeIterNext(Fts5BtreeIter *pIter){ - Fts5Index *p = pIter->p; - int i; - - assert( pIter->bEof==0 && pIter->aLvl[0].s.aData ); - for(i=0; inLvl && p->rc==SQLITE_OK; i++){ - Fts5BtreeIterLevel *pLvl = &pIter->aLvl[i]; - fts5NodeIterNext(&p->rc, &pLvl->s); - if( pLvl->s.aData ){ - fts5BufferSet(&p->rc, &pIter->term, pLvl->s.term.n, pLvl->s.term.p); - break; - }else{ - fts5NodeIterFree(&pLvl->s); - fts5DataRelease(pLvl->pData); - pLvl->pData = 0; - } - } - if( i==pIter->nLvl || p->rc ){ - pIter->bEof = 1; - }else{ - int iSegid = pIter->pSeg->iSegid; - for(i--; i>=0; i--){ - Fts5BtreeIterLevel *pLvl = &pIter->aLvl[i]; - i64 iRowid = FTS5_SEGMENT_ROWID(pIter->iIdx,iSegid,i+1,pLvl[1].s.iChild); - pLvl->pData = fts5DataRead(p, iRowid); - if( pLvl->pData ){ - fts5NodeIterInit(pLvl->pData->p, pLvl->pData->n, &pLvl->s); - } - } - } - - pIter->nEmpty = pIter->aLvl[0].s.nEmpty; - pIter->bDlidx = pIter->aLvl[0].s.bDlidx; - pIter->iLeaf = pIter->aLvl[0].s.iChild; - assert( p->rc==SQLITE_OK || pIter->bEof ); -} - -static void fts5BtreeIterFree(Fts5BtreeIter *pIter){ - int i; - for(i=0; inLvl; i++){ - Fts5BtreeIterLevel *pLvl = &pIter->aLvl[i]; - fts5NodeIterFree(&pLvl->s); - if( pLvl->pData ){ - fts5DataRelease(pLvl->pData); - pLvl->pData = 0; - } - } - sqlite3_free(pIter->aLvl); - fts5BufferFree(&pIter->term); -} - -/* -** This function is purely an internal test. It does not contribute to -** FTS functionality, or even the integrity-check, in any way. -** -** Instead, it tests that the same set of pgno/rowid combinations are -** visited regardless of whether the doclist-index identified by parameters -** iIdx/iSegid/iLeaf is iterated in forwards or reverse order. -*/ -#ifdef SQLITE_DEBUG -static void fts5DlidxIterTestReverse( - Fts5Index *p, - int iIdx, /* Index to load doclist-index from */ - int iSegid, /* Segment id to load from */ - int iLeaf /* Load doclist-index for this leaf */ -){ - Fts5DlidxIter *pDlidx = 0; - i64 cksum1 = 13; - i64 cksum2 = 13; - - for(fts5DlidxIterInit(p, 0, iIdx, iSegid, iLeaf, &pDlidx); - fts5DlidxIterEof(p, pDlidx)==0; - fts5DlidxIterNext(pDlidx) - ){ - assert( pDlidx->iLeafPgno>iLeaf ); - cksum1 = (cksum1 ^ ( (i64)(pDlidx->iLeafPgno) << 32 )); - cksum1 = (cksum1 ^ pDlidx->iRowid); - } - fts5DlidxIterFree(pDlidx); - pDlidx = 0; - - for(fts5DlidxIterInit(p, 1, iIdx, iSegid, iLeaf, &pDlidx); - fts5DlidxIterEof(p, pDlidx)==0; - fts5DlidxIterPrev(pDlidx) - ){ - assert( pDlidx->iLeafPgno>iLeaf ); - cksum2 = (cksum2 ^ ( (i64)(pDlidx->iLeafPgno) << 32 )); - cksum2 = (cksum2 ^ pDlidx->iRowid); - } - fts5DlidxIterFree(pDlidx); - pDlidx = 0; - - if( p->rc==SQLITE_OK && cksum1!=cksum2 ) p->rc = FTS5_CORRUPT; -} -#else -# define fts5DlidxIterTestReverse(w,x,y,z) -#endif - -static void fts5IndexIntegrityCheckSegment( - Fts5Index *p, /* FTS5 backend object */ - int iIdx, /* Index that pSeg is a part of */ - Fts5StructureSegment *pSeg /* Segment to check internal consistency */ -){ - Fts5BtreeIter iter; /* Used to iterate through b-tree hierarchy */ - - /* Iterate through the b-tree hierarchy. */ - for(fts5BtreeIterInit(p, iIdx, pSeg, &iter); - iter.bEof==0; - fts5BtreeIterNext(&iter) - ){ - i64 iRow; /* Rowid for this leaf */ - Fts5Data *pLeaf; /* Data for this leaf */ - int iOff; /* Offset of first term on leaf */ - int i; /* Used to iterate through empty leaves */ - - /* If the leaf in question has already been trimmed from the segment, - ** ignore this b-tree entry. Otherwise, load it into memory. */ - if( iter.iLeafpgnoFirst ) continue; - iRow = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, 0, iter.iLeaf); - pLeaf = fts5DataRead(p, iRow); - if( pLeaf==0 ) break; - - /* Check that the leaf contains at least one term, and that it is equal - ** to or larger than the split-key in iter.term. */ - iOff = fts5GetU16(&pLeaf->p[2]); - if( iOff==0 ){ - p->rc = FTS5_CORRUPT; - }else{ - int nTerm; /* Size of term on leaf in bytes */ - int res; /* Comparison of term and split-key */ - iOff += getVarint32(&pLeaf->p[iOff], nTerm); - res = memcmp(&pLeaf->p[iOff], iter.term.p, MIN(nTerm, iter.term.n)); - if( res==0 ) res = nTerm - iter.term.n; - if( res<0 ){ - p->rc = FTS5_CORRUPT; - } - } - fts5DataRelease(pLeaf); - if( p->rc ) break; - - /* Now check that the iter.nEmpty leaves following the current leaf - ** (a) exist and (b) contain no terms. */ - for(i=1; p->rc==SQLITE_OK && i<=iter.nEmpty; i++){ - pLeaf = fts5DataRead(p, iRow+i); - if( pLeaf && 0!=fts5GetU16(&pLeaf->p[2]) ){ - p->rc = FTS5_CORRUPT; - } - fts5DataRelease(pLeaf); - } - - /* If there is a doclist-index, check that it looks right. */ - if( iter.bDlidx ){ - Fts5DlidxIter *pDlidx = 0; /* For iterating through doclist index */ - int iPrevLeaf = iter.iLeaf; - int iSegid = pSeg->iSegid; - int iPg; - i64 iKey; - - for(fts5DlidxIterInit(p, 0, iIdx, iSegid, iter.iLeaf, &pDlidx); - fts5DlidxIterEof(p, pDlidx)==0; - fts5DlidxIterNext(pDlidx) - ){ - - /* Check any rowid-less pages that occur before the current leaf. */ - for(iPg=iPrevLeaf+1; iPgiLeafPgno; iPg++){ - iKey = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, iPg); - pLeaf = fts5DataRead(p, iKey); - if( pLeaf ){ - if( fts5GetU16(&pLeaf->p[0])!=0 ) p->rc = FTS5_CORRUPT; - fts5DataRelease(pLeaf); - } - } - iPrevLeaf = pDlidx->iLeafPgno; - - /* Check that the leaf page indicated by the iterator really does - ** contain the rowid suggested by the same. */ - iKey = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, pDlidx->iLeafPgno); - pLeaf = fts5DataRead(p, iKey); - if( pLeaf ){ - i64 iRowid; - int iRowidOff = fts5GetU16(&pLeaf->p[0]); - getVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid); - if( iRowid!=pDlidx->iRowid ) p->rc = FTS5_CORRUPT; - fts5DataRelease(pLeaf); - } - - } - - for(iPg=iPrevLeaf+1; iPg<=(iter.iLeaf + iter.nEmpty); iPg++){ - iKey = FTS5_SEGMENT_ROWID(iIdx, iSegid, 0, iPg); - pLeaf = fts5DataRead(p, iKey); - if( pLeaf ){ - if( fts5GetU16(&pLeaf->p[0])!=0 ) p->rc = FTS5_CORRUPT; - fts5DataRelease(pLeaf); - } - } - - fts5DlidxIterFree(pDlidx); - fts5DlidxIterTestReverse(p, iIdx, iSegid, iter.iLeaf); - } - } - - if( p->rc==SQLITE_OK && iter.iLeaf!=pSeg->pgnoLast ){ - p->rc = FTS5_CORRUPT; - } - - fts5BtreeIterFree(&iter); -} - -/* -** Iterator pMulti currently points to a valid entry (not EOF). This -** function appends a copy of the position-list of the entry pMulti -** currently points to to buffer pBuf. -** -** If an error occurs, an error code is left in p->rc. It is assumed -** no error has already occurred when this function is called. -*/ -static void fts5MultiIterPoslist( - Fts5Index *p, - Fts5MultiSegIter *pMulti, - int bSz, - Fts5Buffer *pBuf -){ - if( p->rc==SQLITE_OK ){ - Fts5ChunkIter iter; - Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ]; - assert( fts5MultiIterEof(p, pMulti)==0 ); - fts5ChunkIterInit(p, pSeg, &iter); - if( fts5ChunkIterEof(p, &iter)==0 ){ - if( bSz ){ - fts5BufferAppendVarint(&p->rc, pBuf, iter.nRem); - } - while( fts5ChunkIterEof(p, &iter)==0 ){ - fts5BufferAppendBlob(&p->rc, pBuf, iter.n, iter.p); - fts5ChunkIterNext(p, &iter); - } - } - fts5ChunkIterRelease(&iter); - } -} - -static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ - if( pIter->in ){ - if( pIter->i ){ - i64 iDelta; - pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&iDelta); - if( pIter->bAsc ){ - pIter->iRowid += iDelta; - }else{ - pIter->iRowid -= iDelta; - } - }else{ - pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&pIter->iRowid); - } - pIter->i += getVarint32(&pIter->a[pIter->i], pIter->nPoslist); - pIter->aPoslist = &pIter->a[pIter->i]; - pIter->i += pIter->nPoslist; - }else{ - pIter->aPoslist = 0; - } -} - -static void fts5DoclistIterInit( - Fts5Buffer *pBuf, - int bAsc, - Fts5DoclistIter *pIter -){ - memset(pIter, 0, sizeof(*pIter)); - pIter->a = pBuf->p; - pIter->n = pBuf->n; - pIter->bAsc = bAsc; - fts5DoclistIterNext(pIter); -} - -/* -** Append a doclist to buffer pBuf. -*/ -static void fts5MergeAppendDocid( - int *pRc, /* IN/OUT: Error code */ - int bAsc, - Fts5Buffer *pBuf, /* Buffer to write to */ - i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */ - i64 iRowid /* Rowid to append */ -){ - if( pBuf->n==0 ){ - fts5BufferAppendVarint(pRc, pBuf, iRowid); - }else if( bAsc==0 ){ - fts5BufferAppendVarint(pRc, pBuf, *piLastRowid - iRowid); - }else{ - fts5BufferAppendVarint(pRc, pBuf, iRowid - *piLastRowid); - } - *piLastRowid = iRowid; -} - -/* -** Buffers p1 and p2 contain doclists. This function merges the content -** of the two doclists together and sets buffer p1 to the result before -** returning. -** -** If an error occurs, an error code is left in p->rc. If an error has -** already occurred, this function is a no-op. -*/ -static void fts5MergePrefixLists( - Fts5Index *p, /* FTS5 backend object */ - int bAsc, - Fts5Buffer *p1, /* First list to merge */ - Fts5Buffer *p2 /* Second list to merge */ -){ - if( p2->n ){ - i64 iLastRowid = 0; - Fts5DoclistIter i1; - Fts5DoclistIter i2; - Fts5Buffer out; - Fts5Buffer tmp; - memset(&out, 0, sizeof(out)); - memset(&tmp, 0, sizeof(tmp)); - - fts5DoclistIterInit(p1, bAsc, &i1); - fts5DoclistIterInit(p2, bAsc, &i2); - while( i1.aPoslist!=0 || i2.aPoslist!=0 ){ - if( i2.aPoslist==0 || (i1.aPoslist && - ( (!bAsc && i1.iRowid>i2.iRowid) || (bAsc && i1.iRowidrc, bAsc, &out, &iLastRowid, i1.iRowid); - fts5BufferAppendVarint(&p->rc, &out, i1.nPoslist); - fts5BufferAppendBlob(&p->rc, &out, i1.nPoslist, i1.aPoslist); - fts5DoclistIterNext(&i1); - } - else if( i1.aPoslist==0 || i2.iRowid!=i1.iRowid ){ - /* Copy entry from i2 */ - fts5MergeAppendDocid(&p->rc, bAsc, &out, &iLastRowid, i2.iRowid); - fts5BufferAppendVarint(&p->rc, &out, i2.nPoslist); - fts5BufferAppendBlob(&p->rc, &out, i2.nPoslist, i2.aPoslist); - fts5DoclistIterNext(&i2); - } - else{ - Fts5PoslistReader r1; - Fts5PoslistReader r2; - Fts5PoslistWriter writer; - - memset(&writer, 0, sizeof(writer)); - - /* Merge the two position lists. */ - fts5MergeAppendDocid(&p->rc, bAsc, &out, &iLastRowid, i2.iRowid); - fts5BufferZero(&tmp); - sqlite3Fts5PoslistReaderInit(-1, i1.aPoslist, i1.nPoslist, &r1); - sqlite3Fts5PoslistReaderInit(-1, i2.aPoslist, i2.nPoslist, &r2); - while( p->rc==SQLITE_OK && (r1.bEof==0 || r2.bEof==0) ){ - i64 iNew; - if( r2.bEof || (r1.bEof==0 && r1.iPosrc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew); - } - - fts5BufferAppendVarint(&p->rc, &out, tmp.n); - fts5BufferAppendBlob(&p->rc, &out, tmp.n, tmp.p); - fts5DoclistIterNext(&i1); - fts5DoclistIterNext(&i2); - } - } - - fts5BufferSet(&p->rc, p1, out.n, out.p); - fts5BufferFree(&tmp); - fts5BufferFree(&out); - } -} - -static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){ - Fts5Buffer tmp = *p1; - *p1 = *p2; - *p2 = tmp; -} - -static void fts5SetupPrefixIter( - Fts5Index *p, /* Index to read from */ - int bAsc, /* True for "ORDER BY rowid ASC" */ - const u8 *pToken, /* Buffer containing prefix to match */ - int nToken, /* Size of buffer pToken in bytes */ - Fts5IndexIter *pIter /* Populate this object */ -){ - Fts5Structure *pStruct; - Fts5Buffer *aBuf; - const int nBuf = 32; - - aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); - pStruct = fts5StructureRead(p, 0); - - if( aBuf && pStruct ){ - Fts5DoclistIter *pDoclist; - int i; - i64 iLastRowid = 0; - Fts5MultiSegIter *p1 = 0; /* Iterator used to gather data from index */ - Fts5Buffer doclist; - - memset(&doclist, 0, sizeof(doclist)); - for(fts5MultiIterNew(p, pStruct, 0, 1, 1, pToken, nToken, -1, 0, &p1); - fts5MultiIterEof(p, p1)==0; - fts5MultiIterNext(p, p1, 0, 0) - ){ - i64 iRowid = fts5MultiIterRowid(p1); - int nTerm; - const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm); - assert( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 ); - if( nTerm0 - && ((!bAsc && iRowid>=iLastRowid) || (bAsc && iRowid<=iLastRowid)) - ){ - - for(i=0; doclist.n && p->rc==SQLITE_OK; i++){ - assert( irc, &doclist, iRowid); - }else if( bAsc==0 ){ - fts5BufferAppendVarint(&p->rc, &doclist, iLastRowid - iRowid); - }else{ - fts5BufferAppendVarint(&p->rc, &doclist, iRowid - iLastRowid); - } - iLastRowid = iRowid; - fts5MultiIterPoslist(p, p1, 1, &doclist); - } - - for(i=0; ipDoclist = pDoclist; - fts5DoclistIterInit(&doclist, bAsc, pIter->pDoclist); - } - } - - fts5StructureRelease(pStruct); - sqlite3_free(aBuf); -} - -/* -** Run internal checks to ensure that the FTS index (a) is internally -** consistent and (b) contains entries for which the XOR of the checksums -** as calculated by fts5IndexEntryCksum() is cksum. -** -** Return SQLITE_CORRUPT if any of the internal checks fail, or if the -** checksum does not match. Return SQLITE_OK if all checks pass without -** error, or some other SQLite error code if another error (e.g. OOM) -** occurs. -*/ -int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ - Fts5Config *pConfig = p->pConfig; - int iIdx; /* Used to iterate through indexes */ - int rc; /* Return code */ - u64 cksum2 = 0; /* Checksum based on contents of indexes */ - - /* Check that the checksum of the index matches the argument checksum */ - for(iIdx=0; iIdx<=pConfig->nPrefix; iIdx++){ - Fts5MultiSegIter *pIter; - Fts5Structure *pStruct = fts5StructureRead(p, iIdx); - for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, 0, -1, 0, &pIter); - fts5MultiIterEof(p, pIter)==0; - fts5MultiIterNext(p, pIter, 0, 0) - ){ - Fts5PosIter sPos; /* Used to iterate through position list */ - int n; /* Size of term in bytes */ - i64 iRowid = fts5MultiIterRowid(pIter); - char *z = (char*)fts5MultiIterTerm(pIter, &n); - - for(fts5PosIterInit(p, pIter, &sPos); - fts5PosIterEof(p, &sPos)==0; - fts5PosIterNext(p, &sPos) - ){ - cksum2 ^= fts5IndexEntryCksum(iRowid, sPos.iCol, sPos.iPos, z, n); -#if 0 - fprintf(stdout, "rowid=%d ", (int)iRowid); - fprintf(stdout, "term=%.*s ", n, z); - fprintf(stdout, "col=%d ", sPos.iCol); - fprintf(stdout, "off=%d\n", sPos.iPos); - fflush(stdout); -#endif - } - } - fts5MultiIterFree(p, pIter); - fts5StructureRelease(pStruct); - } - rc = p->rc; - if( rc==SQLITE_OK && cksum!=cksum2 ) rc = FTS5_CORRUPT; - - /* Check that the internal nodes of each segment match the leaves */ - for(iIdx=0; rc==SQLITE_OK && iIdx<=pConfig->nPrefix; iIdx++){ - Fts5Structure *pStruct = fts5StructureRead(p, iIdx); - if( pStruct ){ - int iLvl, iSeg; - for(iLvl=0; iLvlnLevel; iLvl++){ - for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; - fts5IndexIntegrityCheckSegment(p, iIdx, pSeg); - } - } - } - fts5StructureRelease(pStruct); - rc = p->rc; - } - - return rc; -} - - -/* -** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain -** to the document with rowid iRowid. -*/ -int sqlite3Fts5IndexBeginWrite(Fts5Index *p, i64 iRowid){ - assert( p->rc==SQLITE_OK ); - if( iRowid<=p->iWriteRowid || (p->nPendingData > p->nMaxPendingData) ){ - fts5IndexFlush(p); - } - p->iWriteRowid = iRowid; - return fts5IndexReturn(p); -} - -/* -** Commit data to disk. -*/ -int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit){ - assert( p->rc==SQLITE_OK ); - fts5IndexFlush(p); - if( bCommit ) fts5CloseReader(p); - return fts5IndexReturn(p); -} - -/* -** Discard any data stored in the in-memory hash tables. Do not write it -** to the database. Additionally, assume that the contents of the %_data -** table may have changed on disk. So any in-memory caches of %_data -** records must be invalidated. -*/ -int sqlite3Fts5IndexRollback(Fts5Index *p){ - fts5CloseReader(p); - fts5IndexDiscardData(p); - assert( p->rc==SQLITE_OK ); - return SQLITE_OK; -} - -/* -** The %_data table is completely empty when this function is called. This -** function populates it with the initial structure objects for each index, -** and the initial version of the "averages" record (a zero-byte blob). -*/ -int sqlite3Fts5IndexReinit(Fts5Index *p){ - int i; - Fts5Structure s; - - memset(&s, 0, sizeof(Fts5Structure)); - for(i=0; ipConfig->nPrefix+1; i++){ - fts5StructureWrite(p, i, &s); - } - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3Fts5IndexSetAverages(p, (const u8*)"", 0); - } - - return fts5IndexReturn(p); -} - -/* -** Open a new Fts5Index handle. If the bCreate argument is true, create -** and initialize the underlying %_data table. -** -** If successful, set *pp to point to the new object and return SQLITE_OK. -** Otherwise, set *pp to NULL and return an SQLite error code. -*/ -int sqlite3Fts5IndexOpen( - Fts5Config *pConfig, - int bCreate, - Fts5Index **pp, - char **pzErr -){ - int rc = SQLITE_OK; - Fts5Index *p; /* New object */ - - *pp = p = (Fts5Index*)sqlite3_malloc(sizeof(Fts5Index)); - if( !p ) return SQLITE_NOMEM; - - memset(p, 0, sizeof(Fts5Index)); - p->pConfig = pConfig; - p->nCrisisMerge = FTS5_CRISIS_MERGE; - p->nWorkUnit = FTS5_WORK_UNIT; - p->nMaxPendingData = 1024*1024; - p->zDataTbl = sqlite3_mprintf("%s_data", pConfig->zName); - if( p->zDataTbl==0 ){ - rc = SQLITE_NOMEM; - }else if( bCreate ){ - rc = sqlite3Fts5CreateTable( - pConfig, "data", "id INTEGER PRIMARY KEY, block BLOB", 0, pzErr - ); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexReinit(p); - } - } - - assert( p->rc==SQLITE_OK || rc!=SQLITE_OK ); - if( rc ){ - sqlite3Fts5IndexClose(p, 0); - *pp = 0; - } - return rc; -} - -/* -** Close a handle opened by an earlier call to sqlite3Fts5IndexOpen(). -*/ -int sqlite3Fts5IndexClose(Fts5Index *p, int bDestroy){ - int rc = SQLITE_OK; - if( p ){ - if( bDestroy ){ - rc = sqlite3Fts5DropTable(p->pConfig, "data"); - } - assert( p->pReader==0 ); - sqlite3_finalize(p->pWriter); - sqlite3_finalize(p->pDeleter); - if( p->apHash ){ - int i; - for(i=0; i<=p->pConfig->nPrefix; i++){ - sqlite3Fts5HashFree(p->apHash[i]); - } - sqlite3_free(p->apHash); - } - sqlite3_free(p->zDataTbl); - sqlite3_free(p); - } - return rc; -} - -/* -** Argument p points to a buffer containing utf-8 text that is n bytes in -** size. Return the number of bytes in the nChar character prefix of the -** buffer, or 0 if there are less than nChar characters in total. -*/ -static int fts5IndexCharlenToBytelen(const char *p, int nByte, int nChar){ - int n = 0; - int i; - for(i=0; i=nByte ) return 0; /* Input contains fewer than nChar chars */ - if( (unsigned char)p[n++]>=0xc0 ){ - while( (p[n] & 0xc0)==0x80 ) n++; - } - } - return n; -} - -/* -** pIn is a UTF-8 encoded string, nIn bytes in size. Return the number of -** unicode characters in the string. -*/ -int fts5IndexCharlen(const char *pIn, int nIn){ - int nChar = 0; - int i = 0; - while( i=0xc0 ){ - while( inPrefix; iIdx++){ - int nByte = fts5IndexCharlenToBytelen(pTerm, nTerm, pConfig->aPrefix[iIdx]); - if( nByte ){ - ret ^= fts5IndexEntryCksum(iRowid, iCol, iPos, pTerm, nByte); - } - } - - return ret; -} - -/* -** Insert or remove data to or from the index. Each time a document is -** added to or removed from the index, this function is called one or more -** times. -** -** For an insert, it must be called once for each token in the new document. -** If the operation is a delete, it must be called (at least) once for each -** unique token in the document with an iCol value less than zero. The iPos -** argument is ignored for a delete. -*/ -int sqlite3Fts5IndexWrite( - Fts5Index *p, /* Index to write to */ - int iCol, /* Column token appears in (-ve -> delete) */ - int iPos, /* Position of token within column */ - const char *pToken, int nToken /* Token to add or remove to or from index */ -){ - int i; /* Used to iterate through indexes */ - Fts5Config *pConfig = p->pConfig; - assert( p->rc==SQLITE_OK ); - - /* Allocate hash tables if they have not already been allocated */ - if( p->apHash==0 ){ - int nHash = pConfig->nPrefix + 1; - p->apHash = (Fts5Hash**)fts5IdxMalloc(p, sizeof(Fts5Hash*) * nHash); - for(i=0; p->rc==SQLITE_OK && irc = sqlite3Fts5HashNew(&p->apHash[i], &p->nPendingData); - } - } - - /* Add the new token to the main terms hash table. And to each of the - ** prefix hash tables that it is large enough for. */ - fts5AddTermToHash(p, 0, iCol, iPos, pToken, nToken); - for(i=0; inPrefix; i++){ - int nByte = fts5IndexCharlenToBytelen(pToken, nToken, pConfig->aPrefix[i]); - if( nByte ){ - fts5AddTermToHash(p, i+1, iCol, iPos, pToken, nByte); - } - } - - return fts5IndexReturn(p); -} - -/* -** Open a new iterator to iterate though all docids that match the -** specified token or token prefix. -*/ -int sqlite3Fts5IndexQuery( - Fts5Index *p, /* FTS index to query */ - const char *pToken, int nToken, /* Token (or prefix) to query for */ - int flags, /* Mask of FTS5INDEX_QUERY_X flags */ - Fts5IndexIter **ppIter /* OUT: New iterator object */ -){ - Fts5IndexIter *pRet; - int iIdx = 0; - - if( flags & FTS5INDEX_QUERY_PREFIX ){ - Fts5Config *pConfig = p->pConfig; - int nChar = fts5IndexCharlen(pToken, nToken); - for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){ - if( pConfig->aPrefix[iIdx-1]==nChar ) break; - } - if( iIdx>pConfig->nPrefix ){ - iIdx = -1; - } - } - - pRet = (Fts5IndexIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5IndexIter)); - if( pRet ){ - memset(pRet, 0, sizeof(Fts5IndexIter)); - - pRet->pIndex = p; - if( iIdx>=0 ){ - pRet->pStruct = fts5StructureRead(p, iIdx); - if( pRet->pStruct ){ - fts5MultiIterNew(p, pRet->pStruct, - iIdx, 1, flags, (const u8*)pToken, nToken, -1, 0, &pRet->pMulti - ); - } - }else{ - int bAsc = (flags & FTS5INDEX_QUERY_ASC)!=0; - fts5SetupPrefixIter(p, bAsc, (const u8*)pToken, nToken, pRet); - } - } - - if( p->rc ){ - sqlite3Fts5IterClose(pRet); - pRet = 0; - } - *ppIter = pRet; - return fts5IndexReturn(p); -} - -/* -** Return true if the iterator passed as the only argument is at EOF. -*/ -int sqlite3Fts5IterEof(Fts5IndexIter *pIter){ - assert( pIter->pIndex->rc==SQLITE_OK ); - if( pIter->pDoclist ){ - return pIter->pDoclist->aPoslist==0; - }else{ - return fts5MultiIterEof(pIter->pIndex, pIter->pMulti); - } -} - -/* -** Move to the next matching rowid. -*/ -int sqlite3Fts5IterNext(Fts5IndexIter *pIter){ - assert( pIter->pIndex->rc==SQLITE_OK ); - if( pIter->pDoclist ){ - fts5DoclistIterNext(pIter->pDoclist); - }else{ - fts5BufferZero(&pIter->poslist); - fts5MultiIterNext(pIter->pIndex, pIter->pMulti, 0, 0); - } - return fts5IndexReturn(pIter->pIndex); -} - -/* -** Move the doclist-iter passed as the first argument to the next -** matching rowid that occurs at or after iMatch. The definition of "at -** or after" depends on whether this iterator iterates in ascending or -** descending rowid order. -*/ -static void fts5DoclistIterNextFrom(Fts5DoclistIter *p, i64 iMatch){ - do{ - i64 iRowid = p->iRowid; - if( p->bAsc!=0 && iRowid>=iMatch ) break; - if( p->bAsc==0 && iRowid<=iMatch ) break; - fts5DoclistIterNext(p); - }while( p->aPoslist ); -} - -/* -** Move to the next matching rowid that occurs at or after iMatch. The -** definition of "at or after" depends on whether this iterator iterates -** in ascending or descending rowid order. -*/ -int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIter, i64 iMatch){ - if( pIter->pDoclist ){ - fts5DoclistIterNextFrom(pIter->pDoclist, iMatch); - }else{ - fts5MultiIterNextFrom(pIter->pIndex, pIter->pMulti, iMatch); - } - return fts5IndexReturn(pIter->pIndex); -} - -/* -** Return the current rowid. -*/ -i64 sqlite3Fts5IterRowid(Fts5IndexIter *pIter){ - if( pIter->pDoclist ){ - return pIter->pDoclist->iRowid; - }else{ - return fts5MultiIterRowid(pIter->pMulti); - } -} - - -/* -** Return a pointer to a buffer containing a copy of the position list for -** the current entry. Output variable *pn is set to the size of the buffer -** in bytes before returning. -** -** The returned buffer does not include the 0x00 terminator byte stored on -** disk. -*/ -int sqlite3Fts5IterPoslist(Fts5IndexIter *pIter, const u8 **pp, int *pn){ - assert( pIter->pIndex->rc==SQLITE_OK ); - if( pIter->pDoclist ){ - *pn = pIter->pDoclist->nPoslist; - *pp = pIter->pDoclist->aPoslist; - }else{ - Fts5Index *p = pIter->pIndex; - fts5BufferZero(&pIter->poslist); - fts5MultiIterPoslist(p, pIter->pMulti, 0, &pIter->poslist); - *pn = pIter->poslist.n; - *pp = pIter->poslist.p; - } - return fts5IndexReturn(pIter->pIndex); -} - -/* -** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). -*/ -void sqlite3Fts5IterClose(Fts5IndexIter *pIter){ - if( pIter ){ - if( pIter->pDoclist ){ - sqlite3_free(pIter->pDoclist->a); - sqlite3_free(pIter->pDoclist); - }else{ - fts5MultiIterFree(pIter->pIndex, pIter->pMulti); - fts5StructureRelease(pIter->pStruct); - fts5BufferFree(&pIter->poslist); - } - fts5CloseReader(pIter->pIndex); - sqlite3_free(pIter); - } -} - -/* -** Read the "averages" record into the buffer supplied as the second -** argument. Return SQLITE_OK if successful, or an SQLite error code -** if an error occurs. -*/ -int sqlite3Fts5IndexGetAverages(Fts5Index *p, Fts5Buffer *pBuf){ - assert( p->rc==SQLITE_OK ); - fts5DataReadOrBuffer(p, pBuf, FTS5_AVERAGES_ROWID); - return fts5IndexReturn(p); -} - -/* -** Replace the current "averages" record with the contents of the buffer -** supplied as the second argument. -*/ -int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8 *pData, int nData){ - assert( p->rc==SQLITE_OK ); - fts5DataWrite(p, FTS5_AVERAGES_ROWID, pData, nData); - return fts5IndexReturn(p); -} - -/* -** Return the total number of blocks this module has read from the %_data -** table since it was created. -*/ -int sqlite3Fts5IndexReads(Fts5Index *p){ - return p->nRead; -} - -/* -** Set the 32-bit cookie value stored at the start of all structure -** records to the value passed as the second argument. -** -** Return SQLITE_OK if successful, or an SQLite error code if an error -** occurs. -*/ -int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){ - int rc = SQLITE_OK; - Fts5Config *pConfig = p->pConfig; - u8 aCookie[4]; - int i; - - assert( p->rc==SQLITE_OK ); - sqlite3Fts5Put32(aCookie, iNew); - for(i=0; rc==SQLITE_OK && i<=pConfig->nPrefix; i++){ - sqlite3_blob *pBlob = 0; - i64 iRowid = FTS5_STRUCTURE_ROWID(i); - rc = sqlite3_blob_open( - pConfig->db, pConfig->zDb, p->zDataTbl, "block", iRowid, 1, &pBlob - ); - if( rc==SQLITE_OK ){ - sqlite3_blob_write(pBlob, aCookie, 4, 0); - rc = sqlite3_blob_close(pBlob); - } - } - - return rc; -} - -/************************************************************************* -************************************************************************** -** Below this point is the implementation of the fts5_decode() scalar -** function only. -*/ - -/* -** Decode a segment-data rowid from the %_data table. This function is -** the opposite of macro FTS5_SEGMENT_ROWID(). -*/ -static void fts5DecodeRowid( - i64 iRowid, /* Rowid from %_data table */ - int *piIdx, /* OUT: Index */ - int *piSegid, /* OUT: Segment id */ - int *piHeight, /* OUT: Height */ - int *piPgno /* OUT: Page number */ -){ - *piPgno = (int)(iRowid & (((i64)1 << FTS5_DATA_PAGE_B) - 1)); - iRowid >>= FTS5_DATA_PAGE_B; - - *piHeight = (int)(iRowid & (((i64)1 << FTS5_DATA_HEIGHT_B) - 1)); - iRowid >>= FTS5_DATA_HEIGHT_B; - - *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1)); - iRowid >>= FTS5_DATA_ID_B; - - *piIdx = (int)(iRowid & (((i64)1 << FTS5_DATA_IDX_B) - 1)); -} - -static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ - int iIdx,iSegid,iHeight,iPgno; /* Rowid compenents */ - fts5DecodeRowid(iKey, &iIdx, &iSegid, &iHeight, &iPgno); - - if( iSegid==0 ){ - if( iKey==FTS5_AVERAGES_ROWID ){ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(averages) "); - }else{ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, - "{structure idx=%d}", (int)(iKey-10) - ); - } - } - else if( iHeight==FTS5_SEGMENT_MAX_HEIGHT ){ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(dlidx idx=%d segid=%d pgno=%d)", - iIdx, iSegid, iPgno - ); - }else{ - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "(idx=%d segid=%d h=%d pgno=%d)", - iIdx, iSegid, iHeight, iPgno - ); - } -} - -static void fts5DebugStructure( - int *pRc, /* IN/OUT: error code */ - Fts5Buffer *pBuf, - Fts5Structure *p -){ - int iLvl, iSeg; /* Iterate through levels, segments */ - - for(iLvl=0; iLvlnLevel; iLvl++){ - Fts5StructureLevel *pLvl = &p->aLevel[iLvl]; - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, - " {lvl=%d nMerge=%d", iLvl, pLvl->nMerge - ); - for(iSeg=0; iSegnSeg; iSeg++){ - Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg]; - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, - " {id=%d h=%d leaves=%d..%d}", pSeg->iSegid, pSeg->nHeight, - pSeg->pgnoFirst, pSeg->pgnoLast - ); - } - sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}"); - } -} - -/* -** This is part of the fts5_decode() debugging aid. -** -** Arguments pBlob/nBlob contain a serialized Fts5Structure object. This -** function appends a human-readable representation of the same object -** to the buffer passed as the second argument. -*/ -static void fts5DecodeStructure( - int *pRc, /* IN/OUT: error code */ - Fts5Buffer *pBuf, - const u8 *pBlob, int nBlob -){ - int rc; /* Return code */ - Fts5Structure *p = 0; /* Decoded structure object */ - - rc = fts5StructureDecode(pBlob, nBlob, 0, &p); - if( rc!=SQLITE_OK ){ - *pRc = rc; - return; - } - - fts5DebugStructure(pRc, pBuf, p); - fts5StructureRelease(p); -} - -/* -** Buffer (a/n) is assumed to contain a list of serialized varints. Read -** each varint and append its string representation to buffer pBuf. Return -** after either the input buffer is exhausted or a 0 value is read. -** -** The return value is the number of bytes read from the input buffer. -*/ -static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){ - int iOff = 0; - while( iOff0 ){ - i = getVarint(&a[i], (u64*)&iPrev); - sqlite3Fts5BufferAppendPrintf(&rc, &s, " %lld", iPrev); - } - while( i=0 && eStmtaStmt) ); - if( p->aStmt[eStmt]==0 ){ - const char *azStmt[] = { - "SELECT * FROM %s ORDER BY %s ASC", /* SCAN_ASC */ - "SELECT * FROM %s ORDER BY %s DESC", /* SCAN_DESC */ - "SELECT * FROM %s WHERE %s=?", /* LOOKUP */ - - "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */ - "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */ - "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */ - "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */ - "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */ - - "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */ - - "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */ - }; - Fts5Config *pC = p->pConfig; - char *zSql = 0; - - switch( eStmt ){ - case FTS5_STMT_SCAN_ASC: - case FTS5_STMT_SCAN_DESC: - case FTS5_STMT_LOOKUP: - zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContent, pC->zContentRowid); - break; - - case FTS5_STMT_INSERT_CONTENT: - case FTS5_STMT_REPLACE_CONTENT: { - int nCol = pC->nCol + 1; - char *zBind; - int i; - - zBind = sqlite3_malloc(1 + nCol*2); - if( zBind ){ - for(i=0; izDb, pC->zName, zBind); - sqlite3_free(zBind); - } - break; - } - - default: - zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName); - break; - } - - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0); - sqlite3_free(zSql); - if( rc!=SQLITE_OK && pzErrMsg ){ - *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); - } - } - } - - *ppStmt = p->aStmt[eStmt]; - return rc; -} - - -static int fts5ExecPrintf( - sqlite3 *db, - char **pzErr, - const char *zFormat, - ... -){ - int rc; - va_list ap; /* ... printf arguments */ - va_start(ap, zFormat); - char *zSql = sqlite3_vmprintf(zFormat, ap); - - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_exec(db, zSql, 0, 0, pzErr); - sqlite3_free(zSql); - } - - va_end(ap); - return rc; -} - -/* -** Drop the shadow table with the postfix zPost (e.g. "content"). Return -** SQLITE_OK if successful or an SQLite error code otherwise. -*/ -int sqlite3Fts5DropTable(Fts5Config *pConfig, const char *zPost){ - return fts5ExecPrintf(pConfig->db, 0, "DROP TABLE IF EXISTS %Q.'%q_%q'", - pConfig->zDb, pConfig->zName, zPost - ); -} - -/* -** Create the shadow table named zPost, with definition zDefn. Return -** SQLITE_OK if successful, or an SQLite error code otherwise. -*/ -int sqlite3Fts5CreateTable( - Fts5Config *pConfig, /* FTS5 configuration */ - const char *zPost, /* Shadow table to create (e.g. "content") */ - const char *zDefn, /* Columns etc. for shadow table */ - int bWithout, /* True for without rowid */ - char **pzErr /* OUT: Error message */ -){ - int rc; - char *zErr = 0; - - rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s", - pConfig->zDb, pConfig->zName, zPost, zDefn, bWithout?" WITHOUT ROWID":"" - ); - if( zErr ){ - *pzErr = sqlite3_mprintf( - "fts5: error creating shadow table %q_%s: %s", - pConfig->zName, zPost, zErr - ); - sqlite3_free(zErr); - } - - return rc; -} - -/* -** Open a new Fts5Index handle. If the bCreate argument is true, create -** and initialize the underlying tables -** -** If successful, set *pp to point to the new object and return SQLITE_OK. -** Otherwise, set *pp to NULL and return an SQLite error code. -*/ -int sqlite3Fts5StorageOpen( - Fts5Config *pConfig, - Fts5Index *pIndex, - int bCreate, - Fts5Storage **pp, - char **pzErr /* OUT: Error message */ -){ - int rc = SQLITE_OK; - Fts5Storage *p; /* New object */ - int nByte; /* Bytes of space to allocate */ - - nByte = sizeof(Fts5Storage) /* Fts5Storage object */ - + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */ - *pp = p = (Fts5Storage*)sqlite3_malloc(nByte); - if( !p ) return SQLITE_NOMEM; - - memset(p, 0, nByte); - p->aTotalSize = (i64*)&p[1]; - p->pConfig = pConfig; - p->pIndex = pIndex; - - if( bCreate ){ - if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ - char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10); - if( zDefn==0 ){ - rc = SQLITE_NOMEM; - }else{ - int i; - int iOff = sprintf(zDefn, "id INTEGER PRIMARY KEY"); - for(i=0; inCol; i++){ - iOff += sprintf(&zDefn[iOff], ", c%d", i); - } - rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr); - } - sqlite3_free(zDefn); - } - - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5CreateTable( - pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr - ); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5CreateTable( - pConfig, "config", "k PRIMARY KEY, v", 1, pzErr - ); - } - } - - if( rc ){ - sqlite3Fts5StorageClose(p, 0); - *pp = 0; - } - return rc; -} - -/* -** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen(). -*/ -int sqlite3Fts5StorageClose(Fts5Storage *p, int bDestroy){ - int rc = SQLITE_OK; - if( p ){ - int i; - - /* Finalize all SQL statements */ - for(i=0; iaStmt); i++){ - sqlite3_finalize(p->aStmt[i]); - } - - /* If required, remove the shadow tables from the database */ - if( bDestroy ){ - if( p->pConfig->eContent==FTS5_CONTENT_NORMAL ){ - rc = sqlite3Fts5DropTable(p->pConfig, "content"); - } - if( rc==SQLITE_OK ) rc = sqlite3Fts5DropTable(p->pConfig, "docsize"); - if( rc==SQLITE_OK ) rc = sqlite3Fts5DropTable(p->pConfig, "config"); - } - - sqlite3_free(p); - } - return rc; -} - -typedef struct Fts5InsertCtx Fts5InsertCtx; -struct Fts5InsertCtx { - Fts5Storage *pStorage; - int iCol; - int szCol; /* Size of column value in tokens */ -}; - -/* -** Tokenization callback used when inserting tokens into the FTS index. -*/ -static int fts5StorageInsertCallback( - void *pContext, /* Pointer to Fts5InsertCtx object */ - const char *pToken, /* Buffer containing token */ - int nToken, /* Size of token in bytes */ - int iStart, /* Start offset of token */ - int iEnd /* End offset of token */ -){ - Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext; - Fts5Index *pIdx = pCtx->pStorage->pIndex; - int iPos = pCtx->szCol++; - return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, iPos, pToken, nToken); -} - -/* -** If a row with rowid iDel is present in the %_content table, add the -** delete-markers to the FTS index necessary to delete it. Do not actually -** remove the %_content row at this time though. -*/ -static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){ - Fts5Config *pConfig = p->pConfig; - sqlite3_stmt *pSeek; /* SELECT to read row iDel from %_data */ - int rc; /* Return code */ - - rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0); - if( rc==SQLITE_OK ){ - int rc2; - sqlite3_bind_int64(pSeek, 1, iDel); - if( sqlite3_step(pSeek)==SQLITE_ROW ){ - int iCol; - Fts5InsertCtx ctx; - ctx.pStorage = p; - ctx.iCol = -1; - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel); - for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){ - ctx.szCol = 0; - rc = sqlite3Fts5Tokenize(pConfig, - (const char*)sqlite3_column_text(pSeek, iCol), - sqlite3_column_bytes(pSeek, iCol), - (void*)&ctx, - fts5StorageInsertCallback - ); - p->aTotalSize[iCol-1] -= (i64)ctx.szCol; - } - p->nTotalRow--; - } - rc2 = sqlite3_reset(pSeek); - if( rc==SQLITE_OK ) rc = rc2; - } - - return rc; -} - - -/* -** Insert a record into the %_docsize table. Specifically, do: -** -** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf); -*/ -static int fts5StorageInsertDocsize( - Fts5Storage *p, /* Storage module to write to */ - i64 iRowid, /* id value */ - Fts5Buffer *pBuf /* sz value */ -){ - sqlite3_stmt *pReplace = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pReplace, 1, iRowid); - sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); - } - return rc; -} - -/* -** Load the contents of the "averages" record from disk into the -** p->nTotalRow and p->aTotalSize[] variables. If successful, and if -** argument bCache is true, set the p->bTotalsValid flag to indicate -** that the contents of aTotalSize[] and nTotalRow are valid until -** further notice. -** -** Return SQLITE_OK if successful, or an SQLite error code if an error -** occurs. -*/ -static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){ - int rc = SQLITE_OK; - if( p->bTotalsValid==0 ){ - int nCol = p->pConfig->nCol; - Fts5Buffer buf; - memset(&buf, 0, sizeof(buf)); - - memset(p->aTotalSize, 0, sizeof(i64) * nCol); - p->nTotalRow = 0; - rc = sqlite3Fts5IndexGetAverages(p->pIndex, &buf); - if( rc==SQLITE_OK && buf.n ){ - int i = 0; - int iCol; - i += getVarint(&buf.p[i], (u64*)&p->nTotalRow); - for(iCol=0; iaTotalSize[iCol]); - } - } - sqlite3_free(buf.p); - p->bTotalsValid = bCache; - } - return rc; -} - -/* -** Store the current contents of the p->nTotalRow and p->aTotalSize[] -** variables in the "averages" record on disk. -** -** Return SQLITE_OK if successful, or an SQLite error code if an error -** occurs. -*/ -static int fts5StorageSaveTotals(Fts5Storage *p){ - int nCol = p->pConfig->nCol; - int i; - Fts5Buffer buf; - int rc = SQLITE_OK; - memset(&buf, 0, sizeof(buf)); - - sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow); - for(i=0; iaTotalSize[i]); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n); - } - sqlite3_free(buf.p); - - return rc; -} - -/* -** Remove a row from the FTS table. -*/ -int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel){ - int rc; - sqlite3_stmt *pDel; - - rc = fts5StorageLoadTotals(p, 1); - - /* Delete the index records */ - if( rc==SQLITE_OK ){ - rc = fts5StorageDeleteFromIndex(p, iDel); - } - - /* Delete the %_docsize record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); - } - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDel, 1, iDel); - sqlite3_step(pDel); - rc = sqlite3_reset(pDel); - } - - /* Delete the %_content record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0); - } - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDel, 1, iDel); - sqlite3_step(pDel); - rc = sqlite3_reset(pDel); - } - - /* Write the averages record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageSaveTotals(p); - } - - return rc; -} - -int sqlite3Fts5StorageSpecialDelete( - Fts5Storage *p, - i64 iDel, - sqlite3_value **apVal -){ - Fts5Config *pConfig = p->pConfig; - int rc; - sqlite3_stmt *pDel; - - assert( pConfig->eContent!=FTS5_CONTENT_NORMAL ); - rc = fts5StorageLoadTotals(p, 1); - - /* Delete the index records */ - if( rc==SQLITE_OK ){ - int iCol; - Fts5InsertCtx ctx; - ctx.pStorage = p; - ctx.iCol = -1; - - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel); - for(iCol=0; rc==SQLITE_OK && iColnCol; iCol++){ - ctx.szCol = 0; - rc = sqlite3Fts5Tokenize(pConfig, - (const char*)sqlite3_value_text(apVal[iCol]), - sqlite3_value_bytes(apVal[iCol]), - (void*)&ctx, - fts5StorageInsertCallback - ); - p->aTotalSize[iCol] -= (i64)ctx.szCol; - } - p->nTotalRow--; - } - - /* Delete the %_docsize record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0); - } - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pDel, 1, iDel); - sqlite3_step(pDel); - rc = sqlite3_reset(pDel); - } - - /* Write the averages record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageSaveTotals(p); - } - - return rc; -} - -/* -** Delete all entries in the FTS5 index. -*/ -int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){ - Fts5Config *pConfig = p->pConfig; - int rc; - - /* Delete the contents of the %_data and %_docsize tables. */ - rc = fts5ExecPrintf(pConfig->db, 0, - "DELETE FROM %Q.'%q_data';" - "DELETE FROM %Q.'%q_docsize';", - pConfig->zDb, pConfig->zName, - pConfig->zDb, pConfig->zName - ); - - /* Reinitialize the %_data table. This call creates the initial structure - ** and averages records. */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexReinit(p->pIndex); - } - return rc; -} - -int sqlite3Fts5StorageRebuild(Fts5Storage *p){ - Fts5Buffer buf = {0,0,0}; - Fts5Config *pConfig = p->pConfig; - sqlite3_stmt *pScan = 0; - Fts5InsertCtx ctx; - int rc; - - memset(&ctx, 0, sizeof(Fts5InsertCtx)); - ctx.pStorage = p; - rc = sqlite3Fts5StorageDeleteAll(p); - if( rc==SQLITE_OK ){ - rc = fts5StorageLoadTotals(p, 1); - } - - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN_ASC, &pScan, 0); - } - - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){ - i64 iRowid = sqlite3_column_int64(pScan, 0); - - sqlite3Fts5BufferZero(&buf); - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iRowid); - for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ - ctx.szCol = 0; - rc = sqlite3Fts5Tokenize(pConfig, - (const char*)sqlite3_column_text(pScan, ctx.iCol+1), - sqlite3_column_bytes(pScan, ctx.iCol+1), - (void*)&ctx, - fts5StorageInsertCallback - ); - sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); - p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; - } - p->nTotalRow++; - - if( rc==SQLITE_OK ){ - rc = fts5StorageInsertDocsize(p, iRowid, &buf); - } - } - sqlite3_free(buf.p); - - /* Write the averages record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageSaveTotals(p); - } - return rc; -} - -int sqlite3Fts5StorageOptimize(Fts5Storage *p){ - return sqlite3Fts5IndexOptimize(p->pIndex); -} - -/* -** Allocate a new rowid. This is used for "external content" tables when -** a NULL value is inserted into the rowid column. The new rowid is allocated -** by inserting a dummy row into the %_docsize table. The dummy will be -** overwritten later. -*/ -static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){ - sqlite3_stmt *pReplace = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_null(pReplace, 1); - sqlite3_bind_null(pReplace, 2); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); - } - if( rc==SQLITE_OK ){ - *piRowid = sqlite3_last_insert_rowid(p->pConfig->db); - } - return rc; -} - -/* -** Insert a new row into the FTS table. -*/ -int sqlite3Fts5StorageInsert( - Fts5Storage *p, /* Storage module to write to */ - sqlite3_value **apVal, /* Array of values passed to xUpdate() */ - int eConflict, /* on conflict clause */ - i64 *piRowid /* OUT: rowid of new record */ -){ - Fts5Config *pConfig = p->pConfig; - int rc = SQLITE_OK; /* Return code */ - sqlite3_stmt *pInsert; /* Statement used to write %_content table */ - int eStmt = 0; /* Type of statement used on %_content */ - int i; /* Counter variable */ - Fts5InsertCtx ctx; /* Tokenization callback context object */ - Fts5Buffer buf; /* Buffer used to build up %_docsize blob */ - - memset(&buf, 0, sizeof(Fts5Buffer)); - rc = fts5StorageLoadTotals(p, 1); - - /* Insert the new row into the %_content table. */ - if( rc==SQLITE_OK ){ - if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){ - if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ - *piRowid = sqlite3_value_int64(apVal[1]); - }else{ - rc = fts5StorageNewRowid(p, piRowid); - } - }else{ - if( eConflict==SQLITE_REPLACE ){ - eStmt = FTS5_STMT_REPLACE_CONTENT; - if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){ - rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1])); - } - }else{ - eStmt = FTS5_STMT_INSERT_CONTENT; - } - if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, eStmt, &pInsert, 0); - } - for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){ - rc = sqlite3_bind_value(pInsert, i, apVal[i]); - } - if( rc==SQLITE_OK ){ - sqlite3_step(pInsert); - rc = sqlite3_reset(pInsert); - } - *piRowid = sqlite3_last_insert_rowid(pConfig->db); - } - } - - /* Add new entries to the FTS index */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexBeginWrite(p->pIndex, *piRowid); - ctx.pStorage = p; - } - for(ctx.iCol=0; rc==SQLITE_OK && ctx.iColnCol; ctx.iCol++){ - ctx.szCol = 0; - rc = sqlite3Fts5Tokenize(pConfig, - (const char*)sqlite3_value_text(apVal[ctx.iCol+2]), - sqlite3_value_bytes(apVal[ctx.iCol+2]), - (void*)&ctx, - fts5StorageInsertCallback - ); - sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol); - p->aTotalSize[ctx.iCol] += (i64)ctx.szCol; - } - p->nTotalRow++; - - /* Write the %_docsize record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageInsertDocsize(p, *piRowid, &buf); - } - sqlite3_free(buf.p); - - /* Write the averages record */ - if( rc==SQLITE_OK ){ - rc = fts5StorageSaveTotals(p); - } - - return rc; -} - -static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){ - Fts5Config *pConfig = p->pConfig; - char *zSql; - int rc; - - zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'", - pConfig->zDb, pConfig->zName, zSuffix - ); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_stmt *pCnt = 0; - rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pCnt, 0); - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pCnt) ){ - *pnRow = sqlite3_column_int64(pCnt, 0); - } - rc = sqlite3_finalize(pCnt); - } - - sqlite3_free(zSql); - return rc; -} - -/* -** Context object used by sqlite3Fts5StorageIntegrity(). -*/ -typedef struct Fts5IntegrityCtx Fts5IntegrityCtx; -struct Fts5IntegrityCtx { - i64 iRowid; - int iCol; - int szCol; - u64 cksum; - Fts5Config *pConfig; -}; - -/* -** Tokenization callback used by integrity check. -*/ -static int fts5StorageIntegrityCallback( - void *pContext, /* Pointer to Fts5InsertCtx object */ - const char *pToken, /* Buffer containing token */ - int nToken, /* Size of token in bytes */ - int iStart, /* Start offset of token */ - int iEnd /* End offset of token */ -){ - Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; - int iPos = pCtx->szCol++; - pCtx->cksum ^= sqlite3Fts5IndexCksum( - pCtx->pConfig, pCtx->iRowid, pCtx->iCol, iPos, pToken, nToken - ); - return SQLITE_OK; -} - -/* -** Check that the contents of the FTS index match that of the %_content -** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return -** some other SQLite error code if an error occurs while attempting to -** determine this. -*/ -int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ - Fts5Config *pConfig = p->pConfig; - int rc; /* Return code */ - int *aColSize; /* Array of size pConfig->nCol */ - i64 *aTotalSize; /* Array of size pConfig->nCol */ - Fts5IntegrityCtx ctx; - sqlite3_stmt *pScan; - - memset(&ctx, 0, sizeof(Fts5IntegrityCtx)); - ctx.pConfig = p->pConfig; - aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64))); - if( !aTotalSize ) return SQLITE_NOMEM; - aColSize = (int*)&aTotalSize[pConfig->nCol]; - memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol); - - /* Generate the expected index checksum based on the contents of the - ** %_content table. This block stores the checksum in ctx.cksum. */ - rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN_ASC, &pScan, 0); - if( rc==SQLITE_OK ){ - int rc2; - while( SQLITE_ROW==sqlite3_step(pScan) ){ - int i; - ctx.iRowid = sqlite3_column_int64(pScan, 0); - ctx.szCol = 0; - rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); - for(i=0; rc==SQLITE_OK && inCol; i++){ - ctx.iCol = i; - ctx.szCol = 0; - rc = sqlite3Fts5Tokenize( - pConfig, - (const char*)sqlite3_column_text(pScan, i+1), - sqlite3_column_bytes(pScan, i+1), - (void*)&ctx, - fts5StorageIntegrityCallback - ); - if( ctx.szCol!=aColSize[i] ) rc = SQLITE_CORRUPT_VTAB; - aTotalSize[i] += ctx.szCol; - } - if( rc!=SQLITE_OK ) break; - } - rc2 = sqlite3_reset(pScan); - if( rc==SQLITE_OK ) rc = rc2; - } - - /* Test that the "totals" (sometimes called "averages") record looks Ok */ - if( rc==SQLITE_OK ){ - int i; - rc = fts5StorageLoadTotals(p, 0); - for(i=0; rc==SQLITE_OK && inCol; i++){ - if( p->aTotalSize[i]!=aTotalSize[i] ) rc = SQLITE_CORRUPT_VTAB; - } - } - - /* Check that the %_docsize and %_content tables contain the expected - ** number of rows. */ - if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ - i64 nRow; - rc = fts5StorageCount(p, "content", &nRow); - if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = SQLITE_CORRUPT_VTAB; - } - if( rc==SQLITE_OK ){ - i64 nRow; - rc = fts5StorageCount(p, "docsize", &nRow); - if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = SQLITE_CORRUPT_VTAB; - } - - /* Pass the expected checksum down to the FTS index module. It will - ** verify, amongst other things, that it matches the checksum generated by - ** inspecting the index itself. */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum); - } - - sqlite3_free(aTotalSize); - return rc; -} - -/* -** Obtain an SQLite statement handle that may be used to read data from the -** %_content table. -*/ -int sqlite3Fts5StorageStmt( - Fts5Storage *p, - int eStmt, - sqlite3_stmt **pp, - char **pzErrMsg -){ - int rc; - assert( eStmt==FTS5_STMT_SCAN_ASC - || eStmt==FTS5_STMT_SCAN_DESC - || eStmt==FTS5_STMT_LOOKUP - ); - rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg); - if( rc==SQLITE_OK ){ - assert( p->aStmt[eStmt]==*pp ); - p->aStmt[eStmt] = 0; - } - return rc; -} - -/* -** Release an SQLite statement handle obtained via an earlier call to -** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function -** must match that passed to the sqlite3Fts5StorageStmt() call. -*/ -void sqlite3Fts5StorageStmtRelease( - Fts5Storage *p, - int eStmt, - sqlite3_stmt *pStmt -){ - assert( eStmt==FTS5_STMT_SCAN_ASC - || eStmt==FTS5_STMT_SCAN_DESC - || eStmt==FTS5_STMT_LOOKUP - ); - if( p->aStmt[eStmt]==0 ){ - sqlite3_reset(pStmt); - p->aStmt[eStmt] = pStmt; - }else{ - sqlite3_finalize(pStmt); - } -} - -static int fts5StorageDecodeSizeArray( - int *aCol, int nCol, /* Array to populate */ - const u8 *aBlob, int nBlob /* Record to read varints from */ -){ - int i; - int iOff = 0; - for(i=0; i=nBlob ) return 1; - iOff += getVarint32(&aBlob[iOff], aCol[i]); - } - return (iOff!=nBlob); -} - -/* -** Argument aCol points to an array of integers containing one entry for -** each table column. This function reads the %_docsize record for the -** specified rowid and populates aCol[] with the results. -** -** An SQLite error code is returned if an error occurs, or SQLITE_OK -** otherwise. -*/ -int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){ - int nCol = p->pConfig->nCol; - sqlite3_stmt *pLookup = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0); - if( rc==SQLITE_OK ){ - int bCorrupt = 1; - sqlite3_bind_int64(pLookup, 1, iRowid); - if( SQLITE_ROW==sqlite3_step(pLookup) ){ - const u8 *aBlob = sqlite3_column_blob(pLookup, 0); - int nBlob = sqlite3_column_bytes(pLookup, 0); - if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){ - bCorrupt = 0; - } - } - rc = sqlite3_reset(pLookup); - if( bCorrupt && rc==SQLITE_OK ){ - rc = SQLITE_CORRUPT_VTAB; - } - } - return rc; -} - -int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){ - int rc = fts5StorageLoadTotals(p, 0); - if( rc==SQLITE_OK ){ - *pnToken = 0; - if( iCol<0 ){ - int i; - for(i=0; ipConfig->nCol; i++){ - *pnToken += p->aTotalSize[i]; - } - }else if( iColpConfig->nCol ){ - *pnToken = p->aTotalSize[iCol]; - }else{ - rc = SQLITE_RANGE; - } - } - return rc; -} - -int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){ - int rc = fts5StorageLoadTotals(p, 0); - if( rc==SQLITE_OK ){ - *pnRow = p->nTotalRow; - } - return rc; -} - -/* -** Flush any data currently held in-memory to disk. -*/ -int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit){ - if( bCommit && p->bTotalsValid ){ - int rc = fts5StorageSaveTotals(p); - p->bTotalsValid = 0; - if( rc!=SQLITE_OK ) return rc; - } - return sqlite3Fts5IndexSync(p->pIndex, bCommit); -} - -int sqlite3Fts5StorageRollback(Fts5Storage *p){ - p->bTotalsValid = 0; - return sqlite3Fts5IndexRollback(p->pIndex); -} - -int sqlite3Fts5StorageConfigValue( - Fts5Storage *p, - const char *z, - sqlite3_value *pVal -){ - sqlite3_stmt *pReplace = 0; - int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0); - if( rc==SQLITE_OK ){ - sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_TRANSIENT); - sqlite3_bind_value(pReplace, 2, pVal); - sqlite3_step(pReplace); - rc = sqlite3_reset(pReplace); - } - if( rc==SQLITE_OK ){ - int iNew = p->pConfig->iCookie + 1; - rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew); - if( rc==SQLITE_OK ){ - p->pConfig->iCookie = iNew; - } - } - return rc; -} - - DELETED ext/fts5/fts5_tcl.c Index: ext/fts5/fts5_tcl.c ================================================================== --- ext/fts5/fts5_tcl.c +++ /dev/null @@ -1,860 +0,0 @@ -/* -** 2014 Dec 01 -** -** 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. -** -****************************************************************************** -** -*/ - -#ifdef SQLITE_TEST - -#include "fts5.h" -#include -#include -#include - -/************************************************************************* -** This is a copy of the first part of the SqliteDb structure in -** tclsqlite.c. We need it here so that the get_sqlite_pointer routine -** can extract the sqlite3* pointer from an existing Tcl SQLite -** connection. -*/ -struct SqliteDb { - sqlite3 *db; -}; - -/* -** Decode a pointer to an sqlite3 object. -*/ -static int f5tDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **ppDb){ - struct SqliteDb *p; - Tcl_CmdInfo cmdInfo; - char *z = Tcl_GetString(pObj); - if( Tcl_GetCommandInfo(interp, z, &cmdInfo) ){ - p = (struct SqliteDb*)cmdInfo.objClientData; - *ppDb = p->db; - return TCL_OK; - } - return TCL_ERROR; -} - -/* End of code that accesses the SqliteDb struct. -**************************************************************************/ - -static int f5tDbAndApi( - Tcl_Interp *interp, - Tcl_Obj *pObj, - sqlite3 **ppDb, - fts5_api **ppApi -){ - sqlite3 *db = 0; - int rc = f5tDbPointer(interp, pObj, &db); - if( rc!=TCL_OK ){ - return TCL_ERROR; - }else{ - sqlite3_stmt *pStmt = 0; - fts5_api *pApi = 0; - - rc = sqlite3_prepare_v2(db, "SELECT fts5()", -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); - return TCL_ERROR; - } - - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - const void *pPtr = sqlite3_column_blob(pStmt, 0); - memcpy((void*)&pApi, pPtr, sizeof(pApi)); - } - - if( sqlite3_finalize(pStmt)!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); - return TCL_ERROR; - } - - *ppDb = db; - *ppApi = pApi; - } - - return TCL_OK; -} - -typedef struct F5tFunction F5tFunction; -struct F5tFunction { - Tcl_Interp *interp; - Tcl_Obj *pScript; -}; - -typedef struct F5tApi F5tApi; -struct F5tApi { - const Fts5ExtensionApi *pApi; - Fts5Context *pFts; -}; - -/* -** An object of this type is used with the xSetAuxdata() and xGetAuxdata() -** API test wrappers. The tcl interface allows a single tcl value to be -** saved using xSetAuxdata(). Instead of simply storing a pointer to the -** tcl object, the code in this file wraps it in an sqlite3_malloc'd -** instance of the following struct so that if the destructor is not -** correctly invoked it will be reported as an SQLite memory leak. -*/ -typedef struct F5tAuxData F5tAuxData; -struct F5tAuxData { - Tcl_Obj *pObj; -}; - -static int xTokenizeCb( - void *pCtx, - const char *zToken, int nToken, - int iStart, int iEnd -){ - F5tFunction *p = (F5tFunction*)pCtx; - Tcl_Obj *pEval = Tcl_DuplicateObj(p->pScript); - int rc; - - Tcl_IncrRefCount(pEval); - Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zToken, nToken)); - Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(iStart)); - Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(iEnd)); - - rc = Tcl_EvalObjEx(p->interp, pEval, 0); - Tcl_DecrRefCount(pEval); - - return rc; -} - -static int xF5tApi(void*, Tcl_Interp*, int, Tcl_Obj *CONST []); - -static int xQueryPhraseCb( - const Fts5ExtensionApi *pApi, - Fts5Context *pFts, - void *pCtx -){ - F5tFunction *p = (F5tFunction*)pCtx; - static sqlite3_int64 iCmd = 0; - Tcl_Obj *pEval; - int rc; - - char zCmd[64]; - F5tApi sApi; - - sApi.pApi = pApi; - sApi.pFts = pFts; - sprintf(zCmd, "f5t_2_%lld", iCmd++); - Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0); - - pEval = Tcl_DuplicateObj(p->pScript); - Tcl_IncrRefCount(pEval); - Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1)); - rc = Tcl_EvalObjEx(p->interp, pEval, 0); - Tcl_DecrRefCount(pEval); - Tcl_DeleteCommand(p->interp, zCmd); - - return rc; -} - -static void xSetAuxdataDestructor(void *p){ - F5tAuxData *pData = (F5tAuxData*)p; - Tcl_DecrRefCount(pData->pObj); - sqlite3_free(pData); -} - -/* -** api sub-command... -** -** Description... -*/ -static int xF5tApi( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - struct Sub { - const char *zName; - int nArg; - const char *zMsg; - } aSub[] = { - { "xColumnCount", 0, "" }, - { "xRowCount", 0, "" }, - { "xColumnTotalSize", 1, "COL" }, - { "xTokenize", 2, "TEXT SCRIPT" }, - { "xPhraseCount", 0, "" }, - { "xPhraseSize", 1, "PHRASE" }, - { "xInstCount", 0, "" }, - { "xInst", 1, "IDX" }, - { "xRowid", 0, "" }, - { "xColumnText", 1, "COL" }, - { "xColumnSize", 1, "COL" }, - { "xQueryPhrase", 2, "PHRASE SCRIPT" }, - { "xSetAuxdata", 1, "VALUE" }, - { "xGetAuxdata", 1, "CLEAR" }, - { 0, 0, 0} - }; - - int rc; - int iSub = 0; - F5tApi *p = (F5tApi*)clientData; - - if( objc<2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND"); - return TCL_ERROR; - } - - rc = Tcl_GetIndexFromObjStruct( - interp, objv[1], aSub, sizeof(aSub[0]), "SUB-COMMAND", 0, &iSub - ); - if( rc!=TCL_OK ) return rc; - if( aSub[iSub].nArg!=objc-2 ){ - Tcl_WrongNumArgs(interp, 1, objv, aSub[iSub].zMsg); - return TCL_ERROR; - } - -#define CASE(i,str) case i: assert( strcmp(aSub[i].zName, str)==0 ); - switch( iSub ){ - CASE(0, "xColumnCount") { - int nCol; - nCol = p->pApi->xColumnCount(p->pFts); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewIntObj(nCol)); - } - break; - } - CASE(1, "xRowCount") { - sqlite3_int64 nRow; - rc = p->pApi->xRowCount(p->pFts, &nRow); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nRow)); - } - break; - } - CASE(2, "xColumnTotalSize") { - int iCol; - sqlite3_int64 nSize; - if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ) return TCL_ERROR; - rc = p->pApi->xColumnTotalSize(p->pFts, iCol, &nSize); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nSize)); - } - break; - } - CASE(3, "xTokenize") { - int nText; - char *zText = Tcl_GetStringFromObj(objv[2], &nText); - F5tFunction ctx; - ctx.interp = interp; - ctx.pScript = objv[3]; - rc = p->pApi->xTokenize(p->pFts, zText, nText, &ctx, xTokenizeCb); - if( rc==SQLITE_OK ){ - Tcl_ResetResult(interp); - } - return rc; - } - CASE(4, "xPhraseCount") { - int nPhrase; - nPhrase = p->pApi->xPhraseCount(p->pFts); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewIntObj(nPhrase)); - } - break; - } - CASE(5, "xPhraseSize") { - int iPhrase; - int sz; - if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ){ - return TCL_ERROR; - } - sz = p->pApi->xPhraseSize(p->pFts, iPhrase); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewIntObj(sz)); - } - break; - } - CASE(6, "xInstCount") { - int nInst; - rc = p->pApi->xInstCount(p->pFts, &nInst); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewIntObj(nInst)); - } - break; - } - CASE(7, "xInst") { - int iIdx, ip, ic, io; - if( Tcl_GetIntFromObj(interp, objv[2], &iIdx) ){ - return TCL_ERROR; - } - rc = p->pApi->xInst(p->pFts, iIdx, &ip, &ic, &io); - if( rc==SQLITE_OK ){ - Tcl_Obj *pList = Tcl_NewObj(); - Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ip)); - Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(ic)); - Tcl_ListObjAppendElement(interp, pList, Tcl_NewIntObj(io)); - Tcl_SetObjResult(interp, pList); - } - break; - } - CASE(8, "xRowid") { - sqlite3_int64 iRowid = p->pApi->xRowid(p->pFts); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iRowid)); - break; - } - CASE(9, "xColumnText") { - const char *z = 0; - int n = 0; - int iCol; - if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){ - return TCL_ERROR; - } - rc = p->pApi->xColumnText(p->pFts, iCol, &z, &n); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(z, n)); - } - break; - } - CASE(10, "xColumnSize") { - int n = 0; - int iCol; - if( Tcl_GetIntFromObj(interp, objv[2], &iCol) ){ - return TCL_ERROR; - } - rc = p->pApi->xColumnSize(p->pFts, iCol, &n); - if( rc==SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewIntObj(n)); - } - break; - } - CASE(11, "xQueryPhrase") { - int iPhrase; - F5tFunction ctx; - if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ){ - return TCL_ERROR; - } - ctx.interp = interp; - ctx.pScript = objv[3]; - rc = p->pApi->xQueryPhrase(p->pFts, iPhrase, &ctx, xQueryPhraseCb); - if( rc==SQLITE_OK ){ - Tcl_ResetResult(interp); - } - break; - } - CASE(12, "xSetAuxdata") { - F5tAuxData *pData = (F5tAuxData*)sqlite3_malloc(sizeof(F5tAuxData)); - if( pData==0 ){ - Tcl_AppendResult(interp, "out of memory", 0); - return TCL_ERROR; - } - pData->pObj = objv[2]; - Tcl_IncrRefCount(pData->pObj); - rc = p->pApi->xSetAuxdata(p->pFts, pData, xSetAuxdataDestructor); - break; - } - CASE(13, "xGetAuxdata") { - F5tAuxData *pData; - int bClear; - if( Tcl_GetBooleanFromObj(interp, objv[2], &bClear) ){ - return TCL_ERROR; - } - pData = (F5tAuxData*)p->pApi->xGetAuxdata(p->pFts, bClear); - if( pData==0 ){ - Tcl_ResetResult(interp); - }else{ - Tcl_SetObjResult(interp, pData->pObj); - if( bClear ){ - xSetAuxdataDestructor((void*)pData); - } - } - break; - } - - default: - assert( 0 ); - break; - } -#undef CASE - - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error in api call", 0); - return TCL_ERROR; - } - - return TCL_OK; -} - -static void xF5tFunction( - const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ - Fts5Context *pFts, /* First arg to pass to pApi functions */ - sqlite3_context *pCtx, /* Context for returning result/error */ - int nVal, /* Number of values in apVal[] array */ - sqlite3_value **apVal /* Array of trailing arguments */ -){ - F5tFunction *p = (F5tFunction*)pApi->xUserData(pFts); - Tcl_Obj *pEval; /* Script to evaluate */ - int i; - int rc; - - static sqlite3_int64 iCmd = 0; - char zCmd[64]; - F5tApi sApi; - sApi.pApi = pApi; - sApi.pFts = pFts; - - sprintf(zCmd, "f5t_%lld", iCmd++); - Tcl_CreateObjCommand(p->interp, zCmd, xF5tApi, &sApi, 0); - pEval = Tcl_DuplicateObj(p->pScript); - Tcl_IncrRefCount(pEval); - Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zCmd, -1)); - - for(i=0; iinterp, pEval, pObj); - } - - rc = Tcl_EvalObjEx(p->interp, pEval, TCL_GLOBAL_ONLY); - Tcl_DecrRefCount(pEval); - Tcl_DeleteCommand(p->interp, zCmd); - - if( rc!=TCL_OK ){ - sqlite3_result_error(pCtx, Tcl_GetStringResult(p->interp), -1); - }else{ - Tcl_Obj *pVar = Tcl_GetObjResult(p->interp); - int n; - const char *zType = (pVar->typePtr ? pVar->typePtr->name : ""); - char c = zType[0]; - if( c=='b' && strcmp(zType,"bytearray")==0 && pVar->bytes==0 ){ - /* Only return a BLOB type if the Tcl variable is a bytearray and - ** has no string representation. */ - unsigned char *data = Tcl_GetByteArrayFromObj(pVar, &n); - sqlite3_result_blob(pCtx, data, n, SQLITE_TRANSIENT); - }else if( c=='b' && strcmp(zType,"boolean")==0 ){ - Tcl_GetIntFromObj(0, pVar, &n); - sqlite3_result_int(pCtx, n); - }else if( c=='d' && strcmp(zType,"double")==0 ){ - double r; - Tcl_GetDoubleFromObj(0, pVar, &r); - sqlite3_result_double(pCtx, r); - }else if( (c=='w' && strcmp(zType,"wideInt")==0) || - (c=='i' && strcmp(zType,"int")==0) ){ - Tcl_WideInt v; - Tcl_GetWideIntFromObj(0, pVar, &v); - sqlite3_result_int64(pCtx, v); - }else{ - unsigned char *data = (unsigned char *)Tcl_GetStringFromObj(pVar, &n); - sqlite3_result_text(pCtx, (char *)data, n, SQLITE_TRANSIENT); - } - } -} - -static void xF5tDestroy(void *pCtx){ - F5tFunction *p = (F5tFunction*)pCtx; - Tcl_DecrRefCount(p->pScript); - ckfree(p); -} - -/* -** sqlite3_fts5_create_function DB NAME SCRIPT -** -** Description... -*/ -static int f5tCreateFunction( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - char *zName; - Tcl_Obj *pScript; - sqlite3 *db = 0; - fts5_api *pApi = 0; - F5tFunction *pCtx = 0; - int rc; - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT"); - return TCL_ERROR; - } - if( f5tDbAndApi(interp, objv[1], &db, &pApi) ) return TCL_ERROR; - - zName = Tcl_GetString(objv[2]); - pScript = objv[3]; - pCtx = (F5tFunction*)ckalloc(sizeof(F5tFunction)); - pCtx->interp = interp; - pCtx->pScript = pScript; - Tcl_IncrRefCount(pScript); - - rc = pApi->xCreateFunction( - pApi, zName, (void*)pCtx, xF5tFunction, xF5tDestroy - ); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error: ", sqlite3_errmsg(db), 0); - return TCL_ERROR; - } - - return TCL_OK; -} - -typedef struct F5tTokenizeCtx F5tTokenizeCtx; -struct F5tTokenizeCtx { - Tcl_Obj *pRet; - int bSubst; - const char *zInput; -}; - -static int xTokenizeCb2( - void *pCtx, - const char *zToken, int nToken, - int iStart, int iEnd -){ - F5tTokenizeCtx *p = (F5tTokenizeCtx*)pCtx; - if( p->bSubst ){ - Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken)); - Tcl_ListObjAppendElement( - 0, p->pRet, Tcl_NewStringObj(&p->zInput[iStart], iEnd-iStart) - ); - }else{ - Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewStringObj(zToken, nToken)); - Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewIntObj(iStart)); - Tcl_ListObjAppendElement(0, p->pRet, Tcl_NewIntObj(iEnd)); - } - return SQLITE_OK; -} - - -/* -** sqlite3_fts5_tokenize DB TOKENIZER TEXT -** -** Description... -*/ -static int f5tTokenize( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - char *zText; - int nText; - sqlite3 *db = 0; - fts5_api *pApi = 0; - Fts5Tokenizer *pTok = 0; - fts5_tokenizer tokenizer; - Tcl_Obj *pRet = 0; - void *pUserdata; - int rc; - - int nArg; - const char **azArg; - F5tTokenizeCtx ctx; - - if( objc!=4 && objc!=5 ){ - Tcl_WrongNumArgs(interp, 1, objv, "?-subst? DB NAME TEXT"); - return TCL_ERROR; - } - if( objc==5 ){ - char *zOpt = Tcl_GetString(objv[1]); - if( strcmp("-subst", zOpt) ){ - Tcl_AppendResult(interp, "unrecognized option: ", zOpt, 0); - return TCL_ERROR; - } - } - if( f5tDbAndApi(interp, objv[objc-3], &db, &pApi) ) return TCL_ERROR; - if( Tcl_SplitList(interp, Tcl_GetString(objv[objc-2]), &nArg, &azArg) ){ - return TCL_ERROR; - } - if( nArg==0 ){ - Tcl_AppendResult(interp, "no such tokenizer: ", 0); - Tcl_Free((void*)azArg); - return TCL_ERROR; - } - zText = Tcl_GetStringFromObj(objv[objc-1], &nText); - - rc = pApi->xFindTokenizer(pApi, azArg[0], &pUserdata, &tokenizer); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "no such tokenizer: ", azArg[0], 0); - return TCL_ERROR; - } - - rc = tokenizer.xCreate(pUserdata, &azArg[1], nArg-1, &pTok); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error in tokenizer.xCreate()", 0); - return TCL_ERROR; - } - - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); - ctx.bSubst = (objc==5); - ctx.pRet = pRet; - ctx.zInput = zText; - rc = tokenizer.xTokenize(pTok, (void*)&ctx, zText, nText, xTokenizeCb2); - tokenizer.xDelete(pTok); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error in tokenizer.xTokenize()", 0); - Tcl_DecrRefCount(pRet); - return TCL_ERROR; - } - - - Tcl_Free((void*)azArg); - Tcl_SetObjResult(interp, pRet); - Tcl_DecrRefCount(pRet); - return TCL_OK; -} - -/************************************************************************* -** Start of tokenizer wrapper. -*/ - -typedef struct F5tTokenizerContext F5tTokenizerContext; -typedef struct F5tTokenizerCb F5tTokenizerCb; -typedef struct F5tTokenizerModule F5tTokenizerModule; -typedef struct F5tTokenizerModule F5tTokenizerInstance; - -struct F5tTokenizerContext { - void *pCtx; - int (*xToken)(void*, const char*, int, int, int); -}; - -struct F5tTokenizerModule { - Tcl_Interp *interp; - Tcl_Obj *pScript; - F5tTokenizerContext *pContext; -}; - -static int f5tTokenizerCreate( - void *pCtx, - const char **azArg, - int nArg, - Fts5Tokenizer **ppOut -){ - F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx; - Tcl_Obj *pEval; - int rc = TCL_OK; - int i; - - pEval = Tcl_DuplicateObj(pMod->pScript); - Tcl_IncrRefCount(pEval); - for(i=0; rc==TCL_OK && iinterp, pEval, pObj); - } - - if( rc==TCL_OK ){ - rc = Tcl_EvalObjEx(pMod->interp, pEval, TCL_GLOBAL_ONLY); - } - Tcl_DecrRefCount(pEval); - - if( rc==TCL_OK ){ - F5tTokenizerInstance *pInst = ckalloc(sizeof(F5tTokenizerInstance)); - memset(pInst, 0, sizeof(F5tTokenizerInstance)); - pInst->interp = pMod->interp; - pInst->pScript = Tcl_GetObjResult(pMod->interp); - pInst->pContext = pMod->pContext; - Tcl_IncrRefCount(pInst->pScript); - *ppOut = (Fts5Tokenizer*)pInst; - } - - return rc; -} - - -static void f5tTokenizerDelete(Fts5Tokenizer *p){ - F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p; - Tcl_DecrRefCount(pInst->pScript); - ckfree(pInst); -} - -static int f5tTokenizerTokenize( - Fts5Tokenizer *p, - void *pCtx, - const char *pText, int nText, - int (*xToken)(void*, const char*, int, int, int) -){ - F5tTokenizerInstance *pInst = (F5tTokenizerInstance*)p; - void *pOldCtx; - int (*xOldToken)(void*, const char*, int, int, int); - Tcl_Obj *pEval; - int rc; - - pOldCtx = pInst->pContext->pCtx; - xOldToken = pInst->pContext->xToken; - - pEval = Tcl_DuplicateObj(pInst->pScript); - Tcl_IncrRefCount(pEval); - rc = Tcl_ListObjAppendElement( - pInst->interp, pEval, Tcl_NewStringObj(pText, nText) - ); - if( rc==TCL_OK ){ - rc = Tcl_EvalObjEx(pInst->interp, pEval, TCL_GLOBAL_ONLY); - } - Tcl_DecrRefCount(pEval); - - pInst->pContext->pCtx = pOldCtx; - pInst->pContext->xToken = xOldToken; - return rc; -} - -extern const char *sqlite3ErrName(int); - -/* -** sqlite3_fts5_token TEXT START END POS -*/ -static int f5tTokenizerReturn( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - F5tTokenizerContext *p = (F5tTokenizerContext*)clientData; - int iStart; - int iEnd; - int nToken; - char *zToken; - int rc; - - assert( p ); - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "TEXT START END"); - return TCL_ERROR; - } - if( p->xToken==0 ){ - Tcl_AppendResult(interp, - "sqlite3_fts5_token may only be used by tokenizer callback", 0 - ); - return TCL_ERROR; - } - - zToken = Tcl_GetStringFromObj(objv[1], &nToken); - if( Tcl_GetIntFromObj(interp, objv[2], &iStart) - || Tcl_GetIntFromObj(interp, objv[3], &iEnd) - ){ - return TCL_ERROR; - } - - rc = p->xToken(p->pCtx, zToken, nToken, iStart, iEnd); - Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); - return TCL_OK; -} - -static void f5tDelTokenizer(void *pCtx){ - F5tTokenizerModule *pMod = (F5tTokenizerModule*)pCtx; - Tcl_DecrRefCount(pMod->pScript); - ckfree(pMod); -} - -/* -** sqlite3_fts5_create_tokenizer DB NAME SCRIPT -** -** Register a tokenizer named NAME implemented by script SCRIPT. When -** a tokenizer instance is created (fts5_tokenizer.xCreate), any tokenizer -** arguments are appended to SCRIPT and the result executed. -** -** The value returned by (SCRIPT + args) is itself a tcl script. This -** script - call it SCRIPT2 - is executed to tokenize text using the -** tokenizer instance "returned" by SCRIPT. Specifically, to tokenize -** text SCRIPT2 is invoked with a single argument appended to it - the -** text to tokenize. -** -** SCRIPT2 should invoke the [sqlite3_fts5_token] command once for each -** token within the tokenized text. -*/ -static int f5tCreateTokenizer( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - F5tTokenizerContext *pContext = (F5tTokenizerContext*)clientData; - sqlite3 *db; - fts5_api *pApi; - char *zName; - Tcl_Obj *pScript; - fts5_tokenizer t; - F5tTokenizerModule *pMod; - int rc; - - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB NAME SCRIPT"); - return TCL_ERROR; - } - if( f5tDbAndApi(interp, objv[1], &db, &pApi) ){ - return TCL_ERROR; - } - zName = Tcl_GetString(objv[2]); - pScript = objv[3]; - - t.xCreate = f5tTokenizerCreate; - t.xTokenize = f5tTokenizerTokenize; - t.xDelete = f5tTokenizerDelete; - - pMod = (F5tTokenizerModule*)ckalloc(sizeof(F5tTokenizerModule)); - pMod->interp = interp; - pMod->pScript = pScript; - pMod->pContext = pContext; - Tcl_IncrRefCount(pScript); - rc = pApi->xCreateTokenizer(pApi, zName, (void*)pMod, &t, f5tDelTokenizer); - if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, "error in fts5_api.xCreateTokenizer()", 0); - return TCL_ERROR; - } - - return TCL_OK; -} - -static void xF5tFree(ClientData clientData){ - ckfree(clientData); -} - -/* -** Entry point. -*/ -int Fts5tcl_Init(Tcl_Interp *interp){ - static struct Cmd { - char *zName; - Tcl_ObjCmdProc *xProc; - int bTokenizeCtx; - } aCmd[] = { - { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 }, - { "sqlite3_fts5_token", f5tTokenizerReturn, 1 }, - { "sqlite3_fts5_tokenize", f5tTokenize, 0 }, - { "sqlite3_fts5_create_function", f5tCreateFunction, 0 } - }; - int i; - F5tTokenizerContext *pContext; - - pContext = ckalloc(sizeof(F5tTokenizerContext)); - memset(pContext, 0, sizeof(*pContext)); - - for(i=0; ibTokenizeCtx ) pCtx = (void*)pContext; - Tcl_CreateObjCommand(interp, p->zName, p->xProc, pCtx, (i ? 0 : xF5tFree)); - } - - return TCL_OK; -} - -#endif DELETED ext/fts5/fts5_tokenize.c Index: ext/fts5/fts5_tokenize.c ================================================================== --- ext/fts5/fts5_tokenize.c +++ /dev/null @@ -1,865 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -*/ - -#include "fts5.h" -#include -#include - -/************************************************************************** -** Start of ascii tokenizer implementation. -*/ - -/* -** For tokenizers with no "unicode" modifier, the set of token characters -** is the same as the set of ASCII range alphanumeric characters. -*/ -static unsigned char aAsciiTokenChar[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00..0x0F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10..0x1F */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20..0x2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30..0x3F */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40..0x4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50..0x5F */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60..0x6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70..0x7F */ -}; - -typedef struct AsciiTokenizer AsciiTokenizer; -struct AsciiTokenizer { - unsigned char aTokenChar[128]; -}; - -static void fts5AsciiAddExceptions( - AsciiTokenizer *p, - const char *zArg, - int bTokenChars -){ - int i; - for(i=0; zArg[i]; i++){ - if( (zArg[i] & 0x80)==0 ){ - p->aTokenChar[(int)zArg[i]] = (unsigned char)bTokenChars; - } - } -} - -/* -** Create a "ascii" tokenizer. -*/ -static int fts5AsciiCreate( - void *pCtx, - const char **azArg, int nArg, - Fts5Tokenizer **ppOut -){ - int rc = SQLITE_OK; - AsciiTokenizer *p = 0; - if( nArg%2 ){ - rc = SQLITE_ERROR; - }else{ - p = sqlite3_malloc(sizeof(AsciiTokenizer)); - if( p==0 ){ - rc = SQLITE_NOMEM; - }else{ - int i; - memset(p, 0, sizeof(AsciiTokenizer)); - memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar)); - for(i=0; rc==SQLITE_OK && i='A' && c<='Z' ) c += 32; - aOut[i] = c; - } -} - -/* -** Tokenize some text using the ascii tokenizer. -*/ -static int fts5AsciiTokenize( - Fts5Tokenizer *pTokenizer, - void *pCtx, - const char *pText, int nText, - int (*xToken)(void*, const char*, int nToken, int iStart, int iEnd) -){ - AsciiTokenizer *p = (AsciiTokenizer*)pTokenizer; - int rc = SQLITE_OK; - int ie; - int is = 0; - - char aFold[64]; - int nFold = sizeof(aFold); - char *pFold = aFold; - unsigned char *a = p->aTokenChar; - - while( isnFold ){ - if( pFold!=aFold ) sqlite3_free(pFold); - pFold = sqlite3_malloc(nByte*2); - if( pFold==0 ){ - rc = SQLITE_NOMEM; - break; - } - nFold = nByte*2; - } - asciiFold(pFold, &pText[is], nByte); - - /* Invoke the token callback */ - rc = xToken(pCtx, pFold, nByte, is, ie); - is = ie+1; - } - - if( pFold!=aFold ) sqlite3_free(pFold); - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - return rc; -} - -/************************************************************************** -** Start of unicode61 tokenizer implementation. -*/ - -/* -** Functions in fts5_unicode2.c. -*/ -int sqlite3Fts5UnicodeIsalnum(int c); -int sqlite3Fts5UnicodeIsdiacritic(int c); -int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic); - - -/* -** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied -** from the sqlite3 source file utf.c. If this file is compiled as part -** of the amalgamation, they are not required. -*/ -#ifndef SQLITE_AMALGAMATION - -static const unsigned char sqlite3Utf8Trans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, -}; - -#define READ_UTF8(zIn, zTerm, c) \ - c = *(zIn++); \ - if( c>=0xc0 ){ \ - c = sqlite3Utf8Trans1[c-0xc0]; \ - while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ - c = (c<<6) + (0x3f & *(zIn++)); \ - } \ - if( c<0x80 \ - || (c&0xFFFFF800)==0xD800 \ - || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ - } - - -#define WRITE_UTF8(zOut, c) { \ - if( c<0x00080 ){ \ - *zOut++ = (unsigned char)(c&0xFF); \ - } \ - else if( c<0x00800 ){ \ - *zOut++ = 0xC0 + (unsigned char)((c>>6)&0x1F); \ - *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ - } \ - else if( c<0x10000 ){ \ - *zOut++ = 0xE0 + (unsigned char)((c>>12)&0x0F); \ - *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ - }else{ \ - *zOut++ = 0xF0 + (unsigned char)((c>>18) & 0x07); \ - *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \ - *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \ - *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \ - } \ -} - -#endif /* ifndef SQLITE_AMALGAMATION */ - -typedef struct Unicode61Tokenizer Unicode61Tokenizer; -struct Unicode61Tokenizer { - unsigned char aTokenChar[128]; /* ASCII range token characters */ - char *aFold; /* Buffer to fold text into */ - int nFold; /* Size of aFold[] in bytes */ - int bRemoveDiacritic; /* True if remove_diacritics=1 is set */ - int nException; - int *aiException; -}; - -static int fts5UnicodeAddExceptions( - Unicode61Tokenizer *p, /* Tokenizer object */ - const char *z, /* Characters to treat as exceptions */ - int bTokenChars /* 1 for 'tokenchars', 0 for 'separators' */ -){ - int rc = SQLITE_OK; - int n = strlen(z); - int *aNew; - - if( n>0 ){ - aNew = (int*)sqlite3_realloc(p->aiException, (n+p->nException)*sizeof(int)); - if( aNew ){ - int nNew = p->nException; - const unsigned char *zCsr = (const unsigned char*)z; - const unsigned char *zTerm = (const unsigned char*)&z[n]; - while( zCsraTokenChar[iCode] = bTokenChars; - }else{ - bToken = sqlite3Fts5UnicodeIsalnum(iCode); - assert( (bToken==0 || bToken==1) ); - assert( (bTokenChars==0 || bTokenChars==1) ); - if( bToken!=bTokenChars && sqlite3Fts5UnicodeIsdiacritic(iCode)==0 ){ - int i; - for(i=0; iiCode ) break; - } - memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int)); - aNew[i] = iCode; - nNew++; - } - } - } - p->aiException = aNew; - p->nException = nNew; - }else{ - rc = SQLITE_NOMEM; - } - } - - return rc; -} - -/* -** Return true if the p->aiException[] array contains the value iCode. -*/ -static int fts5UnicodeIsException(Unicode61Tokenizer *p, int iCode){ - if( p->nException>0 ){ - int *a = p->aiException; - int iLo = 0; - int iHi = p->nException-1; - - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - if( iCode==a[iTest] ){ - return 1; - }else if( iCode>a[iTest] ){ - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - } - - return 0; -} - -/* -** Delete a "unicode61" tokenizer. -*/ -static void fts5UnicodeDelete(Fts5Tokenizer *pTok){ - if( pTok ){ - Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTok; - sqlite3_free(p->aiException); - sqlite3_free(p->aFold); - sqlite3_free(p); - } - return; -} - -/* -** Create a "unicode61" tokenizer. -*/ -static int fts5UnicodeCreate( - void *pCtx, - const char **azArg, int nArg, - Fts5Tokenizer **ppOut -){ - int rc = SQLITE_OK; /* Return code */ - Unicode61Tokenizer *p = 0; /* New tokenizer object */ - - if( nArg%2 ){ - rc = SQLITE_ERROR; - }else{ - p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer)); - if( p ){ - int i; - memset(p, 0, sizeof(Unicode61Tokenizer)); - memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar)); - p->bRemoveDiacritic = 1; - p->nFold = 64; - p->aFold = sqlite3_malloc(p->nFold * sizeof(char)); - if( p->aFold==0 ){ - rc = SQLITE_NOMEM; - } - for(i=0; rc==SQLITE_OK && ibRemoveDiacritic = (zArg[0]=='1'); - }else - if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){ - rc = fts5UnicodeAddExceptions(p, zArg, 1); - }else - if( 0==sqlite3_stricmp(azArg[i], "separators") ){ - rc = fts5UnicodeAddExceptions(p, zArg, 0); - }else{ - rc = SQLITE_ERROR; - } - } - }else{ - rc = SQLITE_NOMEM; - } - if( rc!=SQLITE_OK ){ - fts5UnicodeDelete((Fts5Tokenizer*)p); - p = 0; - } - *ppOut = (Fts5Tokenizer*)p; - } - return rc; -} - -/* -** Return true if, for the purposes of tokenizing with the tokenizer -** passed as the first argument, codepoint iCode is considered a token -** character (not a separator). -*/ -static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){ - assert( (sqlite3Fts5UnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 ); - return sqlite3Fts5UnicodeIsalnum(iCode) ^ fts5UnicodeIsException(p, iCode); -} - -static int fts5UnicodeTokenize( - Fts5Tokenizer *pTokenizer, - void *pCtx, - const char *pText, int nText, - int (*xToken)(void*, const char*, int nToken, int iStart, int iEnd) -){ - Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTokenizer; - int rc = SQLITE_OK; - unsigned char *a = p->aTokenChar; - - unsigned char *zTerm = (unsigned char*)&pText[nText]; - unsigned char *zCsr = (unsigned char *)pText; - - /* Output buffer */ - char *aFold = p->aFold; - int nFold = p->nFold; - - /* Each iteration of this loop gobbles up a contiguous run of separators, - ** then the next token. */ - while( rc==SQLITE_OK ){ - int iCode; /* non-ASCII codepoint read from input */ - char *zOut = aFold; - int is; - int ie; - - /* Skip any separator characters. */ - while( 1 ){ - if( zCsr>=zTerm ) goto tokenize_done; - if( *zCsr & 0x80 ) { - /* A character outside of the ascii range. Skip past it if it is - ** a separator character. Or break out of the loop if it is not. */ - is = zCsr - (unsigned char*)pText; - READ_UTF8(zCsr, zTerm, iCode); - if( fts5UnicodeIsAlnum(p, iCode) ){ - goto non_ascii_tokenchar; - } - }else{ - if( a[*zCsr] ){ - is = zCsr - (unsigned char*)pText; - goto ascii_tokenchar; - } - zCsr++; - } - } - - /* Run through the tokenchars. Fold them into the output buffer along - ** the way. */ - while( zCsrnFold ){ - aFold = sqlite3_malloc(nFold*2); - if( aFold==0 ){ - rc = SQLITE_NOMEM; - goto tokenize_done; - } - memcpy(aFold, p->aFold, nFold); - sqlite3_free(p->aFold); - p->aFold = aFold; - p->nFold = nFold = nFold*2; - } - - if( *zCsr & 0x80 ){ - /* An non-ascii-range character. Fold it into the output buffer if - ** it is a token character, or break out of the loop if it is not. */ - READ_UTF8(zCsr, zTerm, iCode); - if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){ - non_ascii_tokenchar: - iCode = sqlite3Fts5UnicodeFold(iCode, p->bRemoveDiacritic); - if( iCode ) WRITE_UTF8(zOut, iCode); - }else{ - break; - } - }else if( a[*zCsr]==0 ){ - /* An ascii-range separator character. End of token. */ - break; - }else{ - ascii_tokenchar: - if( *zCsr>='A' && *zCsr<='Z' ){ - *zOut++ = *zCsr + 32; - }else{ - *zOut++ = *zCsr; - } - zCsr++; - } - ie = zCsr - (unsigned char*)pText; - } - - /* Invoke the token callback */ - rc = xToken(pCtx, aFold, zOut-aFold, is, ie); - } - - tokenize_done: - if( rc==SQLITE_DONE ) rc = SQLITE_OK; - return rc; -} - -/************************************************************************** -** Start of porter stemmer implementation. -*/ - -/* Any tokens larger than this (in bytes) are passed through without -** stemming. */ -#define FTS5_PORTER_MAX_TOKEN 64 - -typedef struct PorterTokenizer PorterTokenizer; -struct PorterTokenizer { - fts5_tokenizer tokenizer; /* Parent tokenizer module */ - Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */ - char aBuf[FTS5_PORTER_MAX_TOKEN + 64]; -}; - -/* -** Delete a "porter" tokenizer. -*/ -static void fts5PorterDelete(Fts5Tokenizer *pTok){ - if( pTok ){ - PorterTokenizer *p = (PorterTokenizer*)pTok; - if( p->pTokenizer ){ - p->tokenizer.xDelete(p->pTokenizer); - } - sqlite3_free(p); - } -} - -/* -** Create a "porter" tokenizer. -*/ -static int fts5PorterCreate( - void *pCtx, - const char **azArg, int nArg, - Fts5Tokenizer **ppOut -){ - fts5_api *pApi = (fts5_api*)pCtx; - int rc = SQLITE_OK; - PorterTokenizer *pRet; - void *pUserdata = 0; - - pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer)); - if( pRet ){ - memset(pRet, 0, sizeof(PorterTokenizer)); - rc = pApi->xFindTokenizer(pApi, "ascii", &pUserdata, &pRet->tokenizer); - }else{ - rc = SQLITE_NOMEM; - } - if( rc==SQLITE_OK ){ - rc = pRet->tokenizer.xCreate(pUserdata, 0, 0, &pRet->pTokenizer); - } - - if( rc!=SQLITE_OK ){ - fts5PorterDelete((Fts5Tokenizer*)pRet); - pRet = 0; - } - *ppOut = (Fts5Tokenizer*)pRet; - return rc; -} - -typedef struct PorterContext PorterContext; -struct PorterContext { - void *pCtx; - int (*xToken)(void*, const char*, int, int, int); - char *aBuf; -}; - -typedef struct PorterRule PorterRule; -struct PorterRule { - const char *zSuffix; - int nSuffix; - int (*xCond)(char *zStem, int nStem); - const char *zOutput; - int nOutput; -}; - -static int fts5PorterApply(char *aBuf, int *pnBuf, PorterRule *aRule){ - int ret = -1; - int nBuf = *pnBuf; - PorterRule *p; - - for(p=aRule; p->zSuffix; p++){ - assert( strlen(p->zSuffix)==p->nSuffix ); - assert( strlen(p->zOutput)==p->nOutput ); - if( nBufnSuffix ) continue; - if( 0==memcmp(&aBuf[nBuf - p->nSuffix], p->zSuffix, p->nSuffix) ) break; - } - - if( p->zSuffix ){ - int nStem = nBuf - p->nSuffix; - if( p->xCond==0 || p->xCond(aBuf, nStem) ){ - memcpy(&aBuf[nStem], p->zOutput, p->nOutput); - *pnBuf = nStem + p->nOutput; - ret = p - aRule; - } - } - - return ret; -} - -static int fts5PorterIsVowel(char c, int bYIsVowel){ - return ( - c=='a' || c=='e' || c=='i' || c=='o' || c=='u' || (bYIsVowel && c=='y') - ); -} - -static int fts5PorterGobbleVC(char *zStem, int nStem, int bPrevCons){ - int i; - int bCons = bPrevCons; - - /* Scan for a vowel */ - for(i=0; i 0) */ -static int fts5Porter_MGt0(char *zStem, int nStem){ - return !!fts5PorterGobbleVC(zStem, nStem, 0); -} - -/* porter rule condition: (m > 1) */ -static int fts5Porter_MGt1(char *zStem, int nStem){ - int n; - n = fts5PorterGobbleVC(zStem, nStem, 0); - if( n && fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){ - return 1; - } - return 0; -} - -/* porter rule condition: (m = 1) */ -static int fts5Porter_MEq1(char *zStem, int nStem){ - int n; - n = fts5PorterGobbleVC(zStem, nStem, 0); - if( n && 0==fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){ - return 1; - } - return 0; -} - -/* porter rule condition: (*o) */ -static int fts5Porter_Ostar(char *zStem, int nStem){ - if( zStem[nStem-1]=='w' || zStem[nStem-1]=='x' || zStem[nStem-1]=='y' ){ - return 0; - }else{ - int i; - int mask = 0; - int bCons = 0; - for(i=0; i 1 and (*S or *T)) */ -static int fts5Porter_MGt1_and_S_or_T(char *zStem, int nStem){ - return nStem>0 - && (zStem[nStem-1]=='s' || zStem[nStem-1]=='t') - && fts5Porter_MGt1(zStem, nStem); -} - -/* porter rule condition: (*v*) */ -static int fts5Porter_Vowel(char *zStem, int nStem){ - int i; - for(i=0; i0) ){ - return 1; - } - } - return 0; -} - -static int fts5PorterCb( - void *pCtx, - const char *pToken, - int nToken, - int iStart, - int iEnd -){ - PorterContext *p = (PorterContext*)pCtx; - - PorterRule aStep1A[] = { - { "sses", 4, 0, "ss", 2 }, - { "ies", 3, 0, "i", 1 }, - { "ss", 2, 0, "ss", 2 }, - { "s", 1, 0, "", 0 }, - { 0, 0, 0, 0 } - }; - - PorterRule aStep1B[] = { - { "eed", 3, fts5Porter_MGt0, "ee", 2 }, - { "ed", 2, fts5Porter_Vowel, "", 0 }, - { "ing", 3, fts5Porter_Vowel, "", 0 }, - { 0, 0, 0, 0 } - }; - - PorterRule aStep1B2[] = { - { "at", 2, 0, "ate", 3 }, - { "bl", 2, 0, "ble", 3 }, - { "iz", 2, 0, "ize", 3 }, - { 0, 0, 0, 0 } - }; - - PorterRule aStep1C[] = { - { "y", 1, fts5Porter_Vowel, "i", 1 }, - { 0, 0, 0, 0 } - }; - - PorterRule aStep2[] = { - { "ational", 7, fts5Porter_MGt0, "ate", 3}, - { "tional", 6, fts5Porter_MGt0, "tion", 4}, - { "enci", 4, fts5Porter_MGt0, "ence", 4}, - { "anci", 4, fts5Porter_MGt0, "ance", 4}, - { "izer", 4, fts5Porter_MGt0, "ize", 3}, - { "logi", 4, fts5Porter_MGt0, "log", 3}, /* added post 1979 */ - { "bli", 3, fts5Porter_MGt0, "ble", 3}, /* modified post 1979 */ - { "alli", 4, fts5Porter_MGt0, "al", 2}, - { "entli", 5, fts5Porter_MGt0, "ent", 3}, - { "eli", 3, fts5Porter_MGt0, "e", 1}, - { "ousli", 5, fts5Porter_MGt0, "ous", 3}, - { "ization", 7, fts5Porter_MGt0, "ize", 3}, - { "ation", 5, fts5Porter_MGt0, "ate", 3}, - { "ator", 4, fts5Porter_MGt0, "ate", 3}, - { "alism", 5, fts5Porter_MGt0, "al", 2}, - { "iveness", 7, fts5Porter_MGt0, "ive", 3}, - { "fulness", 7, fts5Porter_MGt0, "ful", 3}, - { "ousness", 7, fts5Porter_MGt0, "ous", 3}, - { "aliti", 5, fts5Porter_MGt0, "al", 2}, - { "iviti", 5, fts5Porter_MGt0, "ive", 3}, - { "biliti", 6, fts5Porter_MGt0, "ble", 3}, - { 0, 0, 0, 0 } - }; - - PorterRule aStep3[] = { - { "icate", 5, fts5Porter_MGt0, "ic", 2}, - { "ative", 5, fts5Porter_MGt0, "", 0}, - { "alize", 5, fts5Porter_MGt0, "al", 2}, - { "iciti", 5, fts5Porter_MGt0, "ic", 2}, - { "ical", 4, fts5Porter_MGt0, "ic", 2}, - { "ful", 3, fts5Porter_MGt0, "", 0}, - { "ness", 4, fts5Porter_MGt0, "", 0}, - { 0, 0, 0, 0 } - }; - - PorterRule aStep4[] = { - { "al", 2, fts5Porter_MGt1, "", 0}, - { "ance", 4, fts5Porter_MGt1, "", 0}, - { "ence", 4, fts5Porter_MGt1, "", 0}, - { "er", 2, fts5Porter_MGt1, "", 0}, - { "ic", 2, fts5Porter_MGt1, "", 0}, - { "able", 4, fts5Porter_MGt1, "", 0}, - { "ible", 4, fts5Porter_MGt1, "", 0}, - { "ant", 3, fts5Porter_MGt1, "", 0}, - { "ement", 5, fts5Porter_MGt1, "", 0}, - { "ment", 4, fts5Porter_MGt1, "", 0}, - { "ent", 3, fts5Porter_MGt1, "", 0}, - { "ion", 3, fts5Porter_MGt1_and_S_or_T, "", 0}, - { "ou", 2, fts5Porter_MGt1, "", 0}, - { "ism", 3, fts5Porter_MGt1, "", 0}, - { "ate", 3, fts5Porter_MGt1, "", 0}, - { "iti", 3, fts5Porter_MGt1, "", 0}, - { "ous", 3, fts5Porter_MGt1, "", 0}, - { "ive", 3, fts5Porter_MGt1, "", 0}, - { "ize", 3, fts5Porter_MGt1, "", 0}, - { 0, 0, 0, 0 } - }; - - - char *aBuf; - int nBuf; - int n; - - if( nToken>FTS5_PORTER_MAX_TOKEN || nToken<3 ) goto pass_through; - aBuf = p->aBuf; - nBuf = nToken; - memcpy(aBuf, pToken, nBuf); - - /* Step 1. */ - fts5PorterApply(aBuf, &nBuf, aStep1A); - n = fts5PorterApply(aBuf, &nBuf, aStep1B); - if( n==1 || n==2 ){ - if( fts5PorterApply(aBuf, &nBuf, aStep1B2)<0 ){ - char c = aBuf[nBuf-1]; - if( fts5PorterIsVowel(c, 0)==0 - && c!='l' && c!='s' && c!='z' && c==aBuf[nBuf-2] - ){ - nBuf--; - }else if( fts5Porter_MEq1(aBuf, nBuf) && fts5Porter_Ostar(aBuf, nBuf) ){ - aBuf[nBuf++] = 'e'; - } - } - } - fts5PorterApply(aBuf, &nBuf, aStep1C); - - /* Steps 2 through 4. */ - fts5PorterApply(aBuf, &nBuf, aStep2); - fts5PorterApply(aBuf, &nBuf, aStep3); - fts5PorterApply(aBuf, &nBuf, aStep4); - - /* Step 5a. */ - if( nBuf>0 && aBuf[nBuf-1]=='e' ){ - if( fts5Porter_MGt1(aBuf, nBuf-1) - || (fts5Porter_MEq1(aBuf, nBuf-1) && !fts5Porter_Ostar(aBuf, nBuf-1)) - ){ - nBuf--; - } - } - - /* Step 5b. */ - if( nBuf>1 && aBuf[nBuf-1]=='l' - && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1) - ){ - nBuf--; - } - - return p->xToken(p->pCtx, aBuf, nBuf, iStart, iEnd); - - pass_through: - return p->xToken(p->pCtx, pToken, nToken, iStart, iEnd); -} - -/* -** Tokenize using the porter tokenizer. -*/ -static int fts5PorterTokenize( - Fts5Tokenizer *pTokenizer, - void *pCtx, - const char *pText, int nText, - int (*xToken)(void*, const char*, int nToken, int iStart, int iEnd) -){ - PorterTokenizer *p = (PorterTokenizer*)pTokenizer; - PorterContext sCtx; - sCtx.xToken = xToken; - sCtx.pCtx = pCtx; - sCtx.aBuf = p->aBuf; - return p->tokenizer.xTokenize( - p->pTokenizer, (void*)&sCtx, pText, nText, fts5PorterCb - ); -} - -/* -** Register all built-in tokenizers with FTS5. -*/ -int sqlite3Fts5TokenizerInit(fts5_api *pApi){ - struct BuiltinTokenizer { - const char *zName; - fts5_tokenizer x; - } aBuiltin[] = { - { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}}, - { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }}, - { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }}, - }; - - int rc = SQLITE_OK; /* Return code */ - int i; /* To iterate through builtin functions */ - - for(i=0; rc==SQLITE_OK && ixCreateTokenizer(pApi, - aBuiltin[i].zName, - (void*)pApi, - &aBuiltin[i].x, - 0 - ); - } - - return SQLITE_OK; -} - - DELETED ext/fts5/fts5_unicode2.c Index: ext/fts5/fts5_unicode2.c ================================================================== --- ext/fts5/fts5_unicode2.c +++ /dev/null @@ -1,363 +0,0 @@ -/* -** 2012 May 25 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -*/ - -/* -** DO NOT EDIT THIS MACHINE GENERATED FILE. -*/ - -#if defined(SQLITE_ENABLE_FTS5) - -#include - -/* -** Return true if the argument corresponds to a unicode codepoint -** classified as either a letter or a number. Otherwise false. -** -** The results are undefined if the value passed to this function -** is less than zero. -*/ -int sqlite3Fts5UnicodeIsalnum(int c){ - /* Each unsigned integer in the following array corresponds to a contiguous - ** range of unicode codepoints that are not either letters or numbers (i.e. - ** codepoints for which this function should return 0). - ** - ** The most significant 22 bits in each 32-bit value contain the first - ** codepoint in the range. The least significant 10 bits are used to store - ** the size of the range (always at least 1). In other words, the value - ** ((C<<22) + N) represents a range of N codepoints starting with codepoint - ** C. It is not possible to represent a range larger than 1023 codepoints - ** using this format. - */ - const static unsigned int aEntry[] = { - 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07, - 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01, - 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401, - 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01, - 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01, - 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802, - 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F, - 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401, - 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804, - 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403, - 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812, - 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001, - 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802, - 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805, - 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401, - 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03, - 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807, - 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001, - 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01, - 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804, - 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001, - 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802, - 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01, - 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06, - 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, - 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006, - 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417, - 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14, - 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07, - 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01, - 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001, - 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802, - 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F, - 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, - 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802, - 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006, - 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D, - 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802, - 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027, - 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, - 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805, - 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04, - 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401, - 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, - 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B, - 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A, - 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, - 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59, - 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, - 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, - 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, - 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100, - 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10, - 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402, - 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804, - 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012, - 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004, - 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002, - 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803, - 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07, - 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02, - 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802, - 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013, - 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06, - 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003, - 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01, - 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403, - 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009, - 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003, - 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003, - 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E, - 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046, - 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401, - 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401, - 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F, - 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C, - 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002, - 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025, - 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6, - 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46, - 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060, - 0x380400F0, - }; - static const unsigned int aAscii[4] = { - 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001, - }; - - if( c<128 ){ - return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 ); - }else if( c<(1<<22) ){ - unsigned int key = (((unsigned int)c)<<10) | 0x000003FF; - int iRes; - int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; - int iLo = 0; - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - if( key >= aEntry[iTest] ){ - iRes = iTest; - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - assert( aEntry[0]=aEntry[iRes] ); - return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF))); - } - return 1; -} - - -/* -** If the argument is a codepoint corresponding to a lowercase letter -** in the ASCII range with a diacritic added, return the codepoint -** of the ASCII letter only. For example, if passed 235 - "LATIN -** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER -** E"). The resuls of passing a codepoint that corresponds to an -** uppercase letter are undefined. -*/ -static int remove_diacritic(int c){ - unsigned short aDia[] = { - 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, - 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, - 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, - 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, - 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, - 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, - 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, - 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, - 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, - 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, - 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, - 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, - 62924, 63050, 63082, 63274, 63390, - }; - char aChar[] = { - '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', - 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', - 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', - 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', - 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', - 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', - 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', - 'e', 'i', 'o', 'u', 'y', - }; - - unsigned int key = (((unsigned int)c)<<3) | 0x00000007; - int iRes = 0; - int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1; - int iLo = 0; - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - if( key >= aDia[iTest] ){ - iRes = iTest; - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - assert( key>=aDia[iRes] ); - return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); -}; - - -/* -** Return true if the argument interpreted as a unicode codepoint -** is a diacritical modifier character. -*/ -int sqlite3Fts5UnicodeIsdiacritic(int c){ - unsigned int mask0 = 0x08029FDF; - unsigned int mask1 = 0x000361F8; - if( c<768 || c>817 ) return 0; - return (c < 768+32) ? - (mask0 & (1 << (c-768))) : - (mask1 & (1 << (c-768-32))); -} - - -/* -** Interpret the argument as a unicode codepoint. If the codepoint -** is an upper case character that has a lower case equivalent, -** return the codepoint corresponding to the lower case version. -** Otherwise, return a copy of the argument. -** -** The results are undefined if the value passed to this function -** is less than zero. -*/ -int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ - /* Each entry in the following array defines a rule for folding a range - ** of codepoints to lower case. The rule applies to a range of nRange - ** codepoints starting at codepoint iCode. - ** - ** If the least significant bit in flags is clear, then the rule applies - ** to all nRange codepoints (i.e. all nRange codepoints are upper case and - ** need to be folded). Or, if it is set, then the rule only applies to - ** every second codepoint in the range, starting with codepoint C. - ** - ** The 7 most significant bits in flags are an index into the aiOff[] - ** array. If a specific codepoint C does require folding, then its lower - ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF). - ** - ** The contents of this array are generated by parsing the CaseFolding.txt - ** file distributed as part of the "Unicode Character Database". See - ** http://www.unicode.org for details. - */ - static const struct TableEntry { - unsigned short iCode; - unsigned char flags; - unsigned char nRange; - } aEntry[] = { - {65, 14, 26}, {181, 64, 1}, {192, 14, 23}, - {216, 14, 7}, {256, 1, 48}, {306, 1, 6}, - {313, 1, 16}, {330, 1, 46}, {376, 116, 1}, - {377, 1, 6}, {383, 104, 1}, {385, 50, 1}, - {386, 1, 4}, {390, 44, 1}, {391, 0, 1}, - {393, 42, 2}, {395, 0, 1}, {398, 32, 1}, - {399, 38, 1}, {400, 40, 1}, {401, 0, 1}, - {403, 42, 1}, {404, 46, 1}, {406, 52, 1}, - {407, 48, 1}, {408, 0, 1}, {412, 52, 1}, - {413, 54, 1}, {415, 56, 1}, {416, 1, 6}, - {422, 60, 1}, {423, 0, 1}, {425, 60, 1}, - {428, 0, 1}, {430, 60, 1}, {431, 0, 1}, - {433, 58, 2}, {435, 1, 4}, {439, 62, 1}, - {440, 0, 1}, {444, 0, 1}, {452, 2, 1}, - {453, 0, 1}, {455, 2, 1}, {456, 0, 1}, - {458, 2, 1}, {459, 1, 18}, {478, 1, 18}, - {497, 2, 1}, {498, 1, 4}, {502, 122, 1}, - {503, 134, 1}, {504, 1, 40}, {544, 110, 1}, - {546, 1, 18}, {570, 70, 1}, {571, 0, 1}, - {573, 108, 1}, {574, 68, 1}, {577, 0, 1}, - {579, 106, 1}, {580, 28, 1}, {581, 30, 1}, - {582, 1, 10}, {837, 36, 1}, {880, 1, 4}, - {886, 0, 1}, {902, 18, 1}, {904, 16, 3}, - {908, 26, 1}, {910, 24, 2}, {913, 14, 17}, - {931, 14, 9}, {962, 0, 1}, {975, 4, 1}, - {976, 140, 1}, {977, 142, 1}, {981, 146, 1}, - {982, 144, 1}, {984, 1, 24}, {1008, 136, 1}, - {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1}, - {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1}, - {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32}, - {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1}, - {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38}, - {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1}, - {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1}, - {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6}, - {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6}, - {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8}, - {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2}, - {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1}, - {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2}, - {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2}, - {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2}, - {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1}, - {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16}, - {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47}, - {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1}, - {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1}, - {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1}, - {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2}, - {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1}, - {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14}, - {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1}, - {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1}, - {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1}, - {65313, 14, 26}, - }; - static const unsigned short aiOff[] = { - 1, 2, 8, 15, 16, 26, 28, 32, - 37, 38, 40, 48, 63, 64, 69, 71, - 79, 80, 116, 202, 203, 205, 206, 207, - 209, 210, 211, 213, 214, 217, 218, 219, - 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721, - 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274, - 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406, - 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462, - 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511, - 65514, 65521, 65527, 65528, 65529, - }; - - int ret = c; - - assert( c>=0 ); - assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 ); - - if( c<128 ){ - if( c>='A' && c<='Z' ) ret = c + ('a' - 'A'); - }else if( c<65536 ){ - int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1; - int iLo = 0; - int iRes = -1; - - while( iHi>=iLo ){ - int iTest = (iHi + iLo) / 2; - int cmp = (c - aEntry[iTest].iCode); - if( cmp>=0 ){ - iRes = iTest; - iLo = iTest+1; - }else{ - iHi = iTest-1; - } - } - assert( iRes<0 || c>=aEntry[iRes].iCode ); - - if( iRes>=0 ){ - const struct TableEntry *p = &aEntry[iRes]; - if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){ - ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF; - assert( ret>0 ); - } - } - - if( bRemoveDiacritic ) ret = remove_diacritic(ret); - } - - else if( c>=66560 && c<66600 ){ - ret = c + 40; - } - - return ret; -} -#endif /* defined(SQLITE_ENABLE_FTS5) */ DELETED ext/fts5/fts5parse.y Index: ext/fts5/fts5parse.y ================================================================== --- ext/fts5/fts5parse.y +++ /dev/null @@ -1,155 +0,0 @@ -/* -** 2014 May 31 -** -** 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. -** -****************************************************************************** -** -*/ - - -// All token codes are small integers with #defines that begin with "TK_" -%token_prefix FTS5_ - -// The type of the data attached to each token is Token. This is also the -// default type for non-terminals. -// -%token_type {Fts5Token} -%default_type {Fts5Token} - -// The generated parser function takes a 4th argument as follows: -%extra_argument {Fts5Parse *pParse} - -// This code runs whenever there is a syntax error -// -%syntax_error { - sqlite3Fts5ParseError( - pParse, "fts5: syntax error near \"%.*s\"",TOKEN.n,TOKEN.p - ); -} -%stack_overflow { - assert( 0 ); -} - -// The name of the generated procedure that implements the parser -// is as follows: -%name sqlite3Fts5Parser - -// The following text is included near the beginning of the C source -// code file that implements the parser. -// -%include { -#include "fts5Int.h" -#include "fts5parse.h" - -/* -** Disable all error recovery processing in the parser push-down -** automaton. -*/ -#define YYNOERRORRECOVERY 1 - -/* -** Make yytestcase() the same as testcase() -*/ -#define yytestcase(X) testcase(X) - -} // end %include - -%left OR. -%left AND. -%left NOT. -%left COLON. - -input ::= expr(X). { sqlite3Fts5ParseFinished(pParse, X); } - -%type cnearset {Fts5ExprNode*} -%type expr {Fts5ExprNode*} -%type exprlist {Fts5ExprNode*} -%destructor cnearset { sqlite3Fts5ParseNodeFree($$); } -%destructor expr { sqlite3Fts5ParseNodeFree($$); } -%destructor exprlist { sqlite3Fts5ParseNodeFree($$); } - -expr(A) ::= expr(X) AND expr(Y). { - A = sqlite3Fts5ParseNode(pParse, FTS5_AND, X, Y, 0); -} -expr(A) ::= expr(X) OR expr(Y). { - A = sqlite3Fts5ParseNode(pParse, FTS5_OR, X, Y, 0); -} -expr(A) ::= expr(X) NOT expr(Y). { - A = sqlite3Fts5ParseNode(pParse, FTS5_NOT, X, Y, 0); -} - -expr(A) ::= LP expr(X) RP. {A = X;} -expr(A) ::= exprlist(X). {A = X;} - -exprlist(A) ::= cnearset(X). {A = X;} -exprlist(A) ::= exprlist(X) cnearset(Y). { - A = sqlite3Fts5ParseNode(pParse, FTS5_AND, X, Y, 0); -} - -cnearset(A) ::= nearset(X). { - A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, X); -} -cnearset(A) ::= STRING(X) COLON nearset(Y). { - sqlite3Fts5ParseSetColumn(pParse, Y, &X); - A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y); -} - -%type nearset {Fts5ExprNearset*} -%type nearphrases {Fts5ExprNearset*} -%destructor nearset { sqlite3Fts5ParseNearsetFree($$); } -%destructor nearphrases { sqlite3Fts5ParseNearsetFree($$); } - -nearset(A) ::= phrase(X). { A = sqlite3Fts5ParseNearset(pParse, 0, X); } -nearset(A) ::= STRING(X) LP nearphrases(Y) neardist_opt(Z) RP. { - sqlite3Fts5ParseNear(pParse, &X); - sqlite3Fts5ParseSetDistance(pParse, Y, &Z); - A = Y; -} - -nearphrases(A) ::= phrase(X). { - A = sqlite3Fts5ParseNearset(pParse, 0, X); -} -nearphrases(A) ::= nearphrases(X) phrase(Y). { - A = sqlite3Fts5ParseNearset(pParse, X, Y); -} - -/* -** The optional ", " at the end of the NEAR() arguments. -*/ -neardist_opt(A) ::= . { A.p = 0; A.n = 0; } -neardist_opt(A) ::= COMMA STRING(X). { A = X; } - -/* -** A phrase. A set of primitives connected by "+" operators. Examples: -** -** "the" + "quick brown" + fo * -** "the quick brown fo" * -** the+quick+brown+fo* -*/ -%type phrase {Fts5ExprPhrase*} -%destructor phrase { sqlite3Fts5ParsePhraseFree($$); } - -phrase(A) ::= phrase(X) PLUS STRING(Y) star_opt(Z). { - A = sqlite3Fts5ParseTerm(pParse, X, &Y, Z); -} -phrase(A) ::= STRING(Y) star_opt(Z). { - A = sqlite3Fts5ParseTerm(pParse, 0, &Y, Z); -} - -/* -** Optional "*" character. -*/ -%type star_opt {int} - -star_opt(A) ::= STAR. { A = 1; } -star_opt(A) ::= . { A = 0; } - - - - DELETED ext/fts5/test/fts5_common.tcl Index: ext/fts5/test/fts5_common.tcl ================================================================== --- ext/fts5/test/fts5_common.tcl +++ /dev/null @@ -1,117 +0,0 @@ -# 2014 Dec 19 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# - -if {![info exists testdir]} { - set testdir [file join [file dirname [info script]] .. .. .. test] -} -source $testdir/tester.tcl - - -proc fts5_test_poslist {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xInstCount]} {incr i} { - lappend res [string map {{ } .} [$cmd xInst $i]] - } - set res -} - -proc fts5_test_columnsize {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { - lappend res [$cmd xColumnSize $i] - } - set res -} - -proc fts5_test_columntext {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { - lappend res [$cmd xColumnText $i] - } - set res -} - -proc fts5_test_columntotalsize {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { - lappend res [$cmd xColumnTotalSize $i] - } - set res -} - -proc test_append_token {varname token iStart iEnd} { - upvar $varname var - lappend var $token -} -proc fts5_test_tokenize {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xColumnCount]} {incr i} { - set tokens [list] - $cmd xTokenize [$cmd xColumnText $i] [list test_append_token tokens] - lappend res $tokens - } - set res -} - -proc fts5_test_rowcount {cmd} { - $cmd xRowCount -} - -proc test_queryphrase_cb {cnt cmd} { - upvar $cnt L - for {set i 0} {$i < [$cmd xInstCount]} {incr i} { - foreach {ip ic io} [$cmd xInst $i] break - set A($ic) 1 - } - foreach ic [array names A] { - lset L $ic [expr {[lindex $L $ic] + 1}] - } -} -proc fts5_test_queryphrase {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xPhraseCount]} {incr i} { - set cnt [list] - for {set j 0} {$j < [$cmd xColumnCount]} {incr j} { lappend cnt 0 } - $cmd xQueryPhrase $i [list test_queryphrase_cb cnt] - lappend res $cnt - } - set res -} - -proc fts5_test_all {cmd} { - set res [list] - lappend res columnsize [fts5_test_columnsize $cmd] - lappend res columntext [fts5_test_columntext $cmd] - lappend res columntotalsize [fts5_test_columntotalsize $cmd] - lappend res poslist [fts5_test_poslist $cmd] - lappend res tokenize [fts5_test_tokenize $cmd] - lappend res rowcount [fts5_test_rowcount $cmd] - set res -} - -proc fts5_aux_test_functions {db} { - foreach f { - fts5_test_columnsize - fts5_test_columntext - fts5_test_columntotalsize - fts5_test_poslist - fts5_test_tokenize - fts5_test_rowcount - fts5_test_all - - fts5_test_queryphrase - } { - sqlite3_fts5_create_function $db $f $f - } -} - - DELETED ext/fts5/test/fts5aa.test Index: ext/fts5/test/fts5aa.test ================================================================== --- ext/fts5/test/fts5aa.test +++ /dev/null @@ -1,325 +0,0 @@ -# 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 fts5aa - -# If SQLITE_ENABLE_FTS3 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b, c); - SELECT name, sql FROM sqlite_master; -} { - t1 {CREATE VIRTUAL TABLE t1 USING fts5(a, b, c)} - t1_data {CREATE TABLE 't1_data'(id INTEGER PRIMARY KEY, block BLOB)} - t1_content {CREATE TABLE 't1_content'(id INTEGER PRIMARY KEY, c0, c1, c2)} - t1_docsize {CREATE TABLE 't1_docsize'(id INTEGER PRIMARY KEY, sz BLOB)} - t1_config {CREATE TABLE 't1_config'(k PRIMARY KEY, v) WITHOUT ROWID} -} - -do_execsql_test 1.1 { - DROP TABLE t1; - SELECT name, sql FROM sqlite_master; -} { -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 2.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y); -} -do_execsql_test 2.1 { - INSERT INTO t1 VALUES('a b c', 'd e f'); -} -do_execsql_test 2.2 { - SELECT fts5_decode(id, block) FROM t1_data WHERE id==10 -} { - {{structure idx=0} {lvl=0 nMerge=0 {id=27723 h=1 leaves=1..1}}} -} -do_execsql_test 2.3 { - INSERT INTO t1(t1) VALUES('integrity-check'); -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 3.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y); -} -foreach {i x y} { - 1 {g f d b f} {h h e i a} - 2 {f i g j e} {i j c f f} - 3 {e e i f a} {e h f d f} - 4 {h j f j i} {h a c f j} - 5 {d b j c g} {f e i b e} - 6 {a j a e e} {j d f d e} - 7 {g i j c h} {j d h c a} - 8 {j j i d d} {e e d f b} - 9 {c j j d c} {h j i f g} - 10 {b f h i a} {c f b b j} -} { - do_execsql_test 3.$i.1 { INSERT INTO t1 VALUES($x, $y) } - do_execsql_test 3.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') } - if {[set_test_counter errors]} break -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 4.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} -foreach {i x y} { - 1 {g f d b f} {h h e i a} - 2 {f i g j e} {i j c f f} - 3 {e e i f a} {e h f d f} - 4 {h j f j i} {h a c f j} - 5 {d b j c g} {f e i b e} - 6 {a j a e e} {j d f d e} - 7 {g i j c h} {j d h c a} - 8 {j j i d d} {e e d f b} - 9 {c j j d c} {h j i f g} - 10 {b f h i a} {c f b b j} -} { - do_execsql_test 4.$i.1 { INSERT INTO t1 VALUES($x, $y) } - do_execsql_test 4.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') } - if {[set_test_counter errors]} break -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 5.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} -foreach {i x y} { - 1 {dd abc abc abc abcde} {aaa dd ddd ddd aab} - 2 {dd aab d aaa b} {abcde c aaa aaa aaa} - 3 {abcde dd b b dd} {abc abc d abc ddddd} - 4 {aaa abcde dddd dddd abcde} {abc b b abcde abc} - 5 {aab dddd d dddd c} {ddd abcde dddd abcde c} - 6 {ddd dd b aab abcde} {d ddddd dddd c abc} - 7 {d ddddd ddd c abcde} {c aab d abcde ddd} - 8 {abcde aaa aab c c} {ddd c dddd b aaa} - 9 {abcde aab ddddd c aab} {dddd dddd b c dd} - 10 {ddd abcde dddd dd c} {dddd c c d abcde} -} { - do_execsql_test 5.$i.1 { INSERT INTO t1 VALUES($x, $y) } - do_execsql_test 5.$i.2 { INSERT INTO t1(t1) VALUES('integrity-check') } - if {[set_test_counter errors]} break -} - -#------------------------------------------------------------------------- -# -breakpoint -reset_db -do_execsql_test 6.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} - -do_execsql_test 6.1 { - INSERT INTO t1(rowid, x, y) VALUES(22, 'a b c', 'c b a'); - REPLACE INTO t1(rowid, x, y) VALUES(22, 'd e f', 'f e d'); -} - -do_execsql_test 6.2 { - INSERT INTO t1(t1) VALUES('integrity-check') -} - -#------------------------------------------------------------------------- -# -reset_db -expr srand(0) -do_execsql_test 7.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y,z); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} - -proc doc {} { - set v [list aaa aab abc abcde b c d dd ddd dddd ddddd] - set ret [list] - for {set j 0} {$j < 20} {incr j} { - lappend ret [lindex $v [expr int(rand()*[llength $v])]] - } - return $ret -} - -proc dump_structure {} { - db eval {SELECT fts5_decode(id, block) AS t FROM t1_data WHERE id=10} { - foreach lvl [lrange $t 1 end] { - set seg [string repeat . [expr [llength $lvl]-2]] - puts "[lrange $lvl 0 1] $seg" - } - } -} - -for {set i 1} {$i <= 10} {incr i} { - do_test 7.$i { - for {set j 0} {$j < 10} {incr j} { - set x [doc] - set y [doc] - set z [doc] - set rowid [expr int(rand() * 100)] - execsql { REPLACE INTO t1(rowid,x,y,z) VALUES($rowid, $x, $y, $z) } - } - execsql { INSERT INTO t1(t1) VALUES('integrity-check'); } - } {} -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 8.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x, prefix="1,2,3"); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} - -do_execsql_test 8.1 { - INSERT INTO t1 VALUES('the quick brown fox'); - INSERT INTO t1(t1) VALUES('integrity-check'); -} - - -#------------------------------------------------------------------------- -# -reset_db - -expr srand(0) - -do_execsql_test 9.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y,z, prefix="1,2,3"); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} - -proc doc {} { - set v [list aaa aab abc abcde b c d dd ddd dddd ddddd] - set ret [list] - for {set j 0} {$j < 20} {incr j} { - lappend ret [lindex $v [expr int(rand()*[llength $v])]] - } - return $ret -} - -proc dump_structure {} { - db eval {SELECT fts5_decode(id, block) AS t FROM t1_data WHERE id=10} { - foreach lvl [lrange $t 1 end] { - set seg [string repeat . [expr [llength $lvl]-2]] - puts "[lrange $lvl 0 1] $seg" - } - } -} - -for {set i 1} {$i <= 10} {incr i} { - do_test 9.$i { - for {set j 0} {$j < 100} {incr j} { - set x [doc] - set y [doc] - set z [doc] - set rowid [expr int(rand() * 100)] - execsql { REPLACE INTO t1(rowid,x,y,z) VALUES($rowid, $x, $y, $z) } - } - execsql { INSERT INTO t1(t1) VALUES('integrity-check'); } - } {} - if {[set_test_counter errors]} break -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 10.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x,y); -} -set d10 { - 1 {g f d b f} {h h e i a} - 2 {f i g j e} {i j c f f} - 3 {e e i f a} {e h f d f} - 4 {h j f j i} {h a c f j} - 5 {d b j c g} {f e i b e} - 6 {a j a e e} {j d f d e} - 7 {g i j c h} {j d h c a} - 8 {j j i d d} {e e d f b} - 9 {c j j d c} {h j i f g} - 10 {b f h i a} {c f b b j} -} -foreach {rowid x y} $d10 { - do_execsql_test 10.1.$rowid.1 { INSERT INTO t1 VALUES($x, $y) } - do_execsql_test 10.1.$rowid.2 { INSERT INTO t1(t1) VALUES('integrity-check') } -} -foreach rowid {5 9 8 1 2 4 10 7 3 5 6} { - do_execsql_test 10.2.$rowid.1 { DELETE FROM t1 WHERE rowid = $rowid } - do_execsql_test 10.2.$rowid.2 { INSERT INTO t1(t1) VALUES('integrity-check') } -} -foreach {rowid x y} $d10 { - do_execsql_test 10.3.$rowid.1 { INSERT INTO t1 VALUES($x, $y) } - do_execsql_test 10.3.$rowid.2 { INSERT INTO t1(t1) VALUES('integrity-check') } -} - -do_execsql_test 10.4.1 { DELETE FROM t1 } -do_execsql_test 10.4.2 { INSERT INTO t1(t1) VALUES('integrity-check') } - -#------------------------------------------------------------------------- -# -do_catchsql_test 11.1 { - CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rank); -} {1 {reserved fts5 column name: rank}} -do_catchsql_test 11.2 { - CREATE VIRTUAL TABLE rank USING fts5(a, b, c); -} {1 {reserved fts5 table name: rank}} - -#------------------------------------------------------------------------- -# -do_execsql_test 12.1 { - CREATE VIRTUAL TABLE t2 USING fts5(x,y); -} {} - -do_catchsql_test 12.2 { - SELECT t2 FROM t2 WHERE t2 MATCH '*stuff' -} {1 {unknown special query: stuff}} - -do_test 12.3 { - set res [db one { SELECT t2 FROM t2 WHERE t2 MATCH '* reads ' }] - string is integer $res -} {1} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 13.1 { - CREATE VIRTUAL TABLE t1 USING fts5(x); - INSERT INTO t1(rowid, x) VALUES(1, 'o n e'), (2, 't w o'); -} {} - -do_execsql_test 13.2 { - SELECT rowid FROM t1 WHERE t1 MATCH 'o'; -} {2 1} - -do_execsql_test 13.4 { - DELETE FROM t1 WHERE rowid=2; -} {} - -do_execsql_test 13.5 { - SELECT rowid FROM t1 WHERE t1 MATCH 'o'; -} {1} - -finish_test - - DELETED ext/fts5/test/fts5ab.test Index: ext/fts5/test/fts5ab.test ================================================================== --- ext/fts5/test/fts5ab.test +++ /dev/null @@ -1,153 +0,0 @@ -# 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 fts5ab - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b); - INSERT INTO t1 VALUES('hello', 'world'); - INSERT INTO t1 VALUES('one two', 'three four'); - INSERT INTO t1(rowid, a, b) VALUES(45, 'forty', 'five'); -} - -do_execsql_test 1.1 { - SELECT * FROM t1; -} { forty five {one two} {three four} hello world } - -do_execsql_test 1.2 { - SELECT rowid FROM t1; -} {45 2 1} - -do_execsql_test 1.3 { - SELECT rowid FROM t1 ORDER BY rowid ASC; -} {1 2 45} - -do_execsql_test 1.4 { - SELECT * FROM t1 WHERE rowid=2; -} {{one two} {three four}} - -do_execsql_test 1.5 { - SELECT * FROM t1 WHERE rowid=2.01; -} {} - -do_execsql_test 1.6 { - SELECT * FROM t1 WHERE rowid=1.99; -} {} - -#------------------------------------------------------------------------- - -reset_db -do_execsql_test 2.1 { - CREATE VIRTUAL TABLE t1 USING fts5(x); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); - INSERT INTO t1 VALUES('one'); - INSERT INTO t1 VALUES('two'); - INSERT INTO t1 VALUES('three'); -} - -do_catchsql_test 2.2 { - SELECT rowid, * FROM t1 WHERE t1 MATCH 'AND AND' -} {1 {fts5: syntax error near "AND"}} - -do_execsql_test 2.3 { SELECT rowid, * FROM t1 WHERE t1 MATCH 'two' } {2 two} -do_execsql_test 2.4 { SELECT rowid, * FROM t1 WHERE t1 MATCH 'three' } {3 three} -do_execsql_test 2.5 { SELECT rowid, * FROM t1 WHERE t1 MATCH 'one' } {1 one} - -do_execsql_test 2.6 { - INSERT INTO t1 VALUES('a b c d e f g'); - INSERT INTO t1 VALUES('b d e a a a i'); - INSERT INTO t1 VALUES('x y z b c c c'); -} - -foreach {tn expr res} { - 1 a {5 4} - 2 b {6 5 4} - 3 c {6 4} - 4 d {5 4} - 5 e {5 4} - 6 f {4} - 7 g {4} - 8 x {6} - 9 y {6} - 10 z {6} -} { - do_execsql_test 2.7.$tn { SELECT rowid FROM t1 WHERE t1 MATCH $expr } $res -} - -#------------------------------------------------------------------------- -# -reset_db -do_execsql_test 3.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a,b); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} - -foreach {tn a b} { - 1 {abashed abandons abase abash abaft} {abases abased} - 2 {abasing abases abaft abated abandons} {abases abandoned} - 3 {abatement abash abash abated abase} {abasements abashing} - 4 {abaft abasements abase abasement abasing} {abasement abases} - 5 {abaft abashing abatement abash abasements} {abandons abandoning} - 6 {aback abate abasements abashes abandoned} {abasement abased} - 7 {abandons abated abased aback abandoning} {abases abandoned} - 8 {abashing abases abasement abaft abashing} {abashed abate} - 9 {abash abase abate abashing abashed} {abandon abandoned} - 10 {abate abandoning abandons abasement aback} {abandon abandoning} -} { - do_execsql_test 3.1.$tn.1 { INSERT INTO t1 VALUES($a, $b) } - do_execsql_test 3.1.$tn.2 { INSERT INTO t1(t1) VALUES('integrity-check') } -} - -foreach {tn expr res} { - 1 {abash} {9 5 3 1} - 2 {abase} {9 4 3 1} - 3 {abase + abash} {1} - 4 {abash + abase} {9} - 5 {abaft + abashing} {8 5} - 6 {abandon + abandoning} {10} - 7 {"abashing abases abasement abaft abashing"} {8} -} { - do_execsql_test 3.2.$tn { - SELECT rowid FROM t1 WHERE t1 MATCH $expr - } $res -} - -do_execsql_test 3.3 { - SELECT rowid FROM t1 WHERE t1 MATCH 'NEAR(aback abate, 2)' -} {6} - -foreach {tn expr res} { - 1 {abash} {1 3 5 9} - 2 {abase} {1 3 4 9} - 3 {abase + abash} {1} - 4 {abash + abase} {9} - 5 {abaft + abashing} {5 8} - 6 {abandon + abandoning} {10} - 7 {"abashing abases abasement abaft abashing"} {8} -} { - do_execsql_test 3.4.$tn { - SELECT rowid FROM t1 WHERE t1 MATCH $expr ORDER BY rowid ASC - } $res -} - - -finish_test DELETED ext/fts5/test/fts5ac.test Index: ext/fts5/test/fts5ac.test ================================================================== --- ext/fts5/test/fts5ac.test +++ /dev/null @@ -1,435 +0,0 @@ -# 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 fts5ac - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE xx USING fts5(x,y); - INSERT INTO xx(xx, rank) VALUES('pgsz', 32); -} - -set data { - 0 {p o q e z k z p n f y u z y n y} {l o o l v v k} - 1 {p k h h p y l l h i p v n} {p p l u r i f a j g e r r x w} - 2 {l s z j k i m p s} {l w e j t j e e i t w r o p o} - 3 {x g y m y m h p} {k j j b r e y y a k y} - 4 {q m a i y i z} {o w a g k x g j m w e u k} - 5 {k o a w y b s z} {s g l m m l m g p} - 6 {d a q i z h b l c p k j g k} {p x u j x t v c z} - 7 {f d a g o c t i} {w f c x l d r k i j} - 8 {y g w u b q p o m j y b p a e k} {r i d k y w o z q m a t p} - 9 {r k o m c c j s x m x m x m q r} {y r c a q d z k n x n} - 10 {k j q m g q a j d} {d d e z g w h c d o o g x d} - 11 {j z u m o y q j f w e e w t r j w} {g m o r x n t n w i f g l z f} - 12 {s y w a w d o h x m k} {c w k z b p o r a} - 13 {u t h x e g s k n g i} {f j w g c s r} - 14 {b f i c s u z t k} {c k q s j u i z o} - 15 {n a f n u s w h y n s i q e w} {x g e g a s s h n} - 16 {k s q e j n p} {t r j f t o e k k l m i} - 17 {g d t u w r o p m n m n p h b o u} {h s w o s l j e} - 18 {f l q y q q g e e x j r} {n b r r g e i r t x q k} - 19 {f i r g o a w e p i l o a w} {e k r z t d g h g i b d i e m} - 20 {l d u u f p y} {g o m m u x m g l j t t x x u} - 21 {m c d k x i c z l} {m i a i e u h} - 22 {w b f o c g x y j} {z d w x d f h i p} - 23 {w u i u x t c h k i b} {b y k h b v r t g j} - 24 {h f d j s w s b a p k} {a q y u z e y m m j q r} - 25 {d i x y x x k i y f s d j h z p n} {l l q m e t c w g y h t s v g} - 26 {g s q w t d k x g f m j p k y} {r m b x e l t d} - 27 {j l s q u g y v e c l o} {m f l m m m h g x x l n c} - 28 {c t j g v r s b z j} {l c f y d t q n} - 29 {e x z y w i h l} {b n b x e y q e n u m} - 30 {g y y h j b w r} {q b q f u s k c k g r} - 31 {g u l x l b r c m z b u c} {k g t b x k x n t e z d h o} - 32 {w g v l z f b z h p s c v h} {g e w v m h k r g w a r f q} - 33 {c g n f u d o y o b} {e y o h x x y y i z s b h a j} - 34 {v y h c q u u s q y x x k s q} {d n r m y k n t i r n w e} - 35 {o u c x l e b t a} {y b a x y f z x r} - 36 {x p h l j a a u u j h} {x o f s z m b c q p} - 37 {k q t i c a q n m v v} {v r z e f m y o} - 38 {r w t t t t r v v o e p g h} {l w x a g a u h y} - 39 {o p v g v b a g o} {j t q c r b b g y z} - 40 {f s o r o d t h q f x l} {r d b m k i f s t d l m y x j w} - 41 {t m o t m f m f} {i p i q j v n v m b q} - 42 {t x w a r l w d t b c o d o} {a h f h w z d n s} - 43 {t u q c d g p q x j o l c x c} {m n t o z z j a y} - 44 {v d i i k b f s z r v r z y} {g n q y s x x m b x c l w} - 45 {p v v a c s z y e o l} {m v t u d k m k q b d c v z r} - 46 {f y k l d r q w r s t r e} {h m v r r l r r t f q e x y} - 47 {w l n l t y x} {n h s l a f c h u f l x x m v n o} - 48 {t n v i k e b p z p d j j l i o} {i v z p g u e j s i k n h w d c} - 49 {z v x p n l t a j c} {e j l e n c e t a d} - 50 {w u b x u i v h a i y m m r p m s} {s r h d o g z y f f x e} - 51 {d c c x b c a x g} {p r a j v u y} - 52 {f w g r c o d l t u e z h i} {j l l s s b j m} - 53 {p m t f k i x} {u v y a z g w v v m x h i} - 54 {l c z g l o j i c d e b} {b f v y w u i b e i y} - 55 {r h c x f x a d s} {z x y k f l r b q c v} - 56 {v x x c y h z x b g m o q n c} {h n b i t g h a q b c o r u} - 57 {d g l o h t b s b r} {n u e p t i m u} - 58 {t d y e t d c w u o s w x f c h} {i o s v y b r d r} - 59 {l b a p q n d r} {k d c c d n y q h g a o p e x} - 60 {f r z v m p k r} {x x r i s b a g f c} - 61 {s a z i e r f i w c n y v z t k s} {y y i r y n l s b w i e k n} - 62 {n x p r e x q r m v i b y} {f o o z n b s r q j} - 63 {y j s u j x o n r q t f} {f v k n v x u s o a d e f e} - 64 {u s i l y c x q} {r k c h p c h b o s s u s p b} - 65 {m p i o s h o} {s w h u n d m n q t y k b w c} - 66 {l d f g m x x x o} {s w d d f b y j j h h t i y p j o} - 67 {c b m h f n v w n h} {i r w i e x r w l z p x u g u l s} - 68 {y a h u h i m a y q} {d d r x h e v q n z y c j} - 69 {c x f d x o n p o b r t b l p l} {m i t k b x v f p t m l l y r o} - 70 {u t l w w m s} {m f m o l t k o p e} - 71 {f g q e l n d m z x q} {z s i i i m f w w f n g p e q} - 72 {n l h a v u o d f j d e x} {v v s l f g d g r a j x i f z x} - 73 {x v m v f i g q e w} {r y s j i k m j j e d g r n o i f} - 74 {g d y n o h p s y q z j d w n h w} {x o d l t j i b r d o r y} - 75 {p g b i u r b e q d v o a g w m k} {q y z s f q o h} - 76 {u z a q u f i f f b} {b s p b a a d x r r i q f} - 77 {w h h z t h p o a h h e e} {h w r p h k z v y f r x} - 78 {c a r k i a p u x} {f w l p t e m l} - 79 {q q u k o t r k z} {f b m c w p s s o z} - 80 {t i g v y q s r x m r x z e f} {x o j w a u e y s j c b u p p r o} - 81 {n j n h r l a r e o z w e} {v o r r j a v b} - 82 {i f i d k w d n h} {o i d z i z l m w s b q v u} - 83 {m d g q q b k b w f q q p p} {j m q f b y c i z k y q p l e a} - 84 {m x o n y f g} {y c n x n q j i y c l h b r q z} - 85 {v o z l n p c} {g n j n t b b x n c l d a g j v} - 86 {z n a y f b t k k t d b z a v} {r p c n r u k u} - 87 {b q t x z e c w} {q a o a l o a h i m j r} - 88 {j f h o x x a z g b a f a m i b} {j z c z y x e x w t} - 89 {t c t p r s u c q n} {z x l i k n f q l n t} - 90 {w t d q j g m r f k n} {l e w f w w a l y q k i q t p c t} - 91 {c b o k l i c b s j n m b l} {y f p q o w g} - 92 {f y d j o q t c c q m f j s t} {f h e d y m o k} - 93 {k x j r m a d o i z j} {r t t t f e b r x i v j v g o} - 94 {s f e a e t i h h d q p z t q} {b k m k w h c} - 95 {h b n j t k i h o q u} {w n g i t o k c a m y p f l x c p} - 96 {f c x p y r b m o l m o a} {p c a q s u n n x d c f a o} - 97 {u h h k m n k} {u b v n u a o c} - 98 {s p e t c z d f n w f} {l s f j b l c e s h} - 99 {r c v w i v h a t a c v c r e} {h h u m g o f b a e o} -} - -do_test 1.1 { - foreach {id x y} $data { - execsql { INSERT INTO xx(rowid, x, y) VALUES($id, $x, $y) } - } -} {} - -# Usage: -# -# poslist aCol ?-pc VARNAME? ?-near N? ?-col C? -- phrase1 phrase2... -# -proc poslist {aCol args} { - set O(-near) 10 - set O(-col) -1 - set O(-pc) "" - - set nOpt [lsearch -exact $args --] - if {$nOpt<0} { error "no -- option" } - - foreach {k v} [lrange $args 0 [expr $nOpt-1]] { - if {[info exists O($k)]==0} { error "unrecognized option $k" } - set O($k) $v - } - - if {$O(-pc) == ""} { - set counter 0 - } else { - upvar $O(-pc) counter - } - - # Set $phraselist to be a list of phrases. $nPhrase its length. - set phraselist [lrange $args [expr $nOpt+1] end] - set nPhrase [llength $phraselist] - - for {set j 0} {$j < [llength $aCol]} {incr j} { - for {set i 0} {$i < $nPhrase} {incr i} { - set A($j,$i) [list] - } - } - - set iCol -1 - foreach col $aCol { - incr iCol - if {$O(-col)>=0 && $O(-col)!=$iCol} continue - - set nToken [llength $col] - - set iFL [expr $O(-near) >= $nToken ? $nToken - 1 : $O(-near)] - for { } {$iFL < $nToken} {incr iFL} { - for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} { - set B($iPhrase) [list] - } - - for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} { - set p [lindex $phraselist $iPhrase] - set nPm1 [expr {[llength $p] - 1}] - set iFirst [expr $iFL - $O(-near) - [llength $p]] - - for {set i $iFirst} {$i <= $iFL} {incr i} { - if {[lrange $col $i [expr $i+$nPm1]] == $p} { lappend B($iPhrase) $i } - } - if {[llength $B($iPhrase)] == 0} break - } - - if {$iPhrase==$nPhrase} { - for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} { - set A($iCol,$iPhrase) [concat $A($iCol,$iPhrase) $B($iPhrase)] - set A($iCol,$iPhrase) [lsort -integer -uniq $A($iCol,$iPhrase)] - } - } - } - } - - set res [list] -#puts [array names A] - - for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} { - for {set iCol 0} {$iCol < [llength $aCol]} {incr iCol} { - foreach a $A($iCol,$iPhrase) { - lappend res "$counter.$iCol.$a" - } - } - incr counter - } - - #puts $res - return $res -} - -# Usage: -# -# nearset aCol ?-near N? ?-col C? -- phrase1 phrase2... -# -proc nearset {args} { - set plist [poslist {*}$args] - return [expr [llength [lindex $plist 0]]>0] -} - -proc instcompare {lhs rhs} { - foreach {p1 c1 o1} [split $lhs .] {} - foreach {p2 c2 o2} [split $rhs .] {} - - set res [expr $c1 - $c2] - if {$res==0} { set res [expr $o1 - $o2] } - if {$res==0} { set res [expr $p1 - $p2] } - - return $res -} - -# Argument $expr is an FTS5 match expression designed to be executed against -# an FTS5 table with the following schema: -# -# CREATE VIRTUAL TABLE xy USING fts5(x, y); -# -# Assuming the table contains the same records as stored int the global -# $::data array (see above), this function returns a list containing one -# element for each match in the dataset. The elements are themselves lists -# formatted as follows: -# -# { ...} -# -# where each element is a list of phrase matches in the -# same form as returned by auxiliary scalar function fts5_test(). -# -proc matchdata {bPos expr {bAsc 0}} { - - set tclexpr [db one {SELECT fts5_expr_tcl($expr, 'nearset $cols', 'x', 'y')}] - set res [list] - - #puts $tclexpr - foreach {id x y} $::data { - set cols [list $x $y] - if $tclexpr { - if {$bPos} { - set N [regexp -all -inline {\[nearset [^\]]*\]} $tclexpr] - set rowres [list] - set cnt 0 - foreach phrase $N { - set arglist [string range $phrase 9 end-1] - set cmd "poslist [lindex $arglist 0] -pc cnt [lrange $arglist 1 end]" - set pos [eval $cmd] - set rowres [concat $rowres $pos] - } - set rowres [lsort -command instcompare $rowres] - lappend res [list $id $rowres] - } else { - lappend res $id - } - } - } - - if {$bAsc} { - set res [lsort -integer -increasing -index 0 $res] - } else { - set res [lsort -integer -decreasing -index 0 $res] - } - - return [concat {*}$res] -} - -# -# End of test code -#------------------------------------------------------------------------- - -proc fts5_test_poslist {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xInstCount]} {incr i} { - lappend res [string map {{ } .} [$cmd xInst $i]] - } - set res -} - -sqlite3_fts5_create_function db fts5_test_poslist fts5_test_poslist - -#------------------------------------------------------------------------- -# Test phrase queries. -# -foreach {tn phrase} { - 1 "o" - 2 "b q" - 3 "e a e" - 4 "m d g q q b k b w f q q p p" - 5 "l o o l v v k" - 6 "a" - 7 "b" - 8 "c" - 9 "no" - 10 "L O O L V V K" -} { - set expr "\"$phrase\"" - set res [matchdata 1 $expr] - - do_execsql_test 1.2.$tn.[llength $res] { - SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr - } $res -} - -#------------------------------------------------------------------------- -# Test some AND and OR queries. -# -foreach {tn expr} { - 1.1 "a AND b" - 1.2 "a+b AND c" - 1.3 "d+c AND u" - 1.4 "d+c AND u+d" - - 2.1 "a OR b" - 2.2 "a+b OR c" - 2.3 "d+c OR u" - 2.4 "d+c OR u+d" - - 3.1 { a AND b AND c } -} { - set res [matchdata 1 $expr] - do_execsql_test 2.$tn.[llength $res] { - SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr - } $res -} - -#------------------------------------------------------------------------- -# Queries on a specific column. -# -foreach {tn expr} { - 1 "x:a" - 2 "y:a" - 3 "x:b" - 4 "y:b" -} { - set res [matchdata 1 $expr] - do_execsql_test 3.$tn.[llength $res] { - SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr - } $res -} - -#------------------------------------------------------------------------- -# Some NEAR queries. -# -foreach {tn expr} { - 1 "NEAR(a b)" - 2 "NEAR(r c)" - 2 { NEAR(r c, 5) } - 3 { NEAR(r c, 3) } - 4 { NEAR(r c, 2) } - 5 { NEAR(r c, 0) } - 6 { NEAR(a b c) } - 7 { NEAR(a b c, 8) } - 8 { x : NEAR(r c) } - 9 { y : NEAR(r c) } -} { - set res [matchdata 1 $expr] - do_execsql_test 4.1.$tn.[llength $res] { - SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr - } $res -} - -do_test 4.1 { poslist {{a b c}} -- a } {0.0.0} -do_test 4.2 { poslist {{a b c}} -- c } {0.0.2} - -foreach {tn expr tclexpr} { - 1 {a b} {[N $x -- {a}] && [N $x -- {b}]} -} { - do_execsql_test 5.$tn {SELECT fts5_expr_tcl($expr, 'N $x')} [list $tclexpr] -} - -#------------------------------------------------------------------------- -# -do_execsql_test 6.integrity { - INSERT INTO xx(xx) VALUES('integrity-check'); -} -#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM xx_data} {puts $r} -foreach {bAsc sql} { - 0 {SELECT rowid FROM xx WHERE xx MATCH $expr} - 1 {SELECT rowid FROM xx WHERE xx MATCH $expr ORDER BY rowid ASC} -} { - foreach {tn expr} { - 0.1 x - 1 { NEAR(r c) } - 2 { NEAR(r c, 5) } - 3 { NEAR(r c, 3) } - 4 { NEAR(r c, 2) } - 5 { NEAR(r c, 0) } - 6 { NEAR(a b c) } - 7 { NEAR(a b c, 8) } - 8 { x : NEAR(r c) } - 9 { y : NEAR(r c) } - 10 { x : "r c" } - 11 { y : "r c" } - 12 { a AND b } - 13 { a AND b AND c } - 14a { a } - 14b { a OR b } - 15 { a OR b AND c } - 16 { c AND b OR a } - 17 { c AND (b OR a) } - 18 { c NOT (b OR a) } - 19 { c NOT b OR a AND d } - } { - set res [matchdata 0 $expr $bAsc] - do_execsql_test 6.$bAsc.$tn.[llength $res] $sql $res - } -} - -finish_test - DELETED ext/fts5/test/fts5ad.test Index: ext/fts5/test/fts5ad.test ================================================================== --- ext/fts5/test/fts5ad.test +++ /dev/null @@ -1,220 +0,0 @@ -# 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 fts5ad - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE yy USING fts5(x, y); - INSERT INTO yy VALUES('Changes the result to be', 'the list of all matching'); - INSERT INTO yy VALUES('indices (or all matching', 'values if -inline is'); - INSERT INTO yy VALUES('specified as well.) If', 'indices are returned, the'); -} {} - -foreach {tn match res} { - 1 {c*} {1} - 2 {i*} {3 2} - 3 {t*} {3 1} - 4 {r*} {3 1} -} { - do_execsql_test 1.$tn { - SELECT rowid FROM yy WHERE yy MATCH $match - } $res -} - -foreach {tn match res} { - 5 {c*} {1} - 6 {i*} {2 3} - 7 {t*} {1 3} - 8 {r*} {1 3} -} { - do_execsql_test 1.$tn { - SELECT rowid FROM yy WHERE yy MATCH $match ORDER BY rowid ASC - } $res -} - -foreach {T create} { - 2 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); - } - - 3 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix=1,2,3,4,5); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); - } - -} { - - do_test $T.1 { - execsql { DROP TABLE IF EXISTS t1 } - execsql $create - } {} - - do_test $T.1 { - foreach {rowid a b} { - 0 {fghij uvwxyz klmn pq uvwx} {klmn f fgh uv fghij klmno} - 1 {uv f abcd abcd fghi} {pq klm uv uv fgh uv a} - 2 {klmn klm pqrs fghij uv} {f k uvw ab abcd pqr uv} - 3 {ab pqrst a fghi ab pqr fg} {k klmno a fg abcd} - 4 {abcd pqrst uvwx a fgh} {f klmno fghij kl pqrst} - 5 {uvwxyz k abcde u a} {uv k k kl klmn} - 6 {uvwxyz k klmn pqrst uv} {fghi pqrs abcde u k} - 7 {uvwxy klmn u p pqrst fgh} {p f fghi abcd uvw kl uv} - 8 {f klmno pqrst uvwxy pqrst} {uv abcde klm pq pqr} - 9 {f abcde a uvwxyz pqrst} {fghij abc k uvwx pqr fghij uvwxy} - 10 {ab uv f fg pqrst uvwxy} {fgh p uv k abc klm uvw} - 11 {pq klmno a uvw abcde uvwxyz} {fghij pq uvwxyz pqr fghi} - 12 {fgh u pq fgh uvw} {uvw pqr f uvwxy uvwx} - 13 {uvwx klmn f fgh abcd pqr} {uvw k fg uv klm abcd} - 14 {ab uvwx pqrst pqr uvwxyz pqrs} {uvwxyz abcde ab ab uvw abcde} - 15 {abc abcde uvwxyz abc kl k pqr} {klm k k klmno u fgh} - 16 {fghi abcd fghij uv uvwxyz ab uv} {klmn pqr a uvw fghi} - 17 {abc pqrst fghi uvwx uvw klmn fghi} {ab fg pqr pqrs p} - 18 {pqr kl a fghij fgh fg kl} {pqr uvwxyz uvw abcd uvwxyz} - 19 {fghi fghi pqr kl fghi f} {klmn u u klmno klmno} - 20 {abc pqrst klmno kl pq uvwxy} {abc k fghi pqrs klm} - 21 {a pqr uvwxyz uv fghi a fgh} {abc pqrs pqrst pq klm} - 22 {klm abc uvwxyz klm pqrst} {fghij k pq pqr u klm fghij} - 23 {p klm uv p a a} {uvwxy klmn uvw abcde pq} - 24 {uv fgh fg pq uvwxy u uvwxy} {pqrs a uvw p uvwx uvwxyz fg} - 25 {fghij fghi klmn abcd pq kl} {fghi abcde pqrs abcd fgh uvwxy} - 26 {pq fgh a abc klmno klmn} {fgh p k p fg fghij} - 27 {fg pq kl uvwx fghij pqrst klmn} {abcd uvw abcd fghij f fghij} - 28 {uvw fghi p fghij pq fgh uvwx} {k fghij abcd uvwx pqr fghi} - 29 {klm pq abcd pq f uvwxy} {pqrst p fghij pqr p} - 30 {ab uvwx fg uvwx klmn klm} {klmn klmno fghij klmn klm} - 31 {pq k pqr abcd a pqrs} {abcd abcd uvw a abcd klmno ab} - 32 {pqrst u abc pq klm} {abc kl uvwxyz fghij u fghi p} - 33 {f uvwxy u k f uvw uvwx} {pqrs uvw fghi fg pqrst klm} - 34 {pqrs pq fghij uvwxyz pqr} {ab abc abc uvw f pq f} - 35 {uvwxy ab uvwxy klmno kl pqrs} {abcde uvw pqrs uvwx k k} - 36 {uvwxyz k ab abcde abc uvw} {uvw abcde uvw klmn uv klmn} - 37 {k kl uv abcde uvwx fg u} {u abc uvwxy k fg abcd} - 38 {fghi pqrst fghi pqr pqrst uvwx} {u uv uvwx fghi abcde} - 39 {k pqrst k uvw fg pqrst fghij} {uvwxy ab kl klmn uvwxyz abcde} - 40 {fg uvwxy pqrs klmn uvwxyz klm p} {k uv ab fghij fgh k pqrs} - 41 {uvwx abc f pq uvwxy k} {ab uvwxyz abc f fghij} - 42 {uvwxy klmno uvwxyz uvwxyz pqrst} {uv kl kl klmno k f abcde} - 43 {abcde ab pqrs fg f fgh} {abc fghij fghi k k} - 44 {uvw abcd a ab pqrst klmn fg} {pqrst u uvwx pqrst fghij f pqrst} - 45 {uvwxy p kl uvwxyz ab pqrst fghi} {abc f pqr fg a k} - 46 {u p f a fgh} {a kl pq uv f} - 47 {pqrs abc fghij fg abcde ab a} {p ab uv pqrs kl fghi abcd} - 48 {abcde uvwxy pqrst uv abc pqr uvwx} {uvwxy klm uvwxy uvwx k} - 49 {fgh klm abcde klmno u} {a f fghij f uvwxyz abc u} - 50 {uv uvw uvwxyz uvwxyz uv ab} {uvwx pq fg u k uvwxy} - 51 {uvwxy pq p kl fghi} {pqrs fghi pqrs abcde uvwxyz ab} - 52 {pqr p uvwxy kl pqrs klmno fghij} {ab abcde abc pqrst pqrs uv} - 53 {fgh pqrst p a klmno} {ab ab pqrst pqr kl pqrst} - 54 {abcd klm ab uvw a fg u} {f pqr f abcd uv} - 55 {u fg uvwxyz k uvw} {abc pqrs f fghij fg pqrs uvwxy} - 56 {klm fg p fghi fg a} {uv a fghi uvwxyz a fghi} - 57 {uvwxy k abcde fgh f fghi} {f kl klmn f fghi klm} - 58 {klm k fgh uvw fgh fghi} {klmno uvwx u pqrst u} - 59 {fghi pqr pqrst p uvw fghij} {uv pqrst pqrs pq fghij klm} - 60 {uvwx klm uvwxy uv klmn} {p a a abc klmn ab k} - 61 {uvwxy uvwx klm uvwx klm} {pqrs ab ab uvwxyz fg} - 62 {kl uv uv uvw fg kl k} {abcde uvw fgh uvwxy klm} - 63 {a abc fgh u klm abcd} {fgh pqr uv klmn fghij} - 64 {klmn k klmn klmno pqrs pqr} {fg kl abcde klmno uvwxy kl pq} - 65 {uvwxyz klm fghi abc abcde kl} {uvwxy uvw uvwxyz uvwxyz pq pqrst} - 66 {pq klm abc pqrst fgh f} {u abcde pqrst abcde fg} - 67 {u pqrst kl u uvw klmno} {u pqr pqrs fgh u p} - 68 {abc fghi uvwxy fgh k pq} {uv p uvwx uvwxyz ab} - 69 {klmno f uvwxyz uvwxy klmn fg ab} {fgh kl a pqr abcd pqr} - 70 {fghi pqrst pqrst uv a} {uvwxy k p uvw uvwx a} - 71 {a fghij f p uvw} {klm fg abcd abcde klmno pqrs} - 72 {uv uvwx uvwx uvw klm} {uv fghi klmno uvwxy uvw} - 73 {kl uvwxy ab f pq klm u} {uvwxy klmn klm abcd pq fg k} - 74 {uvw pqrst abcd uvwxyz ab} {fgh fgh klmn abc pq} - 75 {uvwxyz klm pq abcd klmno pqr uvwxyz} {kl f a fg pqr klmn} - 76 {uvw uvwxy pqr k pqrst kl} {uvwxy abc uvw uvw u} - 77 {fgh klm u uvwxyz f uvwxy abcde} {uv abcde klmno u u ab} - 78 {klmno abc pq pqr fgh} {p uv abcd fgh abc u k} - 79 {fg pqr uvw pq uvwx} {uv uvw fghij pqrs fg p} - 80 {abcd pqrs uvwx uvwxy uvwx} {u uvw pqrst pqr abcde pqrs kl} - 81 {uvwxyz klm pq uvwxy fghij} {p pq klm fghij u a a} - 82 {uvwx k uvwxyz klmno pqrst kl} {abcde p f pqrst abcd uvwxyz p} - 83 {abcd abcde klm pqrst uvwxyz} {uvw pqrst u p uvwxyz a pqrs} - 84 {k klm abc uv uvwxy klm klmn} {k abc pqr a abc p kl} - 85 {klmn abcd pqrs p pq klm a} {klmn kl ab uvw pq} - 86 {klmn a pqrs abc uvw pqrst} {a pqr kl klm a k f} - 87 {pqrs ab uvwx uvwxy a pqr f} {fg klm uvwx pqr pqr} - 88 {klmno ab k kl u uvwxyz} {uv kl uvw fghi uv uvw} - 89 {pq fghi pqrst klmn uvwxy abc pqrs} {fg f f fg abc abcde klm} - 90 {kl a k fghi uvwx fghi u} {ab uvw pqr fg a p abc} - 91 {uvwx pqrs klmno ab fgh uvwx} {pqr uvwx abc kl f klmno kl} - 92 {fghij pq pqrs fghij f pqrst} {u abcde fg pq pqr fgh k} - 93 {fgh u pqrs abcde klmno abc} {abc fg pqrst pqr abcde} - 94 {uvwx p abc f pqr p} {k pqrs kl klm abc fghi klm} - 95 {kl p klmno uvwxyz klmn} {fghi ab a fghi pqrs kl} - 96 {pqr fgh pq uvwx a} {uvw klm klmno fg uvwxy uvwx} - 97 {fg abc uvwxyz fghi pqrst pq} {abc k a ab abcde f} - 98 {uvwxy fghi uvwxy u abcde abcde uvw} {klmn uvwx pqrs uvw uvwxy abcde} - 99 {pq fg fghi uvwx uvwx fghij uvwxy} {klmn klmn f abc fg a} - } { - execsql { - INSERT INTO t1(rowid, a, b) VALUES($rowid, $a, $b); - } - } - } {} - - proc prefix_query {prefixlist} { - set ret [list] - db eval {SELECT rowid, a, b FROM t1 ORDER BY rowid DESC} { - set bMatch 1 - foreach pref $prefixlist { - if { [lsearch -glob $a $pref]<0 && [lsearch -glob $b $pref]<0 } { - set bMatch 0 - break - } - } - if {$bMatch} { lappend ret $rowid } - } - return $ret - } - - foreach {bAsc sql} { - 0 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix} - 1 {SELECT rowid FROM t1 WHERE t1 MATCH $prefix ORDER BY rowid ASC} - } { - foreach {tn prefix} { - 1 {a*} 2 {ab*} 3 {abc*} 4 {abcd*} 5 {abcde*} - 6 {f*} 7 {fg*} 8 {fgh*} 9 {fghi*} 10 {fghij*} - 11 {k*} 12 {kl*} 13 {klm*} 14 {klmn*} 15 {klmno*} - 16 {p*} 17 {pq*} 18 {pqr*} 19 {pqrs*} 20 {pqrst*} - 21 {u*} 22 {uv*} 23 {uvw*} 24 {uvwx*} 25 {uvwxy*} 26 {uvwxyz*} - 27 {x*} - 28 {a f*} 29 {a* f*} 30 {a* fghij*} - } { - set res [prefix_query $prefix] - if {$bAsc} { - set res [lsort -integer -increasing $res] - } - set n [llength $res] - do_execsql_test $T.$bAsc.$tn.$n $sql $res - } - } -} - -finish_test - DELETED ext/fts5/test/fts5ae.test Index: ext/fts5/test/fts5ae.test ================================================================== --- ext/fts5/test/fts5ae.test +++ /dev/null @@ -1,281 +0,0 @@ -# 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 fts5ae - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b); - INSERT INTO t1(t1, rank) VALUES('pgsz', 32); -} - -do_execsql_test 1.1 { - INSERT INTO t1 VALUES('hello', 'world'); - SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC; -} {1} - -do_execsql_test 1.2 { - INSERT INTO t1 VALUES('world', 'hello'); - SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC; -} {1 2} - -do_execsql_test 1.3 { - INSERT INTO t1 VALUES('world', 'world'); - SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC; -} {1 2} - -do_execsql_test 1.4.1 { - INSERT INTO t1 VALUES('hello', 'hello'); -} - -do_execsql_test 1.4.2 { - SELECT rowid FROM t1 WHERE t1 MATCH 'hello' ORDER BY rowid ASC; -} {1 2 4} - -fts5_aux_test_functions db - -#------------------------------------------------------------------------- -# -do_execsql_test 2.0 { - CREATE VIRTUAL TABLE t2 USING fts5(x, y); - INSERT INTO t2 VALUES('u t l w w m s', 'm f m o l t k o p e'); - INSERT INTO t2 VALUES('f g q e l n d m z x q', 'z s i i i m f w w f n g p'); -} - -do_execsql_test 2.1 { - SELECT rowid, fts5_test_poslist(t2) FROM t2 - WHERE t2 MATCH 'm' ORDER BY rowid; -} { - 1 {0.0.5 0.1.0 0.1.2} - 2 {0.0.7 0.1.5} -} - -do_execsql_test 2.2 { - SELECT rowid, fts5_test_poslist(t2) FROM t2 - WHERE t2 MATCH 'u OR q' ORDER BY rowid; -} { - 1 {0.0.0} - 2 {1.0.2 1.0.10} -} - -do_execsql_test 2.3 { - SELECT rowid, fts5_test_poslist(t2) FROM t2 - WHERE t2 MATCH 'y:o' ORDER BY rowid; -} { - 1 {0.1.3 0.1.7} -} - -#------------------------------------------------------------------------- -# -do_execsql_test 3.0 { - CREATE VIRTUAL TABLE t3 USING fts5(x, y); - INSERT INTO t3 VALUES( 'j f h o x x a z g b a f a m i b', 'j z c z y x w t'); - INSERT INTO t3 VALUES( 'r c', ''); -} - -do_execsql_test 3.1 { - SELECT rowid, fts5_test_poslist(t3) FROM t3 WHERE t3 MATCH 'NEAR(a b)'; -} { - 1 {0.0.6 1.0.9 0.0.10 0.0.12 1.0.15} -} - -do_execsql_test 3.2 { - SELECT rowid, fts5_test_poslist(t3) FROM t3 WHERE t3 MATCH 'NEAR(r c)'; -} { - 2 {0.0.0 1.0.1} -} - -do_execsql_test 3.3 { - INSERT INTO t3 - VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o'); - SELECT rowid, fts5_test_poslist(t3) - FROM t3 WHERE t3 MATCH 'a OR b AND c'; -} { - 3 0.0.5 - 1 {0.0.6 1.0.9 0.0.10 0.0.12 1.0.15 2.1.2} -} - -#------------------------------------------------------------------------- -# -do_execsql_test 4.0 { - CREATE VIRTUAL TABLE t4 USING fts5(x, y); - INSERT INTO t4 - VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o'); -} - -do_execsql_test 4.1 { - SELECT rowid, fts5_test_poslist(t4) FROM t4 WHERE t4 MATCH 'a OR b AND c'; -} { - 1 0.0.5 -} - -#------------------------------------------------------------------------- -# Test that the xColumnSize() and xColumnAvgsize() APIs work. -# -reset_db -fts5_aux_test_functions db - -do_execsql_test 5.1 { - CREATE VIRTUAL TABLE t5 USING fts5(x, y); - INSERT INTO t5 VALUES('a b c d', 'e f g h i j'); - INSERT INTO t5 VALUES('', 'a'); - INSERT INTO t5 VALUES('a', ''); -} -do_execsql_test 5.2 { - SELECT rowid, fts5_test_columnsize(t5) FROM t5 WHERE t5 MATCH 'a' - ORDER BY rowid DESC; -} { - 3 {1 0} - 2 {0 1} - 1 {4 6} -} - -do_execsql_test 5.2 { - SELECT rowid, fts5_test_columntext(t5) FROM t5 WHERE t5 MATCH 'a' - ORDER BY rowid DESC; -} { - 3 {a {}} - 2 {{} a} - 1 {{a b c d} {e f g h i j}} -} - -do_execsql_test 5.3 { - SELECT rowid, fts5_test_columntotalsize(t5) FROM t5 WHERE t5 MATCH 'a' - ORDER BY rowid DESC; -} { - 3 {5 7} - 2 {5 7} - 1 {5 7} -} - -do_execsql_test 5.4 { - INSERT INTO t5 VALUES('x y z', 'v w x y z'); - SELECT rowid, fts5_test_columntotalsize(t5) FROM t5 WHERE t5 MATCH 'a' - ORDER BY rowid DESC; -} { - 3 {8 12} - 2 {8 12} - 1 {8 12} -} - -#------------------------------------------------------------------------- -# Test the xTokenize() API -# -reset_db -fts5_aux_test_functions db -do_execsql_test 6.1 { - CREATE VIRTUAL TABLE t6 USING fts5(x, y); - INSERT INTO t6 VALUES('There are more', 'things in heaven and earth'); - INSERT INTO t6 VALUES(', Horatio, Than are', 'dreamt of in your philosophy.'); -} - -do_execsql_test 6.2 { - SELECT rowid, fts5_test_tokenize(t6) FROM t6 WHERE t6 MATCH 't*' -} { - 2 {{horatio than are} {dreamt of in your philosophy}} - 1 {{there are more} {things in heaven and earth}} -} - -#------------------------------------------------------------------------- -# Test the xQueryPhrase() API -# -reset_db -fts5_aux_test_functions db -do_execsql_test 7.1 { - CREATE VIRTUAL TABLE t7 USING fts5(x, y); -} -do_test 7.2 { - foreach {x y} { - {q i b w s a a e l o} {i b z a l f p t e u} - {b a z t a l o x d i} {b p a d b f h d w y} - {z m h n p p u i e g} {v h d v b x j j c z} - {a g i m v a u c b i} {p k s o t l r t b m} - {v v c j o d a s c p} {f f v o k p o f o g} - } { - execsql {INSERT INTO t7 VALUES($x, $y)} - } - execsql { SELECT count(*) FROM t7 } -} {5} - -foreach {tn q res} { - 1 a {{4 2}} - 2 b {{3 4}} - 3 c {{2 1}} - 4 d {{2 2}} - 5 {a AND b} {{4 2} {3 4}} - 6 {a OR b OR c OR d} {{4 2} {3 4} {2 1} {2 2}} -} { - do_execsql_test 7.3.$tn { - SELECT fts5_test_queryphrase(t7) FROM t7 WHERE t7 MATCH $q LIMIT 1 - } [list $res] -} - -do_execsql_test 7.4 { - SELECT fts5_test_rowcount(t7) FROM t7 WHERE t7 MATCH 'a'; -} {5 5 5 5} - -#do_execsql_test 7.4 { -# SELECT rowid, bm25debug(t7) FROM t7 WHERE t7 MATCH 'a'; -#} {5 5 5 5} -# - -#------------------------------------------------------------------------- -# -do_test 8.1 { - execsql { CREATE VIRTUAL TABLE t8 USING fts5(x, y) } - foreach {rowid x y} { - 0 {A o} {o o o C o o o o o o o o} - 1 {o o B} {o o o C C o o o o o o o} - 2 {A o o} {o o o o D D o o o o o o} - 3 {o B} {o o o o o D o o o o o o} - 4 {E o G} {H o o o o o o o o o o o} - 5 {F o G} {I o J o o o o o o o o o} - 6 {E o o} {H o J o o o o o o o o o} - 7 {o o o} {o o o o o o o o o o o o} - 9 {o o o} {o o o o o o o o o o o o} - } { - execsql { INSERT INTO t8(rowid, x, y) VALUES($rowid, $x, $y) } - } -} {} - -foreach {tn q res} { - 1 {a} {0 2} - 2 {b} {3 1} - 3 {c} {1 0} - 4 {d} {2 3} - 5 {g AND (e OR f)} {5 4} - 6 {j AND (h OR i)} {5 6} -} { - do_execsql_test 8.2.$tn.1 { - SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY bm25(t8) DESC; - } $res - - do_execsql_test 8.2.$tn.2 { - SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY +rank DESC; - } $res - - do_execsql_test 8.2.$tn.3 { - SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY rank DESC; - } $res -} - -finish_test - DELETED ext/fts5/test/fts5af.test Index: ext/fts5/test/fts5af.test ================================================================== --- ext/fts5/test/fts5af.test +++ /dev/null @@ -1,144 +0,0 @@ -# 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. -# -# More specifically, the tests in this file focus on the built-in -# snippet() function. -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5af - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x, y); -} - -proc do_snippet_test {tn doc match res} { - - uplevel #0 [list set v1 $doc] - uplevel #0 [list set v2 $match] - - do_execsql_test $tn.1 { - DELETE FROM t1; - INSERT INTO t1 VALUES($v1, NULL); - SELECT snippet(t1, -1, '[', ']', '...', 7) FROM t1 WHERE t1 MATCH $v2; - } [list $res] - - do_execsql_test $tn.2 { - DELETE FROM t1; - INSERT INTO t1 VALUES(NULL, $v1); - SELECT snippet(t1, -1, '[', ']', '...', 7) FROM t1 WHERE t1 MATCH $v2; - } [list $res] - - do_execsql_test $tn.3 { - DELETE FROM t1; - INSERT INTO t1 VALUES($v1, NULL); - SELECT snippet(t1, -1, '[', ']', '...', 7) FROM t1 WHERE t1 MATCH $v2 - ORDER BY rank DESC; - } [list $res] - - -} - - -foreach {tn doc res} { - - 1.1 {X o o o o o o} {[X] o o o o o o} - 1.2 {o X o o o o o} {o [X] o o o o o} - 1.3 {o o X o o o o} {o o [X] o o o o} - 1.4 {o o o X o o o} {o o o [X] o o o} - 1.5 {o o o o X o o} {o o o o [X] o o} - 1.6 {o o o o o X o} {o o o o o [X] o} - 1.7 {o o o o o o X} {o o o o o o [X]} - - 2.1 {X o o o o o o o} {[X] o o o o o o...} - 2.2 {o X o o o o o o} {o [X] o o o o o...} - 2.3 {o o X o o o o o} {o o [X] o o o o...} - 2.4 {o o o X o o o o} {o o o [X] o o o...} - 2.5 {o o o o X o o o} {...o o o [X] o o o} - 2.6 {o o o o o X o o} {...o o o o [X] o o} - 2.7 {o o o o o o X o} {...o o o o o [X] o} - 2.8 {o o o o o o o X} {...o o o o o o [X]} - - 3.1 {X o o o o o o o o} {[X] o o o o o o...} - 3.2 {o X o o o o o o o} {o [X] o o o o o...} - 3.3 {o o X o o o o o o} {o o [X] o o o o...} - 3.4 {o o o X o o o o o} {o o o [X] o o o...} - 3.5 {o o o o X o o o o} {...o o o [X] o o o...} - 3.6 {o o o o o X o o o} {...o o o [X] o o o} - 3.7 {o o o o o o X o o} {...o o o o [X] o o} - 3.8 {o o o o o o o X o} {...o o o o o [X] o} - 3.9 {o o o o o o o o X} {...o o o o o o [X]} - - 4.1 {X o o o o o X o o} {[X] o o o o o [X]...} - 4.2 {o X o o o o o X o} {...[X] o o o o o [X]...} - 4.3 {o o X o o o o o X} {...[X] o o o o o [X]} - - 5.1 {X o o o o X o o o} {[X] o o o o [X] o...} - 5.2 {o X o o o o X o o} {...[X] o o o o [X] o...} - 5.3 {o o X o o o o X o} {...[X] o o o o [X] o} - 5.4 {o o o X o o o o X} {...o [X] o o o o [X]} - - 6.1 {X o o o X o o o} {[X] o o o [X] o o...} - 6.2 {o X o o o X o o o} {o [X] o o o [X] o...} - 6.3 {o o X o o o X o o} {...o [X] o o o [X] o...} - 6.4 {o o o X o o o X o} {...o [X] o o o [X] o} - 6.5 {o o o o X o o o X} {...o o [X] o o o [X]} - - 7.1 {X o o X o o o o o} {[X] o o [X] o o o...} - 7.2 {o X o o X o o o o} {o [X] o o [X] o o...} - 7.3 {o o X o o X o o o} {...o [X] o o [X] o o...} - 7.4 {o o o X o o X o o} {...o [X] o o [X] o o} - 7.5 {o o o o X o o X o} {...o o [X] o o [X] o} - 7.6 {o o o o o X o o X} {...o o o [X] o o [X]} -} { - do_snippet_test 1.$tn $doc X $res -} - -foreach {tn doc res} { - 1.1 {X Y o o o o o} {[X Y] o o o o o} - 1.2 {o X Y o o o o} {o [X Y] o o o o} - 1.3 {o o X Y o o o} {o o [X Y] o o o} - 1.4 {o o o X Y o o} {o o o [X Y] o o} - 1.5 {o o o o X Y o} {o o o o [X Y] o} - 1.6 {o o o o o X Y} {o o o o o [X Y]} - - 2.1 {X Y o o o o o o} {[X Y] o o o o o...} - 2.2 {o X Y o o o o o} {o [X Y] o o o o...} - 2.3 {o o X Y o o o o} {o o [X Y] o o o...} - 2.4 {o o o X Y o o o} {...o o [X Y] o o o} - 2.5 {o o o o X Y o o} {...o o o [X Y] o o} - 2.6 {o o o o o X Y o} {...o o o o [X Y] o} - 2.7 {o o o o o o X Y} {...o o o o o [X Y]} - - 3.1 {X Y o o o o o o o} {[X Y] o o o o o...} - 3.2 {o X Y o o o o o o} {o [X Y] o o o o...} - 3.3 {o o X Y o o o o o} {o o [X Y] o o o...} - 3.4 {o o o X Y o o o o} {...o o [X Y] o o o...} - 3.5 {o o o o X Y o o o} {...o o [X Y] o o o} - 3.6 {o o o o o X Y o o} {...o o o [X Y] o o} - 3.7 {o o o o o o X Y o} {...o o o o [X Y] o} - 3.8 {o o o o o o o X Y} {...o o o o o [X Y]} - -} { - do_snippet_test 2.$tn $doc "X + Y" $res -} - -finish_test - DELETED ext/fts5/test/fts5ag.test Index: ext/fts5/test/fts5ag.test ================================================================== --- ext/fts5/test/fts5ag.test +++ /dev/null @@ -1,138 +0,0 @@ -# 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 fts5ag - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -#------------------------------------------------------------------------- -# This file attempts to verify that the extension APIs work with -# "ORDER BY rank" queries. This is done by comparing the results of -# the fts5_test() function when run with queries of the form: -# -# ... WHERE fts MATCH ? ORDER BY bm25(fts) [ASC|DESC] -# -# and -# -# ... WHERE fts MATCH ? ORDER BY rank [ASC|DESC] -# - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x, y, z); -} - -do_test 1.1 { - foreach {x y z} { - {j s m y m r n l u k} {z k f u z g h s w g} {r n o s s b v n w w} - {m v g n d x q r r s} {q t d a q a v l h j} {s k l f s i n v q v} - {m f f d h h s o h a} {y e v r q i u m h d} {b c k q m z l z h n} - {j e m v k p e c j m} {m p v z d x l n i a} {v p u p m t p q i f} - {v r w l e e t d z p} {c s b w k m n k o u} {w g y f v w v w v p} - {k d g o u j p z n o} {t g e q l z i g b j} {f i q q j y h b g h} - {j s w x o t j b t m} {v a v v r t x c q a} {r t k x w u l h a g} - {j y b i u d e m d w} {y s o j h i n a u p} {n a g b u c w e b m} - {b c k s c w j p w b} {m o c o w o b d q q} {n t y o y z y r z e} - {p n q l e l h z q c} {n s e i h c v b b u} {m p d i t a o o f f} - {k c o n v e z l b m} {s m n i n s d e s u} {t a u e q d a o u c} - {h d t o i a g b b p} {k x c i g f g b b k} {x f i v n a n n j i} - {f z k r b u s k z e} {n z v z w l e r h t} {t i s v v a v p n s} - {k f e c t z r e f d} {f m g r c w q k b v} {v y s y f r b f e f} - {z r c t d q q h x b} {u c g z n z u v s s} {y t n f f x b f d x} - {u n p n u t i m e j} {p j j d m f k p m z} {d o l v c o e a h w} - {h o q w t f v i c y} {c q u n r z s l l q} {z x a q w s b w s y} - {y m s x k i m n x c} {b i a n v h z n k a} {w l q p b h h g d y} - {z v s j f p v l f w} {c s b i z e k i g c} {x b v d w j f e d z} - {r k k j e o m k g b} {h b d c h m y b t u} {u j s h k z c u d y} - {v h i v s y z i k l} {d t m w q w c a z p} {r s e s x v d w k b} - {u r e q j y h o o s} {x x z r x y t f j s} {k n h x i i u e c v} - {q l f d a p w l q o} {y z q w j o p b o v} {s u h z h f d f n l} - {q o e o x x l g q i} {j g m h q q w c d b} {o m d h w a g b f n} - {m x k t s s y l v a} {j x t c a u w b w g} {n f j b v x y p u t} - {u w k a q b u w k w} {a h j u o w f s k p} {j o f s h y t j h g} - {x v b l m t l m h l} {t p y i y i q b q a} {k o o z w a c h c f} - {j g c d k w b d t v} {a k v c m a v h v p} {i c a i j g h l j h} - {l m v l c z j b p b} {z p z f l n k i b a} {j v q k g i x g i b} - {m c i w u z m i s z} {i z r f n l q z k w} {x n b p b q r g i z} - {d g i o o x l f x d} {r t m f b n q y c b} {i u g k w x n m p o} - {t o s i q d z x d t} {v a k s q z j c o o} {z f n n r l y w v v} - {w k h d t l j g n n} {r z m v y b l n c u} {v b v s c l n k g v} - {m a g r a b u u n z} {u y l h v w v k b f} {x l p g i s j f x v} - {v s g x k z a k a r} {l t g v j q l k p l} {f h n a x t v s t y} - {z u v u x p s j y t} {g b q e e g l n w g} {e n p j i g j f u r} - {q z l t w o l m p e} {t s g h r p r o t z} {y b f a o n u m z g} - {d t w n y b o g f o} {d a j e r l g g s h} {d z e l w q l t h f} - {f l u w q v x j a h} {f n u l l d m h h w} {d x c c e r o d q j} - {b y f q s q f u l g} {u z w l f d b i a g} {m v q b g u o z e z} - {h z p t s e x i v m} {l h q m e o x x x j} {e e d n p r m g j f} - {k h s g o n s d a x} {u d t t s j o v h a} {z r b a e u v o e s} - {m b b g a f c p a t} {w c m j o d b l g e} {f p j p m o s y v j} - {c r n h d w c a b l} {s g e u s d n j b g} {b o n a x a b x y l} - {r h u x f c d z n o} {x y l g u m i i w d} {t f h b z v r s r g} - {t i o r b v g g p a} {d x l u q k m o s u} {j f h t u n z u k m} - {g j t y d c n j y g} {w e s k v c w i g t} {g a h r g v g h r o} - {e j l a q j g i n h} {d z k c u p n u p p} {t u e e v z v r r g} - {l j s g k j k h z l} {p v d a t x d e q u} {r l u z b m g k s j} - {i e y d u x d i n l} {p f z k m m w p u l} {z l p m r q w n d a} - } { - execsql { INSERT INTO t1 VALUES($x, $y, $z) } - } - set {} {} -} {} - -fts5_aux_test_functions db - -proc do_fts5ag_test {tn E} { - set q1 {SELECT fts5_test_all(t1) FROM t1 WHERE t1 MATCH $E ORDER BY rank} - set q2 {SELECT fts5_test_all(t1) FROM t1 WHERE t1 MATCH $E ORDER BY bm25(t1)} - - set res [execsql $q1] - set expected [execsql $q2] - uplevel [list do_test $tn.1 [list set {} $res] $expected] - - append q1 " DESC" - append q2 " DESC" - - set res [execsql $q1] - set expected [execsql $q2] - uplevel [list do_test $tn.2 [list set {} $res] $expected] -} - -foreach {tn expr} { - 2.1 a - 2.2 b - 2.3 c - 2.4 d - - 2.5 {"m m"} - 2.6 {e + s} - - 3.0 {a AND b} - 3.1 {a OR b} - 3.2 {b OR c AND d} - 3.3 {NEAR(c d)} -} { - do_fts5ag_test $tn $expr - - if {[set_test_counter errors]} break -} - - - -finish_test - DELETED ext/fts5/test/fts5ah.test Index: ext/fts5/test/fts5ah.test ================================================================== --- ext/fts5/test/fts5ah.test +++ /dev/null @@ -1,100 +0,0 @@ -# 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 fts5ah - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -#------------------------------------------------------------------------- -# This file contains tests for very large doclists. -# - -do_test 1.0 { - execsql { CREATE VIRTUAL TABLE t1 USING fts5(a) } - execsql { INSERT INTO t1(t1, rank) VALUES('pgsz', 128) } - for {set i 1} {$i <= 10000} {incr i} { - set v {x x x x x x x x x x x x x x x x x x x x} - if {($i % 2139)==0} {lset v 3 Y ; lappend Y $i} - if {($i % 1577)==0} {lset v 5 W ; lappend W $i} - execsql { INSERT INTO t1 VALUES($v) } - } -} {} - -do_execsql_test 1.1 { - SELECT rowid FROM t1 WHERE t1 MATCH 'x AND w' -} [lsort -integer -decr $W] - -do_execsql_test 1.2 { - SELECT rowid FROM t1 WHERE t1 MATCH 'y AND x' -} [lsort -integer -decr $Y] - -do_execsql_test 1.3 { - INSERT INTO t1(t1) VALUES('integrity-check'); -} - -proc reads {} { - db one {SELECT t1 FROM t1 WHERE t1 MATCH '*reads'} -} - -proc execsql_reads {sql} { - set nRead [reads] - execsql $sql - expr [reads] - $nRead -} - -do_test 1.4 { - set nRead [reads] - execsql { SELECT rowid FROM t1 WHERE t1 MATCH 'x' } - set nReadX [expr [reads] - $nRead] - expr $nReadX>1000 -} {1} - -do_test 1.5 { - set fwd [execsql_reads {SELECT rowid FROM t1 WHERE t1 MATCH 'x' }] - set bwd [execsql_reads { - SELECT rowid FROM t1 WHERE t1 MATCH 'x' ORDER BY 1 ASC - }] - expr {$bwd < $fwd + 12} -} {1} - -foreach {tn q res} " - 1 { SELECT rowid FROM t1 WHERE t1 MATCH 'w + x' } [list $W] - 2 { SELECT rowid FROM t1 WHERE t1 MATCH 'x + w' } [list $W] - 3 { SELECT rowid FROM t1 WHERE t1 MATCH 'x AND w' } [list $W] - 4 { SELECT rowid FROM t1 WHERE t1 MATCH 'y AND x' } [list $Y] -" { - - do_test 1.6.$tn.1 { - set n [execsql_reads $q] - expr {$n < ($nReadX / 10)} - } {1} - - do_test 1.6.$tn.2 { - set n [execsql_reads "$q ORDER BY rowid ASC"] - expr {$n < ($nReadX / 10)} - } {1} - - do_execsql_test 1.6.$tn.3 $q [lsort -int -decr $res] - do_execsql_test 1.6.$tn.4 "$q ORDER BY rowid ASC" [lsort -int -incr $res] -} - -#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r} - -finish_test - DELETED ext/fts5/test/fts5ai.test Index: ext/fts5/test/fts5ai.test ================================================================== --- ext/fts5/test/fts5ai.test +++ /dev/null @@ -1,55 +0,0 @@ -# 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. -# -# Specifically, it tests transactions and savepoints -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5ai - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a); -} {} - -do_execsql_test 1.1 { - BEGIN; - INSERT INTO t1 VALUES('a b c'); - INSERT INTO t1 VALUES('d e f'); - SAVEPOINT one; - INSERT INTO t1 VALUES('g h i'); - SAVEPOINT two; - INSERT INTO t1 VALUES('j k l'); - ROLLBACK TO one; - INSERT INTO t1 VALUES('m n o'); - SAVEPOINT two; - INSERT INTO t1 VALUES('p q r'); - RELEASE one; - SAVEPOINT one; - INSERT INTO t1 VALUES('s t u'); - ROLLBACK TO one; - COMMIT; -} - -do_execsql_test 1.2 { - INSERT INTO t1(t1) VALUES('integrity-check'); -} - - -finish_test - DELETED ext/fts5/test/fts5aj.test Index: ext/fts5/test/fts5aj.test ================================================================== --- ext/fts5/test/fts5aj.test +++ /dev/null @@ -1,69 +0,0 @@ -# 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. -# -# Specifically, this tests that, provided the amount of data remains -# constant, the FTS index does not grow indefinitely as rows are inserted -# and deleted, -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5aj - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -proc doc {} { - set dict [list a b c d e f g h i j k l m n o p q r s t u v w x y z] - set res [list] - for {set i 0} {$i < 20} {incr i} { - lappend res [lindex $dict [expr int(rand() * 26)]] - } - set res -} - -proc structure {} { - set val [db one {SELECT fts5_decode(rowid,block) FROM t1_data WHERE rowid=10}] - foreach lvl [lrange $val 1 end] { - lappend res [expr [llength $lvl]-2] - } - set res -} - -expr srand(0) -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x); - INSERT INTO t1(t1, rank) VALUES('pgsz', 64); -} - -for {set iTest 0} {$iTest < 50000} {incr iTest} { - if {$iTest > 1000} { execsql { DELETE FROM t1 WHERE rowid=($iTest-1000) } } - set new [doc] - execsql { INSERT INTO t1 VALUES($new) } - if {$iTest==10000} { set sz1 [db one {SELECT count(*) FROM t1_data}] } - if {0==($iTest % 1000)} { - set sz [db one {SELECT count(*) FROM t1_data}] - set s [structure] - do_execsql_test 1.$iTest.$sz.{$s} { - INSERT INTO t1(t1) VALUES('integrity-check') - } - } -} - -do_execsql_test 2.0 { INSERT INTO t1(t1) VALUES('integrity-check') } - - -finish_test - DELETED ext/fts5/test/fts5ak.test Index: ext/fts5/test/fts5ak.test ================================================================== --- ext/fts5/test/fts5ak.test +++ /dev/null @@ -1,143 +0,0 @@ -# 2014 November 24 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#************************************************************************* -# This file implements regression tests for SQLite library. The -# focus of this script is testing the FTS5 module. -# -# Specifically, the auxiliary function "highlight". -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5ak - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.1 { - CREATE VIRTUAL TABLE ft1 USING fts5(x); - INSERT INTO ft1 VALUES('i d d a g i b g d d'); - INSERT INTO ft1 VALUES('h d b j c c g a c a'); - INSERT INTO ft1 VALUES('e j a e f h b f h h'); - INSERT INTO ft1 VALUES('j f h d g h i b d f'); - INSERT INTO ft1 VALUES('d c j d c j b c g e'); - INSERT INTO ft1 VALUES('i a d e g j g d a a'); - INSERT INTO ft1 VALUES('j f c e d a h j d b'); - INSERT INTO ft1 VALUES('i c c f a d g h j e'); - INSERT INTO ft1 VALUES('i d i g c d c h b f'); - INSERT INTO ft1 VALUES('g d a e h a b c f j'); -} - -do_execsql_test 1.2 { - SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'e'; -} { - {g d a [e] h a b c f j} - {i c c f a d g h j [e]} - {j f c [e] d a h j d b} - {i a d [e] g j g d a a} - {d c j d c j b c g [e]} - {[e] j a [e] f h b f h h} -} - -do_execsql_test 1.3 { - SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'h + d'; -} { - {j f [h d] g h i b d f} - {[h d] b j c c g a c a} -} - -do_execsql_test 1.4 { - SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d'; -} { - {i [d d] a g i b g [d d]} -} - -do_execsql_test 1.5 { - SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'e e e' -} { - {g d a [e] h a b c f j} - {i c c f a d g h j [e]} - {j f c [e] d a h j d b} - {i a d [e] g j g d a a} - {d c j d c j b c g [e]} - {[e] j a [e] f h b f h h} -} - -do_execsql_test 1.6 { - SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d d + d'; -} { - {i [d d] a g i b g [d d]} -} - -do_execsql_test 2.1 { - CREATE VIRTUAL TABLE ft2 USING fts5(x); - INSERT INTO ft2 VALUES('a b c d e f g h i j'); -} - -do_execsql_test 2.2 { - SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c+d+e' -} {{a [b c d e] f g h i j}} - -do_execsql_test 2.3 { - SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d e+f+g' -} { - {a [b c d] [e f g] h i j} -} - -do_execsql_test 2.4 { - SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c' -} { - {a [b c d] e f g h i j} -} - -do_execsql_test 2.5 { - SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c c+d+e' -} { - {a [b c d e] f g h i j} -} - -do_execsql_test 2.6.1 { - SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'f d' -} { - {a b c [d] e [f] g h i j} -} - -do_execsql_test 2.6.2 { - SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'd f' -} { - {a b c [d] e [f] g h i j} -} - -#------------------------------------------------------------------------- -# The example from the docs. -# -do_execsql_test 3.1 { - -- Assuming this: - CREATE VIRTUAL TABLE ft USING fts5(a); - INSERT INTO ft VALUES('a b c x c d e'); - INSERT INTO ft VALUES('a b c c d e'); - INSERT INTO ft VALUES('a b c d e'); - - -- The following SELECT statement returns these three rows: - -- '[a b c] x [c d e]' - -- '[a b c] [c d e]' - -- '[a b c d e]' - SELECT highlight(ft, 0, '[', ']') FROM ft WHERE ft MATCH 'a+b+c AND c+d+e'; -} { - {[a b c d e]} - {[a b c] [c d e]} - {[a b c] x [c d e]} -} - - -finish_test - DELETED ext/fts5/test/fts5al.test Index: ext/fts5/test/fts5al.test ================================================================== --- ext/fts5/test/fts5al.test +++ /dev/null @@ -1,272 +0,0 @@ -# 2014 November 24 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#************************************************************************* -# This file implements regression tests for SQLite library. The -# focus of this script is testing the FTS5 module. -# -# Specifically, this function tests the %_config table. -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5al - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -do_execsql_test 1.1 { - CREATE VIRTUAL TABLE ft1 USING fts5(x); - SELECT * FROM ft1_config; -} {} - -do_execsql_test 1.2 { - INSERT INTO ft1(ft1, rank) VALUES('pgsz', 32); - SELECT * FROM ft1_config; -} {pgsz 32} - -do_execsql_test 1.3 { - INSERT INTO ft1(ft1, rank) VALUES('pgsz', 64); - SELECT * FROM ft1_config; -} {pgsz 64} - -#-------------------------------------------------------------------------- -# Test the logic for parsing the rank() function definition. -# -foreach {tn defn} { - 1 "fname()" - 2 "fname(1)" - 3 "fname(1,2)" - 4 "fname(null,NULL,nUlL)" - 5 " fname ( null , NULL , nUlL ) " - 6 "fname('abc')" - 7 "fname('a''bc')" - 8 "fname('''abc')" - 9 "fname('abc''')" - - 7 "fname( 'a''bc' )" - 8 "fname('''abc' )" - 9 "fname( 'abc''' )" - - 10 "fname(X'1234ab')" - - 11 "myfunc(1.2)" - 12 "myfunc(-1.0)" - 13 "myfunc(.01,'abc')" -} { - do_execsql_test 2.1.$tn { - INSERT INTO ft1(ft1, rank) VALUES('rank', $defn); - } -} - -foreach {tn defn} { - 1 "" - 2 "fname" - 3 "fname(X'234ab')" - 4 "myfunc(-1.,'abc')" -} { - do_test 2.2.$tn { - catchsql { INSERT INTO ft1(ft1, rank) VALUES('rank', $defn) } - } {1 {SQL logic error or missing database}} -} - -#------------------------------------------------------------------------- -# Assorted tests of the tcl interface for creating extension functions. -# - -do_execsql_test 3.1 { - CREATE VIRTUAL TABLE t1 USING fts5(x); - INSERT INTO t1 VALUES('q w e r t y'); - INSERT INTO t1 VALUES('y t r e w q'); -} - -proc argtest {cmd args} { return $args } -sqlite3_fts5_create_function db argtest argtest - -do_execsql_test 3.2.1 { - SELECT argtest(t1, 123) FROM t1 WHERE t1 MATCH 'q' -} {123 123} - -do_execsql_test 3.2.2 { - SELECT argtest(t1, 123, 456) FROM t1 WHERE t1 MATCH 'q' -} {{123 456} {123 456}} - -proc rowidtest {cmd} { $cmd xRowid } -sqlite3_fts5_create_function db rowidtest rowidtest - -do_execsql_test 3.3.1 { - SELECT rowidtest(t1) FROM t1 WHERE t1 MATCH 'q' -} {2 1} - -proc insttest {cmd} { - set res [list] - for {set i 0} {$i < [$cmd xInstCount]} {incr i} { - lappend res [$cmd xInst $i] - } - set res -} -sqlite3_fts5_create_function db insttest insttest - -do_execsql_test 3.4.1 { - SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'q' -} { - {{0 0 5}} - {{0 0 0}} -} - -do_execsql_test 3.4.2 { - SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'r+e OR w' -} { - {{0 0 2} {1 0 4}} - {{1 0 1}} -} - -proc coltest {cmd} { - list [$cmd xColumnSize 0] [$cmd xColumnText 0] -} -sqlite3_fts5_create_function db coltest coltest - -do_execsql_test 3.5.1 { - SELECT coltest(t1) FROM t1 WHERE t1 MATCH 'q' -} { - {6 {y t r e w q}} {6 {q w e r t y}} -} - -#------------------------------------------------------------------------- -# Tests for remapping the "rank" column. -# -# 4.1.*: Mapped to a function with no arguments. -# 4.2.*: Mapped to a function with one or more arguments. -# - -do_execsql_test 4.0 { - CREATE VIRTUAL TABLE t2 USING fts5(a, b); - INSERT INTO t2 VALUES('a s h g s b j m r h', 's b p a d b b a o e'); - INSERT INTO t2 VALUES('r h n t a g r d d i', 'l d n j r c f t o q'); - INSERT INTO t2 VALUES('q k n i k c a a e m', 'c h n j p g s c i t'); - INSERT INTO t2 VALUES('h j g t r e l s g s', 'k q k c i i c k n s'); - INSERT INTO t2 VALUES('b l k h d n n n m i', 'p t i a r b t q o l'); - INSERT INTO t2 VALUES('k r i l j b g i p a', 't q c h a i m g n l'); - INSERT INTO t2 VALUES('a e c q n m o m d g', 'l c t g i s q g q e'); - INSERT INTO t2 VALUES('b o j h f o g b p e', 'r t l h s b g i c p'); - INSERT INTO t2 VALUES('s q k f q b j g h f', 'n m a o p e i e k t'); - INSERT INTO t2 VALUES('o q g g q c o k a b', 'r t k p t f t h p c'); -} - -proc firstinst {cmd} { - foreach {p c o} [$cmd xInst 0] {} - expr $c*100 + $o -} -sqlite3_fts5_create_function db firstinst firstinst - -do_execsql_test 4.1.1 { - SELECT rowid, firstinst(t2) FROM t2 WHERE t2 MATCH 'a' ORDER BY rowid ASC -} { - 1 0 2 4 3 6 5 103 - 6 9 7 0 9 102 10 8 -} - -do_execsql_test 4.1.2 { - SELECT rowid, rank FROM t2 - WHERE t2 MATCH 'a' AND rank MATCH 'firstinst()' - ORDER BY rowid ASC -} { - 1 0 2 4 3 6 5 103 - 6 9 7 0 9 102 10 8 -} - -do_execsql_test 4.1.3 { - SELECT rowid, rank FROM t2 - WHERE t2 MATCH 'a' AND rank MATCH 'firstinst()' - ORDER BY rank DESC -} { - 5 103 9 102 6 9 10 8 3 6 2 4 7 0 1 0 -} - -do_execsql_test 4.1.4 { - INSERT INTO t2(t2, rank) VALUES('rank', 'firstinst()'); - SELECT rowid, rank FROM t2 WHERE t2 MATCH 'a' ORDER BY rowid ASC -} { - 1 0 2 4 3 6 5 103 - 6 9 7 0 9 102 10 8 -} - -do_execsql_test 4.1.5 { - SELECT rowid, rank FROM t2 WHERE t2 MATCH 'a' ORDER BY rank DESC -} { - 5 103 9 102 6 9 10 8 3 6 2 4 7 0 1 0 -} - -do_execsql_test 4.1.6 { - INSERT INTO t2(t2, rank) VALUES('rank', 'firstinst ( ) '); - SELECT rowid, rank FROM t2 WHERE t2 MATCH 'a' ORDER BY rank DESC -} { - 5 103 9 102 6 9 10 8 3 6 2 4 7 0 1 0 -} - -proc rowidplus {cmd ival} { - expr [$cmd xRowid] + $ival -} -sqlite3_fts5_create_function db rowidplus rowidplus - -do_execsql_test 4.2.1 { - INSERT INTO t2(t2, rank) VALUES('rank', 'rowidplus(100) '); - SELECT rowid, rank FROM t2 WHERE t2 MATCH 'o + q + g' -} { - 10 110 -} -do_execsql_test 4.2.2 { - INSERT INTO t2(t2, rank) VALUES('rank', 'rowidplus(111) '); - SELECT rowid, rank FROM t2 WHERE t2 MATCH 'o + q + g' -} { - 10 121 -} - -do_execsql_test 4.2.3 { - SELECT rowid, rank FROM t2 - WHERE t2 MATCH 'o + q + g' AND rank MATCH 'rowidplus(112)' -} { - 10 122 -} - -proc rowidmod {cmd imod} { - expr [$cmd xRowid] % $imod -} -sqlite3_fts5_create_function db rowidmod rowidmod -do_execsql_test 4.3.1 { - CREATE VIRTUAL TABLE t3 USING fts5(x); - INSERT INTO t3 VALUES('a one'); - INSERT INTO t3 VALUES('a two'); - INSERT INTO t3 VALUES('a three'); - INSERT INTO t3 VALUES('a four'); - INSERT INTO t3 VALUES('a five'); - INSERT INTO t3(t3, rank) VALUES('rank', 'bm25()'); -} -breakpoint - -do_execsql_test 4.3.2 { - SELECT * FROM t3 - WHERE t3 MATCH 'a' AND rank MATCH 'rowidmod(4)' - ORDER BY rank ASC -} { - {a four} {a five} {a one} {a two} {a three} -} -do_execsql_test 4.3.3 { - SELECT *, rank FROM t3 - WHERE t3 MATCH 'a' AND rank MATCH 'rowidmod(3)' - ORDER BY rank ASC -} { - {a three} 0 {a four} 1 {a one} 1 {a five} 2 {a two} 2 -} - - -finish_test - DELETED ext/fts5/test/fts5auxdata.test Index: ext/fts5/test/fts5auxdata.test ================================================================== --- ext/fts5/test/fts5auxdata.test +++ /dev/null @@ -1,109 +0,0 @@ -# 2014 Dec 20 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# -# Tests focusing on the fts5 xSetAuxdata() and xGetAuxdata() APIs. -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5auxdata - -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE f1 USING fts5(a, b); - INSERT INTO f1(rowid, a, b) VALUES(1, 'a', 'b1'); - INSERT INTO f1(rowid, a, b) VALUES(2, 'a', 'b2'); - INSERT INTO f1(rowid, a, b) VALUES(3, 'a', 'b3'); - INSERT INTO f1(rowid, a, b) VALUES(4, 'a', 'b4'); - INSERT INTO f1(rowid, a, b) VALUES(5, 'a', 'b5'); -} - -proc aux_function_1 {cmd tn} { - switch [$cmd xRowid] { - 1 { - do_test $tn.1 [list $cmd xGetAuxdata 0 ] {} - $cmd xSetAuxdata "one" - } - - 2 { - do_test $tn.2 [list $cmd xGetAuxdata 0 ] {one} - $cmd xSetAuxdata "two" - } - - 3 { - do_test $tn.3 [list $cmd xGetAuxdata 0 ] {two} - } - - 4 { - do_test $tn.4 [list $cmd xGetAuxdata 1 ] {two} - } - - 5 { - do_test $tn.5 [list $cmd xGetAuxdata 0 ] {} - } - } -} - -sqlite3_fts5_create_function db aux_function_1 aux_function_1 -db eval { - SELECT aux_function_1(f1, 1) FROM f1 WHERE f1 MATCH 'a' - ORDER BY rowid ASC -} - -proc aux_function_2 {cmd tn inst} { - if {$inst == "A"} { - switch [$cmd xRowid] { - 1 { - do_test $tn.1.$inst [list $cmd xGetAuxdata 0 ] {} - $cmd xSetAuxdata "one $inst" - } - 2 { - do_test $tn.2.$inst [list $cmd xGetAuxdata 0 ] "one $inst" - $cmd xSetAuxdata "two $inst" - } - 3 { - do_test $tn.3.$inst [list $cmd xGetAuxdata 0 ] "two $inst" - } - 4 { - do_test $tn.4.$inst [list $cmd xGetAuxdata 1 ] "two $inst" - } - 5 { - do_test $tn.5.$inst [list $cmd xGetAuxdata 0 ] {} - } - } - } else { - switch [$cmd xRowid] { - 1 { - do_test $tn.1.$inst [list $cmd xGetAuxdata 0 ] "one A" - } - 2 { - do_test $tn.2.$inst [list $cmd xGetAuxdata 0 ] "two A" - } - 3 { - do_test $tn.3.$inst [list $cmd xGetAuxdata 0 ] "two A" - } - 4 { - do_test $tn.4.$inst [list $cmd xGetAuxdata 0 ] {} - } - 5 { - do_test $tn.5.$inst [list $cmd xGetAuxdata 0 ] {} - } - } - } -} - -sqlite3_fts5_create_function db aux_function_2 aux_function_2 -db eval { - SELECT aux_function_2(f1, 2, 'A'), aux_function_2(f1, 2, 'B') - FROM f1 WHERE f1 MATCH 'a' - ORDER BY rowid ASC -} - -finish_test - DELETED ext/fts5/test/fts5content.test Index: ext/fts5/test/fts5content.test ================================================================== --- ext/fts5/test/fts5content.test +++ /dev/null @@ -1,190 +0,0 @@ -# 2014 Dec 20 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5content - -#------------------------------------------------------------------------- -# Contentless tables -# -do_execsql_test 1.1 { - CREATE VIRTUAL TABLE f1 USING fts5(a, b, content=''); - INSERT INTO f1(rowid, a, b) VALUES(1, 'one', 'o n e'); - INSERT INTO f1(rowid, a, b) VALUES(2, 'two', 't w o'); - INSERT INTO f1(rowid, a, b) VALUES(3, 'three', 't h r e e'); -} - -do_execsql_test 1.2 { - SELECT rowid FROM f1 WHERE f1 MATCH 'o'; -} {2 1} - -do_execsql_test 1.3 { - INSERT INTO f1(a, b) VALUES('four', 'f o u r'); - SELECT rowid FROM f1 WHERE f1 MATCH 'o'; -} {4 2 1} - -do_execsql_test 1.4 { - SELECT rowid, a, b FROM f1 WHERE f1 MATCH 'o'; -} {4 {} {} 2 {} {} 1 {} {}} - -do_execsql_test 1.5 { - SELECT rowid, highlight(f1, 0, '[', ']') FROM f1 WHERE f1 MATCH 'o'; -} {4 {} 2 {} 1 {}} - -do_execsql_test 1.6 { - SELECT rowid, highlight(f1, 0, '[', ']') IS NULL FROM f1 WHERE f1 MATCH 'o'; -} {4 1 2 1 1 1} - -do_execsql_test 1.7 { - SELECT rowid, snippet(f1, -1, '[', ']', '...', 5) IS NULL - FROM f1 WHERE f1 MATCH 'o'; -} {4 1 2 1 1 1} - -do_execsql_test 1.8 { - SELECT rowid, snippet(f1, 1, '[', ']', '...', 5) IS NULL - FROM f1 WHERE f1 MATCH 'o'; -} {4 1 2 1 1 1} - -do_execsql_test 1.9 { - SELECT rowid FROM f1; -} {4 3 2 1} - -do_execsql_test 1.10 { - SELECT * FROM f1; -} {{} {} {} {} {} {} {} {}} - -do_execsql_test 1.11 { - SELECT rowid, a, b FROM f1 ORDER BY rowid ASC; -} {1 {} {} 2 {} {} 3 {} {} 4 {} {}} - -do_execsql_test 1.12 { - SELECT a IS NULL FROM f1; -} {1 1 1 1} - -do_catchsql_test 1.13 { - DELETE FROM f1 WHERE rowid = 2; -} {1 {cannot DELETE from contentless fts5 table: f1}} - -do_catchsql_test 1.14 { - UPDATE f1 SET a = 'a b c' WHERE rowid = 2; -} {1 {cannot UPDATE contentless fts5 table: f1}} - -do_execsql_test 1.15 { - INSERT INTO f1(f1, rowid, a, b) VALUES('delete', 2, 'two', 't w o'); -} {} - -do_execsql_test 1.16 { - SELECT rowid FROM f1 WHERE f1 MATCH 'o'; -} {4 1} - -do_execsql_test 1.17 { - SELECT rowid FROM f1; -} {4 3 1} - -#------------------------------------------------------------------------- -# External content tables -# -reset_db -do_execsql_test 2.1 { - -- Create a table. And an external content fts5 table to index it. - CREATE TABLE tbl(a INTEGER PRIMARY KEY, b, c); - CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a'); - - -- Triggers to keep the FTS index up to date. - CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGIN - INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c); - END; - CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGIN - INSERT INTO fts_idx(fts_idx, rowid, b, c) - VALUES('delete', old.a, old.b, old.c); - END; - CREATE TRIGGER tbl_au AFTER UPDATE ON tbl BEGIN - INSERT INTO fts_idx(fts_idx, rowid, b, c) - VALUES('delete', old.a, old.b, old.c); - INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c); - END; -} - -do_execsql_test 2.2 { - INSERT INTO tbl VALUES(1, 'one', 'o n e'); - INSERT INTO tbl VALUES(NULL, 'two', 't w o'); - INSERT INTO tbl VALUES(3, 'three', 't h r e e'); -} - -do_execsql_test 2.3 { - INSERT INTO fts_idx(fts_idx) VALUES('integrity-check'); -} - -do_execsql_test 2.4 { - DELETE FROM tbl WHERE rowid=2; - INSERT INTO fts_idx(fts_idx) VALUES('integrity-check'); -} - -do_execsql_test 2.5 { - UPDATE tbl SET c = c || ' x y z'; - INSERT INTO fts_idx(fts_idx) VALUES('integrity-check'); -} - -do_execsql_test 2.6 { - SELECT * FROM fts_idx WHERE fts_idx MATCH 't AND x'; -} {three {t h r e e x y z}} - -do_execsql_test 2.7 { - SELECT highlight(fts_idx, 1, '[', ']') FROM fts_idx - WHERE fts_idx MATCH 't AND x'; -} {{[t] h r e e [x] y z}} - -#------------------------------------------------------------------------- -# Quick tests of the 'delete-all' command. -# -do_execsql_test 3.1 { - CREATE VIRTUAL TABLE t3 USING fts5(x, content=''); - INSERT INTO t3 VALUES('a b c'); - INSERT INTO t3 VALUES('d e f'); -} - -do_execsql_test 3.2 { - SELECT count(*) FROM t3_docsize; - SELECT count(*) FROM t3_data; -} {2 4} - -do_execsql_test 3.3 { - INSERT INTO t3(t3) VALUES('delete-all'); - SELECT count(*) FROM t3_docsize; - SELECT count(*) FROM t3_data; -} {0 2} - -do_execsql_test 3.4 { - INSERT INTO t3 VALUES('a b c'); - INSERT INTO t3 VALUES('d e f'); - SELECT rowid FROM t3 WHERE t3 MATCH 'e'; -} {2} - -do_execsql_test 3.5 { - SELECT rowid FROM t3 WHERE t3 MATCH 'c'; -} {1} - -do_execsql_test 3.6 { - SELECT count(*) FROM t3_docsize; - SELECT count(*) FROM t3_data; -} {2 4} - -do_execsql_test 3.7 { - CREATE VIRTUAL TABLE t4 USING fts5(x); -} {} -do_catchsql_test 3.8 { - INSERT INTO t4(t4) VALUES('delete-all'); -} {1 {'delete-all' may only be used with a contentless or external content fts5 table}} - -finish_test - DELETED ext/fts5/test/fts5ea.test Index: ext/fts5/test/fts5ea.test ================================================================== --- ext/fts5/test/fts5ea.test +++ /dev/null @@ -1,83 +0,0 @@ -# 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. -# -#************************************************************************* -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5ea - -# If SQLITE_ENABLE_FTS5 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -proc do_syntax_error_test {tn expr err} { - set ::se_expr $expr - do_catchsql_test $tn {SELECT fts5_expr($se_expr)} [list 1 $err] -} - -proc do_syntax_test {tn expr res} { - set ::se_expr $expr - do_execsql_test $tn {SELECT fts5_expr($se_expr)} [list $res] -} - -foreach {tn expr res} { - 1 {abc} {"abc"} - 2 {abc def} {"abc" AND "def"} - 3 {abc*} {"abc" *} - 4 {"abc def ghi" *} {"abc" + "def" + "ghi" *} - 5 {one AND two} {"one" AND "two"} - 6 {one+two} {"one" + "two"} - 7 {one AND two OR three} {("one" AND "two") OR "three"} - 8 {one OR two AND three} {"one" OR ("two" AND "three")} - 9 {NEAR(one two)} {NEAR("one" "two", 10)} - 10 {NEAR("one three"* two, 5)} {NEAR("one" + "three" * "two", 5)} -} { - do_execsql_test 1.$tn {SELECT fts5_expr($expr)} [list $res] -} - -foreach {tn expr res} { - 1 {c1:abc} - {c1 : "abc"} - 2 {c2 : NEAR(one two) c1:"hello world"} - {c2 : NEAR("one" "two", 10) AND c1 : "hello" + "world"} -} { - do_execsql_test 2.$tn {SELECT fts5_expr($expr, 'c1', 'c2')} [list $res] -} - -breakpoint -foreach {tn expr err} { - 1 {AND} {fts5: syntax error near "AND"} - 2 {abc def AND} {fts5: syntax error near ""} - 3 {abc OR AND} {fts5: syntax error near "AND"} - 4 {(a OR b) abc} {fts5: syntax error near "abc"} - 5 {NEaR (a b)} {fts5: syntax error near "NEaR"} - 6 {(a OR b) NOT c)} {fts5: syntax error near ")"} - 7 {nosuch: a nosuch2: b} {no such column: nosuch} - 8 {addr: a nosuch2: b} {no such column: nosuch2} -} { - do_catchsql_test 3.$tn {SELECT fts5_expr($expr, 'name', 'addr')} [list 1 $err] -} - - - -# do_syntax_error_test 1.0 {NOT} {syntax error near "NOT"} - - - -# do_catchsql_test 1.1 { - # SELECT fts5_expr('a OR b NOT c') -#} {0 {"a" OR "b" NOT "c"}} - - -#do_execsql_test 1.0 { SELECT fts5_expr('a') } {{"a"}} - -finish_test DELETED ext/fts5/test/fts5fault1.test Index: ext/fts5/test/fts5fault1.test ================================================================== --- ext/fts5/test/fts5fault1.test +++ /dev/null @@ -1,112 +0,0 @@ -# 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] -source $testdir/malloc_common.tcl -set testprefix fts5fault1 - -# If SQLITE_ENABLE_FTS3 is defined, omit this file. -ifcapable !fts5 { - finish_test - return -} - -# Simple tests: -# -# 1: CREATE VIRTUAL TABLE -# 2: INSERT statement -# 3: DELETE statement -# 4: MATCH expressions -# - -if 1 { - -faultsim_save_and_close -do_faultsim_test 1 -prep { - faultsim_restore_and_reopen -} -body { - execsql { CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3') } -} -test { - faultsim_test_result {0 {}} -} - -reset_db -do_execsql_test 2.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3'); -} -faultsim_save_and_close -do_faultsim_test 2 -prep { - faultsim_restore_and_reopen -} -body { - execsql { - INSERT INTO t1 VALUES('a b c', 'a bc def ghij klmno'); - } -} -test { - faultsim_test_result {0 {}} -} - -reset_db -do_execsql_test 3.0 { - CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix='1, 2, 3'); - INSERT INTO t1 VALUES('a b c', 'a bc def ghij klmno'); -} -faultsim_save_and_close -do_faultsim_test 3 -prep { - faultsim_restore_and_reopen -} -body { - execsql { DELETE FROM t1 } -} -test { - faultsim_test_result {0 {}} -} - -} - -reset_db -do_execsql_test 4.0 { - CREATE VIRTUAL TABLE t2 USING fts5(a, b); - INSERT INTO t2 VALUES('m f a jj th q jr ar', 'hj n h h sg j i m'); - INSERT INTO t2 VALUES('nr s t g od j kf h', 'sb h aq rg op rb n nl'); - INSERT INTO t2 VALUES('do h h pb p p q fr', 'c rj qs or cr a l i'); - INSERT INTO t2 VALUES('lk gp t i lq mq qm p', 'h mr g f op ld aj h'); - INSERT INTO t2 VALUES('ct d sq kc qi k f j', 'sn gh c of g s qt q'); - INSERT INTO t2 VALUES('d ea d d om mp s ab', 'dm hg l df cm ft pa c'); - INSERT INTO t2 VALUES('tc dk c jn n t sr ge', 'a a kn bc n i af h'); - INSERT INTO t2 VALUES('ie ii d i b sa qo rf', 'a h m aq i b m fn'); - INSERT INTO t2 VALUES('gs r fo a er m h li', 'tm c p gl eb ml q r'); - INSERT INTO t2 VALUES('k fe fd rd a gi ho kk', 'ng m c r d ml rm r'); -} -faultsim_save_and_close - -foreach {tn expr res} { - 1 { dk } 7 - 2 { m f } 1 - 3 { f* } {10 9 8 6 5 4 3 1} - 4 { m OR f } {10 9 8 5 4 1} - 5 { sn + gh } {5} - 6 { "sn gh" } {5} - 7 { NEAR(r a, 5) } {9} - 8 { m* f* } {10 9 8 6 4 1} - 9 { m* + f* } {8 1} -} { - do_faultsim_test 4.$tn -prep { - faultsim_restore_and_reopen - } -body " - execsql { SELECT rowid FROM t2 WHERE t2 MATCH '$expr' } - " -test " - faultsim_test_result {[list 0 $res]} - " -} - -finish_test - DELETED ext/fts5/test/fts5near.test Index: ext/fts5/test/fts5near.test ================================================================== --- ext/fts5/test/fts5near.test +++ /dev/null @@ -1,65 +0,0 @@ -# 2014 Jan 08 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# -# Tests focused on the NEAR operator. -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5near - -proc do_near_test {tn doc near res} { - uplevel [list do_execsql_test $tn " - DELETE FROM t1; - INSERT INTO t1 VALUES('$doc'); - SELECT count(*) FROM t1 WHERE t1 MATCH '$near'; - " $res] -} - -execsql { - CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'ascii tokenchars .') -} - -do_near_test 1.1 ". . a . . . b . ." { NEAR(a b, 5) } 1 -do_near_test 1.2 ". . a . . . b . ." { NEAR(a b, 4) } 1 -do_near_test 1.3 ". . a . . . b . ." { NEAR(a b, 3) } 1 -do_near_test 1.4 ". . a . . . b . ." { NEAR(a b, 2) } 0 - -do_near_test 1.5 ". . a . . . b . ." { NEAR(b a, 5) } 1 -do_near_test 1.6 ". . a . . . b . ." { NEAR(b a, 4) } 1 -do_near_test 1.7 ". . a . . . b . ." { NEAR(b a, 3) } 1 -do_near_test 1.8 ". . a . . . b . ." { NEAR(b a, 2) } 0 - -do_near_test 1.9 ". a b . . . c . ." { NEAR("a b" c, 3) } 1 -do_near_test 1.10 ". a b . . . c . ." { NEAR("a b" c, 2) } 0 -do_near_test 1.11 ". a b . . . c . ." { NEAR(c "a b", 3) } 1 -do_near_test 1.12 ". a b . . . c . ." { NEAR(c "a b", 2) } 0 - -do_near_test 1.13 ". a b . . . c d ." { NEAR(a+b c+d, 3) } 1 -do_near_test 1.14 ". a b . . . c d ." { NEAR(a+b c+d, 2) } 0 -do_near_test 1.15 ". a b . . . c d ." { NEAR(c+d a+b, 3) } 1 -do_near_test 1.16 ". a b . . . c d ." { NEAR(c+d a+b, 2) } 0 - -do_near_test 1.17 ". a b . . . c d ." { NEAR(a b c d, 5) } 1 -do_near_test 1.18 ". a b . . . c d ." { NEAR(a b c d, 4) } 0 -do_near_test 1.19 ". a b . . . c d ." { NEAR(a+b c d, 4) } 1 - -do_near_test 1.20 "a b c d e f g h i" { NEAR(b+c a+b+c+d i, 5) } 1 -do_near_test 1.21 "a b c d e f g h i" { NEAR(b+c a+b+c+d i, 4) } 0 - -do_near_test 1.22 "a b c d e f g h i" { NEAR(a+b+c+d i b+c, 5) } 1 -do_near_test 1.23 "a b c d e f g h i" { NEAR(a+b+c+d i b+c, 4) } 0 - -do_near_test 1.24 "a b c d e f g h i" { NEAR(i a+b+c+d b+c, 5) } 1 -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 - DELETED ext/fts5/test/fts5optimize.test Index: ext/fts5/test/fts5optimize.test ================================================================== --- ext/fts5/test/fts5optimize.test +++ /dev/null @@ -1,60 +0,0 @@ -# 2014 Dec 20 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5optimize - -proc rnddoc {nWord} { - set vocab {a b c d e f g h i j k l m n o p q r s t u v w x y z} - set nVocab [llength $vocab] - set ret [list] - for {set i 0} {$i < $nWord} {incr i} { - lappend ret [lindex $vocab [expr {int(rand() * $nVocab)}]] - } - return $ret -} - - -foreach {tn nStep} { - 1 2 - 2 10 - 3 50 - 4 500 -} { -if {$tn!=4} continue - reset_db - db func rnddoc rnddoc - do_execsql_test 1.$tn.1 { - CREATE VIRTUAL TABLE t1 USING fts5(x, y); - } - do_test 1.$tn.2 { - for {set i 0} {$i < $nStep} {incr i} { - execsql { INSERT INTO t1 VALUES( rnddoc(5), rnddoc(5) ) } - } - } {} - - do_execsql_test 1.$tn.3 { - INSERT INTO t1(t1) VALUES('integrity-check'); - } - - do_execsql_test 1.$tn.4 { - INSERT INTO t1(t1) VALUES('optimize'); - } - - do_execsql_test 1.$tn.5 { - INSERT INTO t1(t1) VALUES('integrity-check'); - } -} - -finish_test - DELETED ext/fts5/test/fts5porter.test Index: ext/fts5/test/fts5porter.test ================================================================== --- ext/fts5/test/fts5porter.test +++ /dev/null @@ -1,11800 +0,0 @@ -# 2014 Dec 20 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# May you do good and not evil. -# May you find forgiveness for yourself and forgive others. -# May you share freely, never taking more than you give. -# -#*********************************************************************** -# -# Tests focusing on the fts5 porter stemmer implementation. -# -# http://tartarus.org/martin/PorterStemmer/ -# - -source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5porter - -set test_vocab { - a a aaron aaron - abaissiez abaissiez abandon abandon - abandoned abandon abase abas - abash abash abate abat - abated abat abatement abat - abatements abat abates abat - abbess abbess abbey abbei - abbeys abbei abbominable abbomin - abbot abbot abbots abbot - abbreviated abbrevi abed ab - abel abel aberga aberga - abergavenny abergavenni abet abet - abetting abet abhominable abhomin - abhor abhor abhorr abhorr - abhorred abhor abhorring abhor - abhors abhor abhorson abhorson - abide abid abides abid - abilities abil ability abil - abject abject abjectly abjectli - abjects abject abjur abjur - abjure abjur able abl - abler abler aboard aboard - abode abod aboded abod - abodements abod aboding abod - abominable abomin abominably abomin - abominations abomin abortive abort - abortives abort abound abound - abounding abound about about - above abov abr abr - abraham abraham abram abram - abreast abreast abridg abridg - abridge abridg abridged abridg - abridgment abridg abroach abroach - abroad abroad abrogate abrog - abrook abrook abrupt abrupt - abruption abrupt abruptly abruptli - absence absenc absent absent - absey absei absolute absolut - absolutely absolut absolv absolv - absolver absolv abstains abstain - abstemious abstemi abstinence abstin - abstract abstract absurd absurd - absyrtus absyrtu abundance abund - abundant abund abundantly abundantli - abus abu abuse abus - abused abus abuser abus - abuses abus abusing abus - abutting abut aby abi - abysm abysm ac ac - academe academ academes academ - accent accent accents accent - accept accept acceptable accept - acceptance accept accepted accept - accepts accept access access - accessary accessari accessible access - accidence accid accident accid - accidental accident accidentally accident - accidents accid accite accit - accited accit accites accit - acclamations acclam accommodate accommod - accommodated accommod accommodation accommod - accommodations accommod accommodo accommodo - accompanied accompani accompany accompani - accompanying accompani accomplices accomplic - accomplish accomplish accomplished accomplish - accomplishing accomplish accomplishment accomplish - accompt accompt accord accord - accordant accord accorded accord - accordeth accordeth according accord - accordingly accordingli accords accord - accost accost accosted accost - account account accountant account - accounted account accounts account - accoutred accoutr accoutrement accoutr - accoutrements accoutr accrue accru - accumulate accumul accumulated accumul - accumulation accumul accurs accur - accursed accurs accurst accurst - accus accu accusation accus - accusations accus accusative accus - accusativo accusativo accuse accus - accused accus accuser accus - accusers accus accuses accus - accuseth accuseth accusing accus - accustom accustom accustomed accustom - ace ac acerb acerb - ache ach acheron acheron - aches ach achiev achiev - achieve achiev achieved achiev - achievement achiev achievements achiev - achiever achiev achieves achiev - achieving achiev achilles achil - aching ach achitophel achitophel - acknowledg acknowledg acknowledge acknowledg - acknowledged acknowledg acknowledgment acknowledg - acknown acknown acold acold - aconitum aconitum acordo acordo - acorn acorn acquaint acquaint - acquaintance acquaint acquainted acquaint - acquaints acquaint acquir acquir - acquire acquir acquisition acquisit - acquit acquit acquittance acquitt - acquittances acquitt acquitted acquit - acre acr acres acr - across across act act - actaeon actaeon acted act - acting act action action - actions action actium actium - active activ actively activ - activity activ actor actor - actors actor acts act - actual actual acture actur - acute acut acutely acut - ad ad adage adag - adallas adalla adam adam - adamant adam add add - added ad adder adder - adders adder addeth addeth - addict addict addicted addict - addiction addict adding ad - addition addit additions addit - addle addl address address - addressing address addrest addrest - adds add adhere adher - adheres adher adieu adieu - adieus adieu adjacent adjac - adjoin adjoin adjoining adjoin - adjourn adjourn adjudg adjudg - adjudged adjudg adjunct adjunct - administer administ administration administr - admir admir admirable admir - admiral admir admiration admir - admire admir admired admir - admirer admir admiring admir - admiringly admiringli admission admiss - admit admit admits admit - admittance admitt admitted admit - admitting admit admonish admonish - admonishing admonish admonishment admonish - admonishments admonish admonition admonit - ado ado adonis adoni - adopt adopt adopted adopt - adoptedly adoptedli adoption adopt - adoptious adopti adopts adopt - ador ador adoration ador - adorations ador adore ador - adorer ador adores ador - adorest adorest adoreth adoreth - adoring ador adorn adorn - adorned adorn adornings adorn - adornment adorn adorns adorn - adown adown adramadio adramadio - adrian adrian adriana adriana - adriano adriano adriatic adriat - adsum adsum adulation adul - adulterate adulter adulterates adulter - adulterers adulter adulteress adulteress - adulteries adulteri adulterous adulter - adultery adulteri adultress adultress - advanc advanc advance advanc - advanced advanc advancement advanc - advancements advanc advances advanc - advancing advanc advantage advantag - advantageable advantag advantaged advantag - advantageous advantag advantages advantag - advantaging advantag advent advent - adventur adventur adventure adventur - adventures adventur adventuring adventur - adventurous adventur adventurously adventur - adversaries adversari adversary adversari - adverse advers adversely advers - adversities advers adversity advers - advertis adverti advertise advertis - advertised advertis advertisement advertis - advertising advertis advice advic - advis advi advise advis - advised advis advisedly advisedli - advises advis advisings advis - advocate advoc advocation advoc - aeacida aeacida aeacides aeacid - aedile aedil aediles aedil - aegeon aegeon aegion aegion - aegles aegl aemelia aemelia - aemilia aemilia aemilius aemiliu - aeneas aenea aeolus aeolu - aer aer aerial aerial - aery aeri aesculapius aesculapiu - aeson aeson aesop aesop - aetna aetna afar afar - afear afear afeard afeard - affability affabl affable affabl - affair affair affaire affair - affairs affair affect affect - affectation affect affectations affect - affected affect affectedly affectedli - affecteth affecteth affecting affect - affection affect affectionate affection - affectionately affection affections affect - affects affect affeer affeer - affianc affianc affiance affianc - affianced affianc affied affi - affin affin affined affin - affinity affin affirm affirm - affirmation affirm affirmatives affirm - afflict afflict afflicted afflict - affliction afflict afflictions afflict - afflicts afflict afford afford - affordeth affordeth affords afford - affray affrai affright affright - affrighted affright affrights affright - affront affront affronted affront - affy affi afield afield - afire afir afloat afloat - afoot afoot afore afor - aforehand aforehand aforesaid aforesaid - afraid afraid afresh afresh - afric afric africa africa - african african afront afront - after after afternoon afternoon - afterward afterward afterwards afterward - ag ag again again - against against agamemmon agamemmon - agamemnon agamemnon agate agat - agaz agaz age ag - aged ag agenor agenor - agent agent agents agent - ages ag aggravate aggrav - aggrief aggrief agile agil - agincourt agincourt agitation agit - aglet aglet agnize agniz - ago ago agone agon - agony agoni agree agre - agreed agre agreeing agre - agreement agreement agrees agre - agrippa agrippa aground aground - ague agu aguecheek aguecheek - agued agu agueface aguefac - agues agu ah ah - aha aha ahungry ahungri - ai ai aialvolio aialvolio - aiaria aiaria aid aid - aidance aidanc aidant aidant - aided aid aiding aid - aidless aidless aids aid - ail ail aim aim - aimed aim aimest aimest - aiming aim aims aim - ainsi ainsi aio aio - air air aired air - airless airless airs air - airy airi ajax ajax - akilling akil al al - alabaster alabast alack alack - alacrity alacr alarbus alarbu - alarm alarm alarms alarm - alarum alarum alarums alarum - alas ala alb alb - alban alban albans alban - albany albani albeit albeit - albion albion alchemist alchemist - alchemy alchemi alcibiades alcibiad - alcides alcid alder alder - alderman alderman aldermen aldermen - ale al alecto alecto - alehouse alehous alehouses alehous - alencon alencon alengon alengon - aleppo aleppo ales al - alewife alewif alexander alexand - alexanders alexand alexandria alexandria - alexandrian alexandrian alexas alexa - alias alia alice alic - alien alien aliena aliena - alight alight alighted alight - alights alight aliis alii - alike alik alisander alisand - alive aliv all all - alla alla allay allai - allayed allai allaying allai - allayment allay allayments allay - allays allai allegation alleg - allegations alleg allege alleg - alleged alleg allegiance allegi - allegiant allegi alley allei - alleys allei allhallowmas allhallowma - alliance allianc allicholy allicholi - allied alli allies alli - alligant allig alligator allig - allons allon allot allot - allots allot allotted allot - allottery allotteri allow allow - allowance allow allowed allow - allowing allow allows allow - allur allur allure allur - allurement allur alluring allur - allusion allus ally alli - allycholly allycholli almain almain - almanac almanac almanack almanack - almanacs almanac almighty almighti - almond almond almost almost - alms alm almsman almsman - aloes alo aloft aloft - alone alon along along - alonso alonso aloof aloof - aloud aloud alphabet alphabet - alphabetical alphabet alphonso alphonso - alps alp already alreadi - also also alt alt - altar altar altars altar - alter alter alteration alter - altered alter alters alter - althaea althaea although although - altitude altitud altogether altogeth - alton alton alway alwai - always alwai am am - amaimon amaimon amain amain - amaking amak amamon amamon - amaz amaz amaze amaz - amazed amaz amazedly amazedli - amazedness amazed amazement amaz - amazes amaz amazeth amazeth - amazing amaz amazon amazon - amazonian amazonian amazons amazon - ambassador ambassador ambassadors ambassador - amber amber ambiguides ambiguid - ambiguities ambigu ambiguous ambigu - ambition ambit ambitions ambit - ambitious ambiti ambitiously ambiti - amble ambl ambled ambl - ambles ambl ambling ambl - ambo ambo ambuscadoes ambuscado - ambush ambush amen amen - amend amend amended amend - amendment amend amends amend - amerce amerc america america - ames am amiable amiabl - amid amid amidst amidst - amiens amien amis ami - amiss amiss amities amiti - amity amiti amnipotent amnipot - among among amongst amongst - amorous amor amorously amor - amort amort amount amount - amounts amount amour amour - amphimacus amphimacu ample ampl - ampler ampler amplest amplest - amplified amplifi amplify amplifi - amply ampli ampthill ampthil - amurath amurath amyntas amynta - an an anatomiz anatomiz - anatomize anatom anatomy anatomi - ancestor ancestor ancestors ancestor - ancestry ancestri anchises anchis - anchor anchor anchorage anchorag - anchored anchor anchoring anchor - anchors anchor anchovies anchovi - ancient ancient ancientry ancientri - ancients ancient ancus ancu - and and andirons andiron - andpholus andpholu andren andren - andrew andrew andromache andromach - andronici andronici andronicus andronicu - anew anew ang ang - angel angel angelica angelica - angelical angel angelo angelo - angels angel anger anger - angerly angerli angers anger - anges ang angiers angier - angl angl anglais anglai - angle angl angler angler - angleterre angleterr angliae anglia - angling angl anglish anglish - angrily angrili angry angri - anguish anguish angus angu - animal anim animals anim - animis animi anjou anjou - ankle ankl anna anna - annals annal anne ann - annex annex annexed annex - annexions annexion annexment annex - annothanize annothan announces announc - annoy annoi annoyance annoy - annoying annoi annual annual - anoint anoint anointed anoint - anon anon another anoth - anselmo anselmo answer answer - answerable answer answered answer - answerest answerest answering answer - answers answer ant ant - ante ant antenor antenor - antenorides antenorid anteroom anteroom - anthem anthem anthems anthem - anthony anthoni anthropophagi anthropophagi - anthropophaginian anthropophaginian antiates antiat - antic antic anticipate anticip - anticipates anticip anticipatest anticipatest - anticipating anticip anticipation anticip - antick antick anticly anticli - antics antic antidote antidot - antidotes antidot antigonus antigonu - antiopa antiopa antipathy antipathi - antipholus antipholu antipholuses antipholus - antipodes antipod antiquary antiquari - antique antiqu antiquity antiqu - antium antium antoniad antoniad - antonio antonio antonius antoniu - antony antoni antres antr - anvil anvil any ani - anybody anybodi anyone anyon - anything anyth anywhere anywher - ap ap apace apac - apart apart apartment apart - apartments apart ape ap - apemantus apemantu apennines apennin - apes ap apiece apiec - apish apish apollinem apollinem - apollo apollo apollodorus apollodoru - apology apolog apoplex apoplex - apoplexy apoplexi apostle apostl - apostles apostl apostrophas apostropha - apoth apoth apothecary apothecari - appal appal appall appal - appalled appal appals appal - apparel apparel apparell apparel - apparelled apparel apparent appar - apparently appar apparition apparit - apparitions apparit appeach appeach - appeal appeal appeals appeal - appear appear appearance appear - appeared appear appeareth appeareth - appearing appear appears appear - appeas appea appease appeas - appeased appeas appelant appel - appele appel appelee appele - appeles appel appelez appelez - appellant appel appellants appel - appelons appelon appendix appendix - apperil apperil appertain appertain - appertaining appertain appertainings appertain - appertains appertain appertinent appertin - appertinents appertin appetite appetit - appetites appetit applaud applaud - applauded applaud applauding applaud - applause applaus applauses applaus - apple appl apples appl - appletart appletart appliance applianc - appliances applianc applications applic - applied appli applies appli - apply appli applying appli - appoint appoint appointed appoint - appointment appoint appointments appoint - appoints appoint apprehend apprehend - apprehended apprehend apprehends apprehend - apprehension apprehens apprehensions apprehens - apprehensive apprehens apprendre apprendr - apprenne apprenn apprenticehood apprenticehood - appris appri approach approach - approachers approach approaches approach - approacheth approacheth approaching approach - approbation approb approof approof - appropriation appropri approv approv - approve approv approved approv - approvers approv approves approv - appurtenance appurten appurtenances appurten - apricocks apricock april april - apron apron aprons apron - apt apt apter apter - aptest aptest aptly aptli - aptness apt aqua aqua - aquilon aquilon aquitaine aquitain - arabia arabia arabian arabian - araise arais arbitrate arbitr - arbitrating arbitr arbitrator arbitr - arbitrement arbitr arbors arbor - arbour arbour arc arc - arch arch archbishop archbishop - archbishopric archbishopr archdeacon archdeacon - arched arch archelaus archelau - archer archer archers archer - archery archeri archibald archibald - archidamus archidamu architect architect - arcu arcu arde ard - arden arden ardent ardent - ardour ardour are ar - argal argal argier argier - argo argo argosies argosi - argosy argosi argu argu - argue argu argued argu - argues argu arguing argu - argument argument arguments argument - argus argu ariachne ariachn - ariadne ariadn ariel ariel - aries ari aright aright - arinado arinado arinies arini - arion arion arise aris - arises aris ariseth ariseth - arising aris aristode aristod - aristotle aristotl arithmetic arithmet - arithmetician arithmetician ark ark - arm arm arma arma - armado armado armadoes armado - armagnac armagnac arme arm - armed arm armenia armenia - armies armi armigero armigero - arming arm armipotent armipot - armor armor armour armour - armourer armour armourers armour - armours armour armoury armouri - arms arm army armi - arn arn aroint aroint - arose aros arouse arous - aroused arous arragon arragon - arraign arraign arraigned arraign - arraigning arraign arraignment arraign - arrant arrant arras arra - array arrai arrearages arrearag - arrest arrest arrested arrest - arrests arrest arriv arriv - arrival arriv arrivance arriv - arrive arriv arrived arriv - arrives arriv arriving arriv - arrogance arrog arrogancy arrog - arrogant arrog arrow arrow - arrows arrow art art - artemidorus artemidoru arteries arteri - arthur arthur article articl - articles articl articulate articul - artificer artific artificial artifici - artillery artilleri artire artir - artist artist artists artist - artless artless artois artoi - arts art artus artu - arviragus arviragu as as - asaph asaph ascanius ascaniu - ascend ascend ascended ascend - ascendeth ascendeth ascends ascend - ascension ascens ascent ascent - ascribe ascrib ascribes ascrib - ash ash asham asham - ashamed asham asher asher - ashes ash ashford ashford - ashore ashor ashouting ashout - ashy ashi asia asia - aside asid ask ask - askance askanc asked ask - asker asker asketh asketh - asking ask asks ask - aslant aslant asleep asleep - asmath asmath asp asp - aspect aspect aspects aspect - aspen aspen aspersion aspers - aspic aspic aspicious aspici - aspics aspic aspir aspir - aspiration aspir aspire aspir - aspiring aspir asquint asquint - ass ass assail assail - assailable assail assailant assail - assailants assail assailed assail - assaileth assaileth assailing assail - assails assail assassination assassin - assault assault assaulted assault - assaults assault assay assai - assaying assai assays assai - assemblance assembl assemble assembl - assembled assembl assemblies assembl - assembly assembl assent assent - asses ass assez assez - assign assign assigned assign - assigns assign assinico assinico - assist assist assistance assist - assistances assist assistant assist - assistants assist assisted assist - assisting assist associate associ - associated associ associates associ - assuage assuag assubjugate assubjug - assum assum assume assum - assumes assum assumption assumpt - assur assur assurance assur - assure assur assured assur - assuredly assuredli assures assur - assyrian assyrian astonish astonish - astonished astonish astraea astraea - astray astrai astrea astrea - astronomer astronom astronomers astronom - astronomical astronom astronomy astronomi - asunder asund at at - atalanta atalanta ate at - ates at athenian athenian - athenians athenian athens athen - athol athol athversary athversari - athwart athwart atlas atla - atomies atomi atomy atomi - atone aton atonement aton - atonements aton atropos atropo - attach attach attached attach - attachment attach attain attain - attainder attaind attains attain - attaint attaint attainted attaint - attainture attaintur attempt attempt - attemptable attempt attempted attempt - attempting attempt attempts attempt - attend attend attendance attend - attendant attend attendants attend - attended attend attendents attend - attendeth attendeth attending attend - attends attend attent attent - attention attent attentive attent - attentivenes attentiven attest attest - attested attest attir attir - attire attir attired attir - attires attir attorney attornei - attorneyed attornei attorneys attornei - attorneyship attorneyship attract attract - attraction attract attractive attract - attracts attract attribute attribut - attributed attribut attributes attribut - attribution attribut attributive attribut - atwain atwain au au - aubrey aubrei auburn auburn - aucun aucun audacious audaci - audaciously audaci audacity audac - audible audibl audience audienc - audis audi audit audit - auditor auditor auditors auditor - auditory auditori audre audr - audrey audrei aufidius aufidiu - aufidiuses aufidius auger auger - aught aught augment augment - augmentation augment augmented augment - augmenting augment augurer augur - augurers augur augures augur - auguring augur augurs augur - augury auguri august august - augustus augustu auld auld - aumerle aumerl aunchient aunchient - aunt aunt aunts aunt - auricular auricular aurora aurora - auspicious auspici aussi aussi - austere auster austerely auster - austereness auster austerity auster - austria austria aut aut - authentic authent author author - authorities author authority author - authorized author authorizing author - authors author autolycus autolycu - autre autr autumn autumn - auvergne auvergn avail avail - avails avail avarice avaric - avaricious avarici avaunt avaunt - ave av aveng aveng - avenge aveng avenged aveng - averring aver avert avert - aves av avez avez - avis avi avoid avoid - avoided avoid avoiding avoid - avoids avoid avoirdupois avoirdupoi - avouch avouch avouched avouch - avouches avouch avouchment avouch - avow avow aw aw - await await awaits await - awak awak awake awak - awaked awak awaken awaken - awakened awaken awakens awaken - awakes awak awaking awak - award award awards award - awasy awasi away awai - awe aw aweary aweari - aweless aweless awful aw - awhile awhil awkward awkward - awl awl awooing awoo - awork awork awry awri - axe ax axle axl - axletree axletre ay ay - aye ay ayez ayez - ayli ayli azur azur - azure azur b b - ba ba baa baa - babbl babbl babble babbl - babbling babbl babe babe - babes babe babies babi - baboon baboon baboons baboon - baby babi babylon babylon - bacare bacar bacchanals bacchan - bacchus bacchu bach bach - bachelor bachelor bachelors bachelor - back back backbite backbit - backbitten backbitten backing back - backs back backward backward - backwardly backwardli backwards backward - bacon bacon bacons bacon - bad bad bade bade - badge badg badged badg - badges badg badly badli - badness bad baes bae - baffl baffl baffle baffl - baffled baffl bag bag - baggage baggag bagot bagot - bagpipe bagpip bags bag - bail bail bailiff bailiff - baillez baillez baily baili - baisant baisant baisees baise - baiser baiser bait bait - baited bait baiting bait - baitings bait baits bait - bajazet bajazet bak bak - bake bake baked bake - baker baker bakers baker - bakes bake baking bake - bal bal balanc balanc - balance balanc balcony balconi - bald bald baldrick baldrick - bale bale baleful bale - balk balk ball ball - ballad ballad ballads ballad - ballast ballast ballasting ballast - ballet ballet ballow ballow - balls ball balm balm - balms balm balmy balmi - balsam balsam balsamum balsamum - balth balth balthasar balthasar - balthazar balthazar bames bame - ban ban banbury banburi - band band bandied bandi - banding band bandit bandit - banditti banditti banditto banditto - bands band bandy bandi - bandying bandi bane bane - banes bane bang bang - bangor bangor banish banish - banished banish banishers banish - banishment banish banister banist - bank bank bankrout bankrout - bankrupt bankrupt bankrupts bankrupt - banks bank banner banner - bannerets banneret banners banner - banning ban banns bann - banquet banquet banqueted banquet - banqueting banquet banquets banquet - banquo banquo bans ban - baptism baptism baptista baptista - baptiz baptiz bar bar - barbarian barbarian barbarians barbarian - barbarism barbar barbarous barbar - barbary barbari barbason barbason - barbed barb barber barber - barbermonger barbermong bard bard - bardolph bardolph bards bard - bare bare bared bare - barefac barefac barefaced barefac - barefoot barefoot bareheaded barehead - barely bare bareness bare - barful bar bargain bargain - bargains bargain barge barg - bargulus bargulu baring bare - bark bark barking bark - barkloughly barkloughli barks bark - barky barki barley barlei - barm barm barn barn - barnacles barnacl barnardine barnardin - barne barn barnes barn - barnet barnet barns barn - baron baron barons baron - barony baroni barr barr - barrabas barraba barrel barrel - barrels barrel barren barren - barrenly barrenli barrenness barren - barricado barricado barricadoes barricado - barrow barrow bars bar - barson barson barter barter - bartholomew bartholomew bas ba - basan basan base base - baseless baseless basely base - baseness base baser baser - bases base basest basest - bashful bash bashfulness bash - basilisco basilisco basilisk basilisk - basilisks basilisk basimecu basimecu - basin basin basingstoke basingstok - basins basin basis basi - bask bask basket basket - baskets basket bass bass - bassanio bassanio basset basset - bassianus bassianu basta basta - bastard bastard bastardizing bastard - bastardly bastardli bastards bastard - bastardy bastardi basted bast - bastes bast bastinado bastinado - basting bast bat bat - batailles batail batch batch - bate bate bated bate - bates bate bath bath - bathe bath bathed bath - bathing bath baths bath - bating bate batler batler - bats bat batt batt - battalia battalia battalions battalion - batten batten batter batter - battering batter batters batter - battery batteri battle battl - battled battl battlefield battlefield - battlements battlement battles battl - batty batti bauble baubl - baubles baubl baubling baubl - baulk baulk bavin bavin - bawcock bawcock bawd bawd - bawdry bawdri bawds bawd - bawdy bawdi bawl bawl - bawling bawl bay bai - baying bai baynard baynard - bayonne bayonn bays bai - be be beach beach - beached beach beachy beachi - beacon beacon bead bead - beaded bead beadle beadl - beadles beadl beads bead - beadsmen beadsmen beagle beagl - beagles beagl beak beak - beaks beak beam beam - beamed beam beams beam - bean bean beans bean - bear bear beard beard - bearded beard beardless beardless - beards beard bearer bearer - bearers bearer bearest bearest - beareth beareth bearing bear - bears bear beast beast - beastliest beastliest beastliness beastli - beastly beastli beasts beast - beat beat beated beat - beaten beaten beating beat - beatrice beatric beats beat - beau beau beaufort beaufort - beaumond beaumond beaumont beaumont - beauteous beauteou beautied beauti - beauties beauti beautified beautifi - beautiful beauti beautify beautifi - beauty beauti beaver beaver - beavers beaver became becam - because becaus bechanc bechanc - bechance bechanc bechanced bechanc - beck beck beckon beckon - beckons beckon becks beck - becom becom become becom - becomed becom becomes becom - becoming becom becomings becom - bed bed bedabbled bedabbl - bedash bedash bedaub bedaub - bedazzled bedazzl bedchamber bedchamb - bedclothes bedcloth bedded bed - bedeck bedeck bedecking bedeck - bedew bedew bedfellow bedfellow - bedfellows bedfellow bedford bedford - bedlam bedlam bedrench bedrench - bedrid bedrid beds bed - bedtime bedtim bedward bedward - bee bee beef beef - beefs beef beehives beehiv - been been beer beer - bees bee beest beest - beetle beetl beetles beetl - beeves beev befall befal - befallen befallen befalls befal - befell befel befits befit - befitted befit befitting befit - befor befor before befor - beforehand beforehand befortune befortun - befriend befriend befriended befriend - befriends befriend beg beg - began began beget beget - begets beget begetting beget - begg begg beggar beggar - beggared beggar beggarly beggarli - beggarman beggarman beggars beggar - beggary beggari begging beg - begin begin beginners beginn - beginning begin beginnings begin - begins begin begnawn begnawn - begone begon begot begot - begotten begotten begrimed begrim - begs beg beguil beguil - beguile beguil beguiled beguil - beguiles beguil beguiling beguil - begun begun behalf behalf - behalfs behalf behav behav - behaved behav behavedst behavedst - behavior behavior behaviors behavior - behaviour behaviour behaviours behaviour - behead behead beheaded behead - beheld beheld behest behest - behests behest behind behind - behold behold beholder behold - beholders behold beholdest beholdest - beholding behold beholds behold - behoof behoof behooffull behoofful - behooves behoov behove behov - behoves behov behowls behowl - being be bel bel - belarius belariu belch belch - belching belch beldam beldam - beldame beldam beldams beldam - belee bele belgia belgia - belie beli belied beli - belief belief beliest beliest - believ believ believe believ - believed believ believes believ - believest believest believing believ - belike belik bell bell - bellario bellario belle bell - bellied belli bellies belli - bellman bellman bellona bellona - bellow bellow bellowed bellow - bellowing bellow bellows bellow - bells bell belly belli - bellyful belly belman belman - belmont belmont belock belock - belong belong belonging belong - belongings belong belongs belong - belov belov beloved belov - beloving belov below below - belt belt belzebub belzebub - bemadding bemad bemet bemet - bemete bemet bemoan bemoan - bemoaned bemoan bemock bemock - bemoil bemoil bemonster bemonst - ben ben bench bench - bencher bencher benches bench - bend bend bended bend - bending bend bends bend - bene bene beneath beneath - benedicite benedicit benedick benedick - benediction benedict benedictus benedictu - benefactors benefactor benefice benefic - beneficial benefici benefit benefit - benefited benefit benefits benefit - benetted benet benevolence benevol - benevolences benevol benied beni - benison benison bennet bennet - bent bent bentii bentii - bentivolii bentivolii bents bent - benumbed benumb benvolio benvolio - bepaint bepaint bepray beprai - bequeath bequeath bequeathed bequeath - bequeathing bequeath bequest bequest - ber ber berard berard - berattle berattl beray berai - bere bere bereave bereav - bereaved bereav bereaves bereav - bereft bereft bergamo bergamo - bergomask bergomask berhym berhym - berhyme berhym berkeley berkelei - bermoothes bermooth bernardo bernardo - berod berod berowne berown - berri berri berries berri - berrord berrord berry berri - bertram bertram berwick berwick - bescreen bescreen beseech beseech - beseeched beseech beseechers beseech - beseeching beseech beseek beseek - beseem beseem beseemeth beseemeth - beseeming beseem beseems beseem - beset beset beshrew beshrew - beside besid besides besid - besieg besieg besiege besieg - besieged besieg beslubber beslubb - besmear besmear besmeared besmear - besmirch besmirch besom besom - besort besort besotted besot - bespake bespak bespeak bespeak - bespice bespic bespoke bespok - bespotted bespot bess bess - bessy bessi best best - bestained bestain bested best - bestial bestial bestir bestir - bestirr bestirr bestow bestow - bestowed bestow bestowing bestow - bestows bestow bestraught bestraught - bestrew bestrew bestrid bestrid - bestride bestrid bestrides bestrid - bet bet betake betak - beteem beteem bethink bethink - bethought bethought bethrothed bethroth - bethump bethump betid betid - betide betid betideth betideth - betime betim betimes betim - betoken betoken betook betook - betossed betoss betray betrai - betrayed betrai betraying betrai - betrays betrai betrims betrim - betroth betroth betrothed betroth - betroths betroth bett bett - betted bet better better - bettered better bettering better - betters better betting bet - bettre bettr between between - betwixt betwixt bevel bevel - beverage beverag bevis bevi - bevy bevi bewail bewail - bewailed bewail bewailing bewail - bewails bewail beware bewar - bewasted bewast beweep beweep - bewept bewept bewet bewet - bewhored bewhor bewitch bewitch - bewitched bewitch bewitchment bewitch - bewray bewrai beyond beyond - bezonian bezonian bezonians bezonian - bianca bianca bianco bianco - bias bia bibble bibbl - bickerings bicker bid bid - bidden bidden bidding bid - biddings bid biddy biddi - bide bide bides bide - biding bide bids bid - bien bien bier bier - bifold bifold big big - bigamy bigami biggen biggen - bigger bigger bigness big - bigot bigot bilberry bilberri - bilbo bilbo bilboes bilbo - bilbow bilbow bill bill - billeted billet billets billet - billiards billiard billing bill - billow billow billows billow - bills bill bin bin - bind bind bindeth bindeth - binding bind binds bind - biondello biondello birch birch - bird bird birding bird - birdlime birdlim birds bird - birnam birnam birth birth - birthday birthdai birthdom birthdom - birthplace birthplac birthright birthright - birthrights birthright births birth - bis bi biscuit biscuit - bishop bishop bishops bishop - bisson bisson bit bit - bitch bitch bite bite - biter biter bites bite - biting bite bits bit - bitt bitt bitten bitten - bitter bitter bitterest bitterest - bitterly bitterli bitterness bitter - blab blab blabb blabb - blabbing blab blabs blab - black black blackamoor blackamoor - blackamoors blackamoor blackberries blackberri - blackberry blackberri blacker blacker - blackest blackest blackfriars blackfriar - blackheath blackheath blackmere blackmer - blackness black blacks black - bladder bladder bladders bladder - blade blade bladed blade - blades blade blains blain - blam blam blame blame - blamed blame blameful blame - blameless blameless blames blame - blanc blanc blanca blanca - blanch blanch blank blank - blanket blanket blanks blank - blaspheme blasphem blaspheming blasphem - blasphemous blasphem blasphemy blasphemi - blast blast blasted blast - blasting blast blastments blastment - blasts blast blaz blaz - blaze blaze blazes blaze - blazing blaze blazon blazon - blazoned blazon blazoning blazon - bleach bleach bleaching bleach - bleak bleak blear blear - bleared blear bleat bleat - bleated bleat bleats bleat - bled bled bleed bleed - bleedest bleedest bleedeth bleedeth - bleeding bleed bleeds bleed - blemish blemish blemishes blemish - blench blench blenches blench - blend blend blended blend - blent blent bless bless - blessed bless blessedly blessedli - blessedness blessed blesses bless - blesseth blesseth blessing bless - blessings bless blest blest - blew blew blind blind - blinded blind blindfold blindfold - blinding blind blindly blindli - blindness blind blinds blind - blink blink blinking blink - bliss bliss blist blist - blister blister blisters blister - blithe blith blithild blithild - bloat bloat block block - blockish blockish blocks block - blois bloi blood blood - blooded blood bloodhound bloodhound - bloodied bloodi bloodier bloodier - bloodiest bloodiest bloodily bloodili - bloodless bloodless bloods blood - bloodshed bloodsh bloodshedding bloodshed - bloodstained bloodstain bloody bloodi - bloom bloom blooms bloom - blossom blossom blossoming blossom - blossoms blossom blot blot - blots blot blotted blot - blotting blot blount blount - blow blow blowed blow - blowers blower blowest blowest - blowing blow blown blown - blows blow blowse blows - blubb blubb blubber blubber - blubbering blubber blue blue - bluecaps bluecap bluest bluest - blunt blunt blunted blunt - blunter blunter bluntest bluntest - blunting blunt bluntly bluntli - bluntness blunt blunts blunt - blur blur blurr blurr - blurs blur blush blush - blushes blush blushest blushest - blushing blush blust blust - bluster bluster blusterer bluster - blusters bluster bo bo - boar boar board board - boarded board boarding board - boards board boarish boarish - boars boar boast boast - boasted boast boastful boast - boasting boast boasts boast - boat boat boats boat - boatswain boatswain bob bob - bobb bobb boblibindo boblibindo - bobtail bobtail bocchus bocchu - bode bode boded bode - bodements bodement bodes bode - bodg bodg bodied bodi - bodies bodi bodiless bodiless - bodily bodili boding bode - bodkin bodkin body bodi - bodykins bodykin bog bog - boggle boggl boggler boggler - bogs bog bohemia bohemia - bohemian bohemian bohun bohun - boil boil boiling boil - boils boil boist boist - boisterous boister boisterously boister - boitier boitier bold bold - bolden bolden bolder bolder - boldest boldest boldly boldli - boldness bold bolds bold - bolingbroke bolingbrok bolster bolster - bolt bolt bolted bolt - bolter bolter bolters bolter - bolting bolt bolts bolt - bombard bombard bombards bombard - bombast bombast bon bon - bona bona bond bond - bondage bondag bonded bond - bondmaid bondmaid bondman bondman - bondmen bondmen bonds bond - bondslave bondslav bone bone - boneless boneless bones bone - bonfire bonfir bonfires bonfir - bonjour bonjour bonne bonn - bonnet bonnet bonneted bonnet - bonny bonni bonos bono - bonto bonto bonville bonvil - bood bood book book - bookish bookish books book - boon boon boor boor - boorish boorish boors boor - boot boot booted boot - booties booti bootless bootless - boots boot booty booti - bor bor bora bora - borachio borachio bordeaux bordeaux - border border bordered border - borderers border borders border - bore bore boreas borea - bores bore boring bore - born born borne born - borough borough boroughs borough - borrow borrow borrowed borrow - borrower borrow borrowing borrow - borrows borrow bosko bosko - boskos bosko bosky boski - bosom bosom bosoms bosom - boson boson boss boss - bosworth bosworth botch botch - botcher botcher botches botch - botchy botchi both both - bots bot bottle bottl - bottled bottl bottles bottl - bottom bottom bottomless bottomless - bottoms bottom bouciqualt bouciqualt - bouge boug bough bough - boughs bough bought bought - bounce bounc bouncing bounc - bound bound bounded bound - bounden bounden boundeth boundeth - bounding bound boundless boundless - bounds bound bounteous bounteou - bounteously bounteous bounties bounti - bountiful bounti bountifully bountifulli - bounty bounti bourbier bourbier - bourbon bourbon bourchier bourchier - bourdeaux bourdeaux bourn bourn - bout bout bouts bout - bove bove bow bow - bowcase bowcas bowed bow - bowels bowel bower bower - bowing bow bowl bowl - bowler bowler bowling bowl - bowls bowl bows bow - bowsprit bowsprit bowstring bowstr - box box boxes box - boy boi boyet boyet - boyish boyish boys boi - brabant brabant brabantio brabantio - brabble brabbl brabbler brabbler - brac brac brace brace - bracelet bracelet bracelets bracelet - brach brach bracy braci - brag brag bragg bragg - braggardism braggard braggards braggard - braggart braggart braggarts braggart - bragged brag bragging brag - bragless bragless brags brag - braid braid braided braid - brain brain brained brain - brainford brainford brainish brainish - brainless brainless brains brain - brainsick brainsick brainsickly brainsickli - brake brake brakenbury brakenburi - brakes brake brambles brambl - bran bran branch branch - branches branch branchless branchless - brand brand branded brand - brandish brandish brandon brandon - brands brand bras bra - brass brass brassy brassi - brat brat brats brat - brav brav brave brave - braved brave bravely brave - braver braver bravery braveri - braves brave bravest bravest - braving brave brawl brawl - brawler brawler brawling brawl - brawls brawl brawn brawn - brawns brawn bray brai - braying brai braz braz - brazen brazen brazier brazier - breach breach breaches breach - bread bread breadth breadth - break break breaker breaker - breakfast breakfast breaking break - breaks break breast breast - breasted breast breasting breast - breastplate breastplat breasts breast - breath breath breathe breath - breathed breath breather breather - breathers breather breathes breath - breathest breathest breathing breath - breathless breathless breaths breath - brecknock brecknock bred bred - breech breech breeches breech - breeching breech breed breed - breeder breeder breeders breeder - breeding breed breeds breed - breese brees breeze breez - breff breff bretagne bretagn - brethen brethen bretheren bretheren - brethren brethren brevis brevi - brevity breviti brew brew - brewage brewag brewer brewer - brewers brewer brewing brew - brews brew briareus briareu - briars briar brib brib - bribe bribe briber briber - bribes bribe brick brick - bricklayer bricklay bricks brick - bridal bridal bride bride - bridegroom bridegroom bridegrooms bridegroom - brides bride bridge bridg - bridgenorth bridgenorth bridges bridg - bridget bridget bridle bridl - bridled bridl brief brief - briefer briefer briefest briefest - briefly briefli briefness brief - brier brier briers brier - brigandine brigandin bright bright - brighten brighten brightest brightest - brightly brightli brightness bright - brim brim brimful brim - brims brim brimstone brimston - brinded brind brine brine - bring bring bringer bringer - bringeth bringeth bringing bring - bringings bring brings bring - brinish brinish brink brink - brisk brisk brisky briski - bristle bristl bristled bristl - bristly bristli bristol bristol - bristow bristow britain britain - britaine britain britaines britain - british british briton briton - britons briton brittany brittani - brittle brittl broach broach - broached broach broad broad - broader broader broadsides broadsid - brocas broca brock brock - brogues brogu broil broil - broiling broil broils broil - broke broke broken broken - brokenly brokenli broker broker - brokers broker brokes broke - broking broke brooch brooch - brooches brooch brood brood - brooded brood brooding brood - brook brook brooks brook - broom broom broomstaff broomstaff - broth broth brothel brothel - brother brother brotherhood brotherhood - brotherhoods brotherhood brotherly brotherli - brothers brother broths broth - brought brought brow brow - brown brown browner browner - brownist brownist browny browni - brows brow browse brows - browsing brows bruis brui - bruise bruis bruised bruis - bruises bruis bruising bruis - bruit bruit bruited bruit - brundusium brundusium brunt brunt - brush brush brushes brush - brute brute brutish brutish - brutus brutu bubble bubbl - bubbles bubbl bubbling bubbl - bubukles bubukl buck buck - bucket bucket buckets bucket - bucking buck buckingham buckingham - buckle buckl buckled buckl - buckler buckler bucklers buckler - bucklersbury bucklersburi buckles buckl - buckram buckram bucks buck - bud bud budded bud - budding bud budge budg - budger budger budget budget - buds bud buff buff - buffet buffet buffeting buffet - buffets buffet bug bug - bugbear bugbear bugle bugl - bugs bug build build - builded build buildeth buildeth - building build buildings build - builds build built built - bulk bulk bulks bulk - bull bull bullcalf bullcalf - bullen bullen bullens bullen - bullet bullet bullets bullet - bullocks bullock bulls bull - bully bulli bulmer bulmer - bulwark bulwark bulwarks bulwark - bum bum bumbast bumbast - bump bump bumper bumper - bums bum bunch bunch - bunches bunch bundle bundl - bung bung bunghole bunghol - bungle bungl bunting bunt - buoy buoi bur bur - burbolt burbolt burd burd - burden burden burdened burden - burdening burden burdenous burden - burdens burden burgh burgh - burgher burgher burghers burgher - burglary burglari burgomasters burgomast - burgonet burgonet burgundy burgundi - burial burial buried buri - burier burier buriest buriest - burly burli burn burn - burned burn burnet burnet - burneth burneth burning burn - burnish burnish burns burn - burnt burnt burr burr - burrows burrow burs bur - burst burst bursting burst - bursts burst burthen burthen - burthens burthen burton burton - bury buri burying buri - bush bush bushels bushel - bushes bush bushy bushi - busied busi busily busili - busines busin business busi - businesses busi buskin buskin - busky buski buss buss - busses buss bussing buss - bustle bustl bustling bustl - busy busi but but - butcheed butche butcher butcher - butchered butcher butcheries butcheri - butcherly butcherli butchers butcher - butchery butcheri butler butler - butt butt butter butter - buttered butter butterflies butterfli - butterfly butterfli butterwoman butterwoman - buttery butteri buttock buttock - buttocks buttock button button - buttonhole buttonhol buttons button - buttress buttress buttry buttri - butts butt buxom buxom - buy bui buyer buyer - buying bui buys bui - buzz buzz buzzard buzzard - buzzards buzzard buzzers buzzer - buzzing buzz by by - bye bye byzantium byzantium - c c ca ca - cabbage cabbag cabileros cabilero - cabin cabin cabins cabin - cable cabl cables cabl - cackling cackl cacodemon cacodemon - caddis caddi caddisses caddiss - cade cade cadence cadenc - cadent cadent cades cade - cadmus cadmu caduceus caduceu - cadwal cadwal cadwallader cadwallad - caelius caeliu caelo caelo - caesar caesar caesarion caesarion - caesars caesar cage cage - caged cage cagion cagion - cain cain caithness caith - caitiff caitiff caitiffs caitiff - caius caiu cak cak - cake cake cakes cake - calaber calab calais calai - calamities calam calamity calam - calchas calcha calculate calcul - calen calen calendar calendar - calendars calendar calf calf - caliban caliban calibans caliban - calipolis calipoli cality caliti - caliver caliv call call - callat callat called call - callet callet calling call - calls call calm calm - calmest calmest calmly calmli - calmness calm calms calm - calpurnia calpurnia calumniate calumni - calumniating calumni calumnious calumni - calumny calumni calve calv - calved calv calves calv - calveskins calveskin calydon calydon - cam cam cambio cambio - cambria cambria cambric cambric - cambrics cambric cambridge cambridg - cambyses cambys came came - camel camel camelot camelot - camels camel camest camest - camillo camillo camlet camlet - camomile camomil camp camp - campeius campeiu camping camp - camps camp can can - canakin canakin canaries canari - canary canari cancel cancel - cancell cancel cancelled cancel - cancelling cancel cancels cancel - cancer cancer candidatus candidatu - candied candi candle candl - candles candl candlesticks candlestick - candy candi canidius canidiu - cank cank canker canker - cankerblossom cankerblossom cankers canker - cannibally cannib cannibals cannib - cannon cannon cannoneer cannon - cannons cannon cannot cannot - canon canon canoniz canoniz - canonize canon canonized canon - canons canon canopied canopi - canopies canopi canopy canopi - canst canst canstick canstick - canterbury canterburi cantle cantl - cantons canton canus canu - canvas canva canvass canvass - canzonet canzonet cap cap - capability capabl capable capabl - capacities capac capacity capac - caparison caparison capdv capdv - cape cape capel capel - capels capel caper caper - capers caper capet capet - caphis caphi capilet capilet - capitaine capitain capital capit - capite capit capitol capitol - capitulate capitul capocchia capocchia - capon capon capons capon - capp capp cappadocia cappadocia - capriccio capriccio capricious caprici - caps cap capt capt - captain captain captains captain - captainship captainship captious captiou - captivate captiv captivated captiv - captivates captiv captive captiv - captives captiv captivity captiv - captum captum capucius capuciu - capulet capulet capulets capulet - car car carack carack - caracks carack carat carat - caraways carawai carbonado carbonado - carbuncle carbuncl carbuncled carbuncl - carbuncles carbuncl carcanet carcanet - carcase carcas carcases carcas - carcass carcass carcasses carcass - card card cardecue cardecu - carded card carders carder - cardinal cardin cardinally cardin - cardinals cardin cardmaker cardmak - cards card carduus carduu - care care cared care - career career careers career - careful care carefully carefulli - careless careless carelessly carelessli - carelessness careless cares care - caret caret cargo cargo - carl carl carlisle carlisl - carlot carlot carman carman - carmen carmen carnal carnal - carnally carnal carnarvonshire carnarvonshir - carnation carnat carnations carnat - carol carol carous carou - carouse carous caroused carous - carouses carous carousing carous - carp carp carpenter carpent - carper carper carpet carpet - carpets carpet carping carp - carriage carriag carriages carriag - carried carri carrier carrier - carriers carrier carries carri - carrion carrion carrions carrion - carry carri carrying carri - cars car cart cart - carters carter carthage carthag - carts cart carv carv - carve carv carved carv - carver carver carves carv - carving carv cas ca - casa casa casaer casaer - casca casca case case - casement casement casements casement - cases case cash cash - cashier cashier casing case - cask cask casket casket - casketed casket caskets casket - casque casqu casques casqu - cassado cassado cassandra cassandra - cassibelan cassibelan cassio cassio - cassius cassiu cassocks cassock - cast cast castalion castalion - castaway castawai castaways castawai - casted cast caster caster - castigate castig castigation castig - castile castil castiliano castiliano - casting cast castle castl - castles castl casts cast - casual casual casually casual - casualties casualti casualty casualti - cat cat cataian cataian - catalogue catalogu cataplasm cataplasm - cataracts cataract catarrhs catarrh - catastrophe catastroph catch catch - catcher catcher catches catch - catching catch cate cate - catechising catechis catechism catech - catechize catech cater cater - caterpillars caterpillar caters cater - caterwauling caterwaul cates cate - catesby catesbi cathedral cathedr - catlike catlik catling catl - catlings catl cato cato - cats cat cattle cattl - caucasus caucasu caudle caudl - cauf cauf caught caught - cauldron cauldron caus cau - cause caus caused caus - causeless causeless causer causer - causes caus causest causest - causeth causeth cautel cautel - cautelous cautel cautels cautel - cauterizing cauter caution caution - cautions caution cavaleiro cavaleiro - cavalery cavaleri cavaliers cavali - cave cave cavern cavern - caverns cavern caves cave - caveto caveto caviary caviari - cavil cavil cavilling cavil - cawdor cawdor cawdron cawdron - cawing caw ce ce - ceas cea cease ceas - ceases ceas ceaseth ceaseth - cedar cedar cedars cedar - cedius cediu celebrate celebr - celebrated celebr celebrates celebr - celebration celebr celerity celer - celestial celesti celia celia - cell cell cellar cellar - cellarage cellarag celsa celsa - cement cement censer censer - censor censor censorinus censorinu - censur censur censure censur - censured censur censurers censur - censures censur censuring censur - centaur centaur centaurs centaur - centre centr cents cent - centuries centuri centurion centurion - centurions centurion century centuri - cerberus cerberu cerecloth cerecloth - cerements cerement ceremonial ceremoni - ceremonies ceremoni ceremonious ceremoni - ceremoniously ceremoni ceremony ceremoni - ceres cere cerns cern - certain certain certainer certain - certainly certainli certainties certainti - certainty certainti certes cert - certificate certif certified certifi - certifies certifi certify certifi - ces ce cesario cesario - cess cess cesse cess - cestern cestern cetera cetera - cette cett chaces chace - chaf chaf chafe chafe - chafed chafe chafes chafe - chaff chaff chaffless chaffless - chafing chafe chain chain - chains chain chair chair - chairs chair chalic chalic - chalice chalic chalices chalic - chalk chalk chalks chalk - chalky chalki challeng challeng - challenge challeng challenged challeng - challenger challeng challengers challeng - challenges challeng cham cham - chamber chamber chamberers chamber - chamberlain chamberlain chamberlains chamberlain - chambermaid chambermaid chambermaids chambermaid - chambers chamber chameleon chameleon - champ champ champagne champagn - champain champain champains champain - champion champion champions champion - chanc chanc chance chanc - chanced chanc chancellor chancellor - chances chanc chandler chandler - chang chang change chang - changeable changeabl changed chang - changeful chang changeling changel - changelings changel changer changer - changes chang changest changest - changing chang channel channel - channels channel chanson chanson - chant chant chanticleer chanticl - chanting chant chantries chantri - chantry chantri chants chant - chaos chao chap chap - chape chape chapel chapel - chapeless chapeless chapels chapel - chaplain chaplain chaplains chaplain - chapless chapless chaplet chaplet - chapmen chapmen chaps chap - chapter chapter character charact - charactered charact characterless characterless - characters charact charactery characteri - characts charact charbon charbon - chare chare chares chare - charg charg charge charg - charged charg chargeful charg - charges charg chargeth chargeth - charging charg chariest chariest - chariness chari charing chare - chariot chariot chariots chariot - charitable charit charitably charit - charities chariti charity chariti - charlemain charlemain charles charl - charm charm charmed charm - charmer charmer charmeth charmeth - charmian charmian charming charm - charmingly charmingli charms charm - charneco charneco charnel charnel - charolois charoloi charon charon - charter charter charters charter - chartreux chartreux chary chari - charybdis charybdi chas cha - chase chase chased chase - chaser chaser chaseth chaseth - chasing chase chaste chast - chastely chast chastis chasti - chastise chastis chastised chastis - chastisement chastis chastity chastiti - chat chat chatham chatham - chatillon chatillon chats chat - chatt chatt chattels chattel - chatter chatter chattering chatter - chattles chattl chaud chaud - chaunted chaunt chaw chaw - chawdron chawdron che che - cheap cheap cheapen cheapen - cheaper cheaper cheapest cheapest - cheaply cheapli cheapside cheapsid - cheat cheat cheated cheat - cheater cheater cheaters cheater - cheating cheat cheats cheat - check check checked check - checker checker checking check - checks check cheek cheek - cheeks cheek cheer cheer - cheered cheer cheerer cheerer - cheerful cheer cheerfully cheerfulli - cheering cheer cheerless cheerless - cheerly cheerli cheers cheer - cheese chees chequer chequer - cher cher cherish cherish - cherished cherish cherisher cherish - cherishes cherish cherishing cherish - cherries cherri cherry cherri - cherrypit cherrypit chertsey chertsei - cherub cherub cherubims cherubim - cherubin cherubin cherubins cherubin - cheshu cheshu chess chess - chest chest chester chester - chestnut chestnut chestnuts chestnut - chests chest chetas cheta - chev chev cheval cheval - chevalier chevali chevaliers chevali - cheveril cheveril chew chew - chewed chew chewet chewet - chewing chew chez chez - chi chi chick chick - chicken chicken chickens chicken - chicurmurco chicurmurco chid chid - chidden chidden chide chide - chiders chider chides chide - chiding chide chief chief - chiefest chiefest chiefly chiefli - chien chien child child - childed child childeric childer - childhood childhood childhoods childhood - childing child childish childish - childishness childish childlike childlik - childness child children children - chill chill chilling chill - chime chime chimes chime - chimney chimnei chimneypiece chimneypiec - chimneys chimnei chimurcho chimurcho - chin chin china china - chine chine chines chine - chink chink chinks chink - chins chin chipp chipp - chipper chipper chips chip - chiron chiron chirping chirp - chirrah chirrah chirurgeonly chirurgeonli - chisel chisel chitopher chitoph - chivalrous chivalr chivalry chivalri - choice choic choicely choic - choicest choicest choir choir - choirs choir chok chok - choke choke choked choke - chokes choke choking choke - choler choler choleric choler - cholers choler chollors chollor - choose choos chooser chooser - chooses choos chooseth chooseth - choosing choos chop chop - chopine chopin choplogic choplog - chopp chopp chopped chop - chopping chop choppy choppi - chops chop chopt chopt - chor chor choristers chorist - chorus choru chose chose - chosen chosen chough chough - choughs chough chrish chrish - christ christ christen christen - christendom christendom christendoms christendom - christening christen christenings christen - christian christian christianlike christianlik - christians christian christmas christma - christom christom christopher christoph - christophero christophero chronicle chronicl - chronicled chronicl chronicler chronicl - chroniclers chronicl chronicles chronicl - chrysolite chrysolit chuck chuck - chucks chuck chud chud - chuffs chuff church church - churches church churchman churchman - churchmen churchmen churchyard churchyard - churchyards churchyard churl churl - churlish churlish churlishly churlishli - churls churl churn churn - chus chu cicatrice cicatric - cicatrices cicatric cicely cice - cicero cicero ciceter cicet - ciel ciel ciitzens ciitzen - cilicia cilicia cimber cimber - cimmerian cimmerian cinable cinabl - cincture cinctur cinders cinder - cine cine cinna cinna - cinque cinqu cipher cipher - ciphers cipher circa circa - circe circ circle circl - circled circl circlets circlet - circling circl circuit circuit - circum circum circumcised circumcis - circumference circumfer circummur circummur - circumscrib circumscrib circumscribed circumscrib - circumscription circumscript circumspect circumspect - circumstance circumst circumstanced circumstanc - circumstances circumst circumstantial circumstanti - circumvent circumv circumvention circumvent - cistern cistern citadel citadel - cital cital cite cite - cited cite cites cite - cities citi citing cite - citizen citizen citizens citizen - cittern cittern city citi - civet civet civil civil - civility civil civilly civilli - clack clack clad clad - claim claim claiming claim - claims claim clamb clamb - clamber clamber clammer clammer - clamor clamor clamorous clamor - clamors clamor clamour clamour - clamours clamour clang clang - clangor clangor clap clap - clapp clapp clapped clap - clapper clapper clapping clap - claps clap clare clare - clarence clarenc claret claret - claribel claribel clasp clasp - clasps clasp clatter clatter - claud claud claudio claudio - claudius claudiu clause claus - claw claw clawed claw - clawing claw claws claw - clay clai clays clai - clean clean cleanliest cleanliest - cleanly cleanli cleans clean - cleanse cleans cleansing cleans - clear clear clearer clearer - clearest clearest clearly clearli - clearness clear clears clear - cleave cleav cleaving cleav - clef clef cleft cleft - cleitus cleitu clemency clemenc - clement clement cleomenes cleomen - cleopatpa cleopatpa cleopatra cleopatra - clepeth clepeth clept clept - clerestories clerestori clergy clergi - clergyman clergyman clergymen clergymen - clerk clerk clerkly clerkli - clerks clerk clew clew - client client clients client - cliff cliff clifford clifford - cliffords clifford cliffs cliff - clifton clifton climate climat - climature climatur climb climb - climbed climb climber climber - climbeth climbeth climbing climb - climbs climb clime clime - cling cling clink clink - clinking clink clinquant clinquant - clip clip clipp clipp - clipper clipper clippeth clippeth - clipping clip clipt clipt - clitus clitu clo clo - cloak cloak cloakbag cloakbag - cloaks cloak clock clock - clocks clock clod clod - cloddy cloddi clodpole clodpol - clog clog clogging clog - clogs clog cloister cloister - cloistress cloistress cloquence cloquenc - clos clo close close - closed close closely close - closeness close closer closer - closes close closest closest - closet closet closing close - closure closur cloten cloten - clotens cloten cloth cloth - clothair clothair clotharius clothariu - clothe cloth clothes cloth - clothier clothier clothiers clothier - clothing cloth cloths cloth - clotpoles clotpol clotpoll clotpol - cloud cloud clouded cloud - cloudiness cloudi clouds cloud - cloudy cloudi clout clout - clouted clout clouts clout - cloven cloven clover clover - cloves clove clovest clovest - clowder clowder clown clown - clownish clownish clowns clown - cloy cloi cloyed cloi - cloying cloi cloyless cloyless - cloyment cloyment cloys cloi - club club clubs club - cluck cluck clung clung - clust clust clusters cluster - clutch clutch clyster clyster - cneius cneiu cnemies cnemi - co co coach coach - coaches coach coachmakers coachmak - coact coact coactive coactiv - coagulate coagul coal coal - coals coal coarse coars - coarsely coars coast coast - coasting coast coasts coast - coat coat coated coat - coats coat cobble cobbl - cobbled cobbl cobbler cobbler - cobham cobham cobloaf cobloaf - cobweb cobweb cobwebs cobweb - cock cock cockatrice cockatric - cockatrices cockatric cockle cockl - cockled cockl cockney cocknei - cockpit cockpit cocks cock - cocksure cocksur coctus coctu - cocytus cocytu cod cod - codding cod codling codl - codpiece codpiec codpieces codpiec - cods cod coelestibus coelestibu - coesar coesar coeur coeur - coffer coffer coffers coffer - coffin coffin coffins coffin - cog cog cogging cog - cogitation cogit cogitations cogit - cognition cognit cognizance cogniz - cogscomb cogscomb cohabitants cohabit - coher coher cohere coher - coherence coher coherent coher - cohorts cohort coif coif - coign coign coil coil - coin coin coinage coinag - coiner coiner coining coin - coins coin col col - colbrand colbrand colchos colcho - cold cold colder colder - coldest coldest coldly coldli - coldness cold coldspur coldspur - colebrook colebrook colic colic - collar collar collars collar - collateral collater colleagued colleagu - collect collect collected collect - collection collect college colleg - colleges colleg collied colli - collier collier colliers collier - collop collop collusion collus - colme colm colmekill colmekil - coloquintida coloquintida color color - colors color colossus colossu - colour colour colourable colour - coloured colour colouring colour - colours colour colt colt - colted colt colts colt - columbine columbin columbines columbin - colville colvil com com - comagene comagen comart comart - comb comb combat combat - combatant combat combatants combat - combated combat combating combat - combin combin combinate combin - combination combin combine combin - combined combin combless combless - combustion combust come come - comedian comedian comedians comedian - comedy comedi comeliness comeli - comely come comer comer - comers comer comes come - comest comest comet comet - cometh cometh comets comet - comfect comfect comfit comfit - comfits comfit comfort comfort - comfortable comfort comforted comfort - comforter comfort comforting comfort - comfortless comfortless comforts comfort - comic comic comical comic - coming come comings come - cominius cominiu comma comma - command command commande command - commanded command commander command - commanders command commanding command - commandment command commandments command - commands command comme comm - commenc commenc commence commenc - commenced commenc commencement commenc - commences commenc commencing commenc - commend commend commendable commend - commendation commend commendations commend - commended commend commending commend - commends commend comment comment - commentaries commentari commenting comment - comments comment commerce commerc - commingled commingl commiseration commiser - commission commiss commissioners commission - commissions commiss commit commit - commits commit committ committ - committed commit committing commit - commix commix commixed commix - commixtion commixt commixture commixtur - commodious commodi commodities commod - commodity commod common common - commonalty commonalti commoner common - commoners common commonly commonli - commons common commonweal commonw - commonwealth commonwealth commotion commot - commotions commot commune commun - communicat communicat communicate commun - communication commun communities commun - community commun comonty comonti - compact compact companies compani - companion companion companions companion - companionship companionship company compani - compar compar comparative compar - compare compar compared compar - comparing compar comparison comparison - comparisons comparison compartner compartn - compass compass compasses compass - compassing compass compassion compass - compassionate compassion compeers compeer - compel compel compell compel - compelled compel compelling compel - compels compel compensation compens - competence compet competency compet - competent compet competitor competitor - competitors competitor compil compil - compile compil compiled compil - complain complain complainer complain - complainest complainest complaining complain - complainings complain complains complain - complaint complaint complaints complaint - complement complement complements complement - complete complet complexion complexion - complexioned complexion complexions complexion - complices complic complies compli - compliment compliment complimental compliment - compliments compliment complot complot - complots complot complotted complot - comply compli compos compo - compose compos composed compos - composition composit compost compost - composture compostur composure composur - compound compound compounded compound - compounds compound comprehend comprehend - comprehended comprehend comprehends comprehend - compremises compremis compris compri - comprising compris compromis compromi - compromise compromis compt compt - comptible comptibl comptrollers comptrol - compulsatory compulsatori compulsion compuls - compulsive compuls compunctious compuncti - computation comput comrade comrad - comrades comrad comutual comutu - con con concave concav - concavities concav conceal conceal - concealed conceal concealing conceal - concealment conceal concealments conceal - conceals conceal conceit conceit - conceited conceit conceitless conceitless - conceits conceit conceiv conceiv - conceive conceiv conceived conceiv - conceives conceiv conceiving conceiv - conception concept conceptions concept - conceptious concepti concern concern - concernancy concern concerneth concerneth - concerning concern concernings concern - concerns concern conclave conclav - conclud conclud conclude conclud - concluded conclud concludes conclud - concluding conclud conclusion conclus - conclusions conclus concolinel concolinel - concord concord concubine concubin - concupiscible concupisc concupy concupi - concur concur concurring concur - concurs concur condemn condemn - condemnation condemn condemned condemn - condemning condemn condemns condemn - condescend condescend condign condign - condition condit conditionally condition - conditions condit condole condol - condolement condol condoling condol - conduce conduc conduct conduct - conducted conduct conducting conduct - conductor conductor conduit conduit - conduits conduit conected conect - coney conei confection confect - confectionary confectionari confections confect - confederacy confederaci confederate confeder - confederates confeder confer confer - conference confer conferr conferr - conferring confer confess confess - confessed confess confesses confess - confesseth confesseth confessing confess - confession confess confessions confess - confessor confessor confidence confid - confident confid confidently confid - confin confin confine confin - confined confin confineless confineless - confiners confin confines confin - confining confin confirm confirm - confirmation confirm confirmations confirm - confirmed confirm confirmer confirm - confirmers confirm confirming confirm - confirmities confirm confirms confirm - confiscate confisc confiscated confisc - confiscation confisc confixed confix - conflict conflict conflicting conflict - conflicts conflict confluence confluenc - conflux conflux conform conform - conformable conform confound confound - confounded confound confounding confound - confounds confound confront confront - confronted confront confus confu - confused confus confusedly confusedli - confusion confus confusions confus - confutation confut confutes confut - congeal congeal congealed congeal - congealment congeal congee conge - conger conger congest congest - congied congi congratulate congratul - congreeing congre congreeted congreet - congregate congreg congregated congreg - congregation congreg congregations congreg - congruent congruent congruing congru - conies coni conjectural conjectur - conjecture conjectur conjectures conjectur - conjoin conjoin conjoined conjoin - conjoins conjoin conjointly conjointli - conjunct conjunct conjunction conjunct - conjunctive conjunct conjur conjur - conjuration conjur conjurations conjur - conjure conjur conjured conjur - conjurer conjur conjurers conjur - conjures conjur conjuring conjur - conjuro conjuro conn conn - connected connect connive conniv - conqu conqu conquer conquer - conquered conquer conquering conquer - conqueror conqueror conquerors conqueror - conquers conquer conquest conquest - conquests conquest conquring conqur - conrade conrad cons con - consanguineous consanguin consanguinity consanguin - conscienc conscienc conscience conscienc - consciences conscienc conscionable conscion - consecrate consecr consecrated consecr - consecrations consecr consent consent - consented consent consenting consent - consents consent consequence consequ - consequences consequ consequently consequ - conserve conserv conserved conserv - conserves conserv consider consid - considerance consider considerate consider - consideration consider considerations consider - considered consid considering consid - considerings consid considers consid - consign consign consigning consign - consist consist consisteth consisteth - consisting consist consistory consistori - consists consist consolate consol - consolation consol consonancy conson - consonant conson consort consort - consorted consort consortest consortest - conspectuities conspectu conspir conspir - conspiracy conspiraci conspirant conspir - conspirator conspir conspirators conspir - conspire conspir conspired conspir - conspirers conspir conspires conspir - conspiring conspir constable constabl - constables constabl constance constanc - constancies constanc constancy constanc - constant constant constantine constantin - constantinople constantinopl constantly constantli - constellation constel constitution constitut - constrain constrain constrained constrain - constraineth constraineth constrains constrain - constraint constraint constring constr - construction construct construe constru - consul consul consuls consul - consulship consulship consulships consulship - consult consult consulting consult - consults consult consum consum - consume consum consumed consum - consumes consum consuming consum - consummate consumm consummation consumm - consumption consumpt consumptions consumpt - contagion contagion contagious contagi - contain contain containing contain - contains contain contaminate contamin - contaminated contamin contemn contemn - contemned contemn contemning contemn - contemns contemn contemplate contempl - contemplation contempl contemplative contempl - contempt contempt contemptible contempt - contempts contempt contemptuous contemptu - contemptuously contemptu contend contend - contended contend contending contend - contendon contendon content content - contenta contenta contented content - contenteth contenteth contention content - contentious contenti contentless contentless - contento contento contents content - contest contest contestation contest - continence contin continency contin - continent contin continents contin - continu continu continual continu - continually continu continuance continu - continuantly continuantli continuate continu - continue continu continued continu - continuer continu continues continu - continuing continu contract contract - contracted contract contracting contract - contraction contract contradict contradict - contradicted contradict contradiction contradict - contradicts contradict contraries contrari - contrarieties contrarieti contrariety contrarieti - contrarious contrari contrariously contrari - contrary contrari contre contr - contribution contribut contributors contributor - contrite contrit contriv contriv - contrive contriv contrived contriv - contriver contriv contrives contriv - contriving contriv control control - controll control controller control - controlling control controlment control - controls control controversy controversi - contumelious contumeli contumeliously contumeli - contumely contum contusions contus - convenience conveni conveniences conveni - conveniency conveni convenient conveni - conveniently conveni convented convent - conventicles conventicl convents convent - convers conver conversant convers - conversation convers conversations convers - converse convers conversed convers - converses convers conversing convers - conversion convers convert convert - converted convert convertest convertest - converting convert convertite convertit - convertites convertit converts convert - convey convei conveyance convey - conveyances convey conveyers convey - conveying convei convict convict - convicted convict convince convinc - convinced convinc convinces convinc - convive conviv convocation convoc - convoy convoi convulsions convuls - cony coni cook cook - cookery cookeri cooks cook - cool cool cooled cool - cooling cool cools cool - coop coop coops coop - cop cop copatain copatain - cope cope cophetua cophetua - copied copi copies copi - copious copiou copper copper - copperspur copperspur coppice coppic - copulation copul copulatives copul - copy copi cor cor - coragio coragio coral coral - coram coram corambus corambu - coranto coranto corantos coranto - corbo corbo cord cord - corded cord cordelia cordelia - cordial cordial cordis cordi - cords cord core core - corin corin corinth corinth - corinthian corinthian coriolanus coriolanu - corioli corioli cork cork - corky corki cormorant cormor - corn corn cornelia cornelia - cornelius corneliu corner corner - corners corner cornerstone cornerston - cornets cornet cornish cornish - corns corn cornuto cornuto - cornwall cornwal corollary corollari - coronal coron coronation coron - coronet coronet coronets coronet - corporal corpor corporals corpor - corporate corpor corpse corps - corpulent corpul correct correct - corrected correct correcting correct - correction correct correctioner correction - corrects correct correspondence correspond - correspondent correspond corresponding correspond - corresponsive correspons corrigible corrig - corrival corriv corrivals corriv - corroborate corrobor corrosive corros - corrupt corrupt corrupted corrupt - corrupter corrupt corrupters corrupt - corruptible corrupt corruptibly corrupt - corrupting corrupt corruption corrupt - corruptly corruptli corrupts corrupt - corse cors corses cors - corslet corslet cosmo cosmo - cost cost costard costard - costermongers costermong costlier costlier - costly costli costs cost - cot cot cote cote - coted cote cotsall cotsal - cotsole cotsol cotswold cotswold - cottage cottag cottages cottag - cotus cotu couch couch - couched couch couching couch - couchings couch coude coud - cough cough coughing cough - could could couldst couldst - coulter coulter council council - councillor councillor councils council - counsel counsel counsell counsel - counsellor counsellor counsellors counsellor - counselor counselor counselors counselor - counsels counsel count count - counted count countenanc countenanc - countenance counten countenances counten - counter counter counterchange counterchang - countercheck countercheck counterfeit counterfeit - counterfeited counterfeit counterfeiting counterfeit - counterfeitly counterfeitli counterfeits counterfeit - countermand countermand countermands countermand - countermines countermin counterpart counterpart - counterpoints counterpoint counterpois counterpoi - counterpoise counterpois counters counter - countervail countervail countess countess - countesses countess counties counti - counting count countless countless - countries countri countrv countrv - country countri countryman countryman - countrymen countrymen counts count - county counti couper couper - couple coupl coupled coupl - couplement couplement couples coupl - couplet couplet couplets couplet - cour cour courage courag - courageous courag courageously courag - courages courag courier courier - couriers courier couronne couronn - cours cour course cours - coursed cours courser courser - coursers courser courses cours - coursing cours court court - courted court courteous courteou - courteously courteous courtesan courtesan - courtesies courtesi courtesy courtesi - courtezan courtezan courtezans courtezan - courtier courtier courtiers courtier - courtlike courtlik courtly courtli - courtney courtnei courts court - courtship courtship cousin cousin - cousins cousin couterfeit couterfeit - coutume coutum covenant coven - covenants coven covent covent - coventry coventri cover cover - covered cover covering cover - coverlet coverlet covers cover - covert covert covertly covertli - coverture covertur covet covet - coveted covet coveting covet - covetings covet covetous covet - covetously covet covetousness covet - covets covet cow cow - coward coward cowarded coward - cowardice cowardic cowardly cowardli - cowards coward cowardship cowardship - cowish cowish cowl cowl - cowslip cowslip cowslips cowslip - cox cox coxcomb coxcomb - coxcombs coxcomb coy coi - coystrill coystril coz coz - cozen cozen cozenage cozenag - cozened cozen cozener cozen - cozeners cozen cozening cozen - coziers cozier crab crab - crabbed crab crabs crab - crack crack cracked crack - cracker cracker crackers cracker - cracking crack cracks crack - cradle cradl cradled cradl - cradles cradl craft craft - crafted craft craftied crafti - craftier craftier craftily craftili - crafts craft craftsmen craftsmen - crafty crafti cram cram - cramm cramm cramp cramp - cramps cramp crams cram - cranking crank cranks crank - cranmer cranmer crannied cranni - crannies cranni cranny cranni - crants crant crare crare - crash crash crassus crassu - crav crav crave crave - craved crave craven craven - cravens craven craves crave - craveth craveth craving crave - crawl crawl crawling crawl - crawls crawl craz craz - crazed craze crazy crazi - creaking creak cream cream - create creat created creat - creates creat creating creat - creation creation creator creator - creature creatur creatures creatur - credence credenc credent credent - credible credibl credit credit - creditor creditor creditors creditor - credo credo credulity credul - credulous credul creed creed - creek creek creeks creek - creep creep creeping creep - creeps creep crept crept - crescent crescent crescive cresciv - cressets cresset cressid cressid - cressida cressida cressids cressid - cressy cressi crest crest - crested crest crestfall crestfal - crestless crestless crests crest - cretan cretan crete crete - crevice crevic crew crew - crews crew crib crib - cribb cribb cribs crib - cricket cricket crickets cricket - cried cri criedst criedst - crier crier cries cri - criest criest crieth crieth - crime crime crimeful crime - crimeless crimeless crimes crime - criminal crimin crimson crimson - cringe cring cripple crippl - crisp crisp crisped crisp - crispian crispian crispianus crispianu - crispin crispin critic critic - critical critic critics critic - croak croak croaking croak - croaks croak crocodile crocodil - cromer cromer cromwell cromwel - crone crone crook crook - crookback crookback crooked crook - crooking crook crop crop - cropp cropp crosby crosbi - cross cross crossed cross - crosses cross crossest crossest - crossing cross crossings cross - crossly crossli crossness cross - crost crost crotchets crotchet - crouch crouch crouching crouch - crow crow crowd crowd - crowded crowd crowding crowd - crowds crowd crowflowers crowflow - crowing crow crowkeeper crowkeep - crown crown crowned crown - crowner crowner crownet crownet - crownets crownet crowning crown - crowns crown crows crow - crudy crudi cruel cruel - cruell cruell crueller crueller - cruelly cruelli cruels cruel - cruelty cruelti crum crum - crumble crumbl crumbs crumb - crupper crupper crusadoes crusado - crush crush crushed crush - crushest crushest crushing crush - crust crust crusts crust - crusty crusti crutch crutch - crutches crutch cry cry - crying cry crystal crystal - crystalline crystallin crystals crystal - cub cub cubbert cubbert - cubiculo cubiculo cubit cubit - cubs cub cuckold cuckold - cuckoldly cuckoldli cuckolds cuckold - cuckoo cuckoo cucullus cucullu - cudgel cudgel cudgeled cudgel - cudgell cudgel cudgelling cudgel - cudgels cudgel cue cue - cues cue cuff cuff - cuffs cuff cuique cuiqu - cull cull culling cull - cullion cullion cullionly cullionli - cullions cullion culpable culpabl - culverin culverin cum cum - cumber cumber cumberland cumberland - cunning cun cunningly cunningli - cunnings cun cuore cuor - cup cup cupbearer cupbear - cupboarding cupboard cupid cupid - cupids cupid cuppele cuppel - cups cup cur cur - curan curan curate curat - curb curb curbed curb - curbing curb curbs curb - curd curd curdied curdi - curds curd cure cure - cured cure cureless cureless - curer curer cures cure - curfew curfew curing cure - curio curio curiosity curios - curious curiou curiously curious - curl curl curled curl - curling curl curls curl - currance curranc currants currant - current current currents current - currish currish curry curri - curs cur curse curs - cursed curs curses curs - cursies cursi cursing curs - cursorary cursorari curst curst - curster curster curstest curstest - curstness curst cursy cursi - curtail curtail curtain curtain - curtains curtain curtal curtal - curtis curti curtle curtl - curtsied curtsi curtsies curtsi - curtsy curtsi curvet curvet - curvets curvet cushes cush - cushion cushion cushions cushion - custalorum custalorum custard custard - custody custodi custom custom - customary customari customed custom - customer custom customers custom - customs custom custure custur - cut cut cutler cutler - cutpurse cutpurs cutpurses cutpurs - cuts cut cutter cutter - cutting cut cuttle cuttl - cxsar cxsar cyclops cyclop - cydnus cydnu cygnet cygnet - cygnets cygnet cym cym - cymbals cymbal cymbeline cymbelin - cyme cyme cynic cynic - cynthia cynthia cypress cypress - cypriot cypriot cyprus cypru - cyrus cyru cytherea cytherea - d d dabbled dabbl - dace dace dad dad - daedalus daedalu daemon daemon - daff daff daffed daf - daffest daffest daffodils daffodil - dagger dagger daggers dagger - dagonet dagonet daily daili - daintier daintier dainties dainti - daintiest daintiest daintily daintili - daintiness dainti daintry daintri - dainty dainti daisied daisi - daisies daisi daisy daisi - dale dale dalliance dallianc - dallied dalli dallies dalli - dally dalli dallying dalli - dalmatians dalmatian dam dam - damage damag damascus damascu - damask damask damasked damask - dame dame dames dame - damm damm damn damn - damnable damnabl damnably damnabl - damnation damnat damned damn - damns damn damoiselle damoisel - damon damon damosella damosella - damp damp dams dam - damsel damsel damsons damson - dan dan danc danc - dance danc dancer dancer - dances danc dancing danc - dandle dandl dandy dandi - dane dane dang dang - danger danger dangerous danger - dangerously danger dangers danger - dangling dangl daniel daniel - danish danish dank dank - dankish dankish danskers dansker - daphne daphn dappled dappl - dapples dappl dar dar - dardan dardan dardanian dardanian - dardanius dardaniu dare dare - dared dare dareful dare - dares dare darest darest - daring dare darius dariu - dark dark darken darken - darkening darken darkens darken - darker darker darkest darkest - darkling darkl darkly darkli - darkness dark darling darl - darlings darl darnel darnel - darraign darraign dart dart - darted dart darter darter - dartford dartford darting dart - darts dart dash dash - dashes dash dashing dash - dastard dastard dastards dastard - dat dat datchet datchet - date date dated date - dateless dateless dates date - daub daub daughter daughter - daughters daughter daunt daunt - daunted daunt dauntless dauntless - dauphin dauphin daventry daventri - davy davi daw daw - dawn dawn dawning dawn - daws daw day dai - daylight daylight days dai - dazzle dazzl dazzled dazzl - dazzling dazzl de de - dead dead deadly deadli - deaf deaf deafing deaf - deafness deaf deafs deaf - deal deal dealer dealer - dealers dealer dealest dealest - dealing deal dealings deal - deals deal dealt dealt - dean dean deanery deaneri - dear dear dearer dearer - dearest dearest dearly dearli - dearness dear dears dear - dearth dearth dearths dearth - death death deathbed deathb - deathful death deaths death - deathsman deathsman deathsmen deathsmen - debarred debar debase debas - debate debat debated debat - debatement debat debateth debateth - debating debat debauch debauch - debile debil debility debil - debitor debitor debonair debonair - deborah deborah debosh debosh - debt debt debted debt - debtor debtor debtors debtor - debts debt debuty debuti - decay decai decayed decai - decayer decay decaying decai - decays decai deceas decea - decease deceas deceased deceas - deceit deceit deceitful deceit - deceits deceit deceiv deceiv - deceivable deceiv deceive deceiv - deceived deceiv deceiver deceiv - deceivers deceiv deceives deceiv - deceivest deceivest deceiveth deceiveth - deceiving deceiv december decemb - decent decent deceptious decepti - decerns decern decide decid - decides decid decimation decim - decipher deciph deciphers deciph - decision decis decius deciu - deck deck decking deck - decks deck deckt deckt - declare declar declares declar - declension declens declensions declens - declin declin decline declin - declined declin declines declin - declining declin decoct decoct - decorum decorum decreas decrea - decrease decreas decreasing decreas - decree decre decreed decre - decrees decre decrepit decrepit - dedicate dedic dedicated dedic - dedicates dedic dedication dedic - deed deed deedless deedless - deeds deed deem deem - deemed deem deep deep - deeper deeper deepest deepest - deeply deepli deeps deep - deepvow deepvow deer deer - deesse deess defac defac - deface defac defaced defac - defacer defac defacers defac - defacing defac defam defam - default default defeat defeat - defeated defeat defeats defeat - defeatures defeatur defect defect - defective defect defects defect - defence defenc defences defenc - defend defend defendant defend - defended defend defender defend - defenders defend defending defend - defends defend defense defens - defensible defens defensive defens - defer defer deferr deferr - defiance defianc deficient defici - defied defi defies defi - defil defil defile defil - defiler defil defiles defil - defiling defil define defin - definement defin definite definit - definitive definit definitively definit - deflow deflow deflower deflow - deflowered deflow deform deform - deformed deform deformities deform - deformity deform deftly deftli - defunct defunct defunction defunct - defuse defus defy defi - defying defi degenerate degener - degraded degrad degree degre - degrees degre deified deifi - deifying deifi deign deign - deigned deign deiphobus deiphobu - deities deiti deity deiti - deja deja deject deject - dejected deject delabreth delabreth - delay delai delayed delai - delaying delai delays delai - delectable delect deliberate deliber - delicate delic delicates delic - delicious delici deliciousness delici - delight delight delighted delight - delightful delight delights delight - delinquents delinqu deliv deliv - deliver deliv deliverance deliver - delivered deliv delivering deliv - delivers deliv delivery deliveri - delphos delpho deluded delud - deluding delud deluge delug - delve delv delver delver - delves delv demand demand - demanded demand demanding demand - demands demand demean demean - demeanor demeanor demeanour demeanour - demerits demerit demesnes demesn - demetrius demetriu demi demi - demigod demigod demise demis - demoiselles demoisel demon demon - demonstrable demonstr demonstrate demonstr - demonstrated demonstr demonstrating demonstr - demonstration demonstr demonstrative demonstr - demure demur demurely demur - demuring demur den den - denay denai deni deni - denial denial denials denial - denied deni denier denier - denies deni deniest deniest - denis deni denmark denmark - dennis denni denny denni - denote denot denoted denot - denotement denot denounc denounc - denounce denounc denouncing denounc - dens den denunciation denunci - deny deni denying deni - deo deo depart depart - departed depart departest departest - departing depart departure departur - depeche depech depend depend - dependant depend dependants depend - depended depend dependence depend - dependences depend dependency depend - dependent depend dependents depend - depender depend depending depend - depends depend deplore deplor - deploring deplor depopulate depopul - depos depo depose depos - deposed depos deposing depos - depositaries depositari deprav deprav - depravation deprav deprave deprav - depraved deprav depraves deprav - depress depress depriv depriv - deprive depriv depth depth - depths depth deputation deput - depute deput deputed deput - deputies deputi deputing deput - deputy deputi deracinate deracin - derby derbi dercetas derceta - dere dere derides derid - derision deris deriv deriv - derivation deriv derivative deriv - derive deriv derived deriv - derives deriv derogate derog - derogately derog derogation derog - des de desartless desartless - descant descant descend descend - descended descend descending descend - descends descend descension descens - descent descent descents descent - describe describ described describ - describes describ descried descri - description descript descriptions descript - descry descri desdemon desdemon - desdemona desdemona desert desert - deserts desert deserv deserv - deserve deserv deserved deserv - deservedly deservedli deserver deserv - deservers deserv deserves deserv - deservest deservest deserving deserv - deservings deserv design design - designment design designments design - designs design desir desir - desire desir desired desir - desirers desir desires desir - desirest desirest desiring desir - desirous desir desist desist - desk desk desolate desol - desolation desol desp desp - despair despair despairing despair - despairs despair despatch despatch - desperate desper desperately desper - desperation desper despis despi - despise despis despised despis - despiser despis despiseth despiseth - despising despis despite despit - despiteful despit despoiled despoil - dest dest destin destin - destined destin destinies destini - destiny destini destitute destitut - destroy destroi destroyed destroi - destroyer destroy destroyers destroy - destroying destroi destroys destroi - destruction destruct destructions destruct - det det detain detain - detains detain detect detect - detected detect detecting detect - detection detect detector detector - detects detect detention detent - determin determin determinate determin - determination determin determinations determin - determine determin determined determin - determines determin detest detest - detestable detest detested detest - detesting detest detests detest - detract detract detraction detract - detractions detract deucalion deucalion - deuce deuc deum deum - deux deux devant devant - devesting devest device devic - devices devic devil devil - devilish devilish devils devil - devis devi devise devis - devised devis devises devis - devising devis devoid devoid - devonshire devonshir devote devot - devoted devot devotion devot - devour devour devoured devour - devourers devour devouring devour - devours devour devout devout - devoutly devoutli dew dew - dewberries dewberri dewdrops dewdrop - dewlap dewlap dewlapp dewlapp - dews dew dewy dewi - dexter dexter dexteriously dexteri - dexterity dexter di di - diable diabl diablo diablo - diadem diadem dial dial - dialect dialect dialogue dialogu - dialogued dialogu dials dial - diameter diamet diamond diamond - diamonds diamond dian dian - diana diana diaper diaper - dibble dibbl dic dic - dice dice dicers dicer - dich dich dick dick - dickens dicken dickon dickon - dicky dicki dictator dictat - diction diction dictynna dictynna - did did diddle diddl - didest didest dido dido - didst didst die die - died di diedst diedst - dies di diest diest - diet diet dieted diet - dieter dieter dieu dieu - diff diff differ differ - difference differ differences differ - differency differ different differ - differing differ differs differ - difficile difficil difficult difficult - difficulties difficulti difficulty difficulti - diffidence diffid diffidences diffid - diffus diffu diffused diffus - diffusest diffusest dig dig - digest digest digested digest - digestion digest digestions digest - digg digg digging dig - dighton dighton dignified dignifi - dignifies dignifi dignify dignifi - dignities digniti dignity digniti - digress digress digressing digress - digression digress digs dig - digt digt dilate dilat - dilated dilat dilations dilat - dilatory dilatori dild dild - dildos dildo dilemma dilemma - dilemmas dilemma diligence dilig - diligent dilig diluculo diluculo - dim dim dimension dimens - dimensions dimens diminish diminish - diminishing diminish diminution diminut - diminutive diminut diminutives diminut - dimm dimm dimmed dim - dimming dim dimpled dimpl - dimples dimpl dims dim - din din dine dine - dined dine diner diner - dines dine ding ding - dining dine dinner dinner - dinners dinner dinnertime dinnertim - dint dint diomed diom - diomede diomed diomedes diomed - dion dion dip dip - dipp dipp dipping dip - dips dip dir dir - dire dire direct direct - directed direct directing direct - direction direct directions direct - directitude directitud directive direct - directly directli directs direct - direful dire direness dire - direst direst dirge dirg - dirges dirg dirt dirt - dirty dirti dis di - disability disabl disable disabl - disabled disabl disabling disabl - disadvantage disadvantag disagree disagre - disallow disallow disanimates disanim - disannul disannul disannuls disannul - disappointed disappoint disarm disarm - disarmed disarm disarmeth disarmeth - disarms disarm disaster disast - disasters disast disastrous disastr - disbench disbench disbranch disbranch - disburdened disburden disburs disbur - disburse disburs disbursed disburs - discandy discandi discandying discandi - discard discard discarded discard - discase discas discased discas - discern discern discerner discern - discerning discern discernings discern - discerns discern discharg discharg - discharge discharg discharged discharg - discharging discharg discipled discipl - disciples discipl disciplin disciplin - discipline disciplin disciplined disciplin - disciplines disciplin disclaim disclaim - disclaiming disclaim disclaims disclaim - disclos disclo disclose disclos - disclosed disclos discloses disclos - discolour discolour discoloured discolour - discolours discolour discomfit discomfit - discomfited discomfit discomfiture discomfitur - discomfort discomfort discomfortable discomfort - discommend discommend disconsolate disconsol - discontent discont discontented discont - discontentedly discontentedli discontenting discont - discontents discont discontinue discontinu - discontinued discontinu discord discord - discordant discord discords discord - discourse discours discoursed discours - discourser discours discourses discours - discoursive discours discourtesy discourtesi - discov discov discover discov - discovered discov discoverers discover - discoveries discoveri discovering discov - discovers discov discovery discoveri - discredit discredit discredited discredit - discredits discredit discreet discreet - discreetly discreetli discretion discret - discretions discret discuss discuss - disdain disdain disdained disdain - disdaineth disdaineth disdainful disdain - disdainfully disdainfulli disdaining disdain - disdains disdain disdnguish disdnguish - diseas disea disease diseas - diseased diseas diseases diseas - disedg disedg disembark disembark - disfigure disfigur disfigured disfigur - disfurnish disfurnish disgorge disgorg - disgrac disgrac disgrace disgrac - disgraced disgrac disgraceful disgrac - disgraces disgrac disgracing disgrac - disgracious disgraci disguis disgui - disguise disguis disguised disguis - disguiser disguis disguises disguis - disguising disguis dish dish - dishabited dishabit dishclout dishclout - dishearten dishearten disheartens dishearten - dishes dish dishonest dishonest - dishonestly dishonestli dishonesty dishonesti - dishonor dishonor dishonorable dishonor - dishonors dishonor dishonour dishonour - dishonourable dishonour dishonoured dishonour - dishonours dishonour disinherit disinherit - disinherited disinherit disjoin disjoin - disjoining disjoin disjoins disjoin - disjoint disjoint disjunction disjunct - dislik dislik dislike dislik - disliken disliken dislikes dislik - dislimns dislimn dislocate disloc - dislodg dislodg disloyal disloy - disloyalty disloyalti dismal dismal - dismantle dismantl dismantled dismantl - dismask dismask dismay dismai - dismayed dismai dismemb dismemb - dismember dismemb dismes dism - dismiss dismiss dismissed dismiss - dismissing dismiss dismission dismiss - dismount dismount dismounted dismount - disnatur disnatur disobedience disobedi - disobedient disobedi disobey disobei - disobeys disobei disorb disorb - disorder disord disordered disord - disorderly disorderli disorders disord - disparage disparag disparagement disparag - disparagements disparag dispark dispark - dispatch dispatch dispensation dispens - dispense dispens dispenses dispens - dispers disper disperse dispers - dispersed dispers dispersedly dispersedli - dispersing dispers dispiteous dispit - displac displac displace displac - displaced displac displant displant - displanting displant display displai - displayed displai displeas displea - displease displeas displeased displeas - displeasing displeas displeasure displeasur - displeasures displeasur disponge dispong - disport disport disports disport - dispos dispo dispose dispos - disposed dispos disposer dispos - disposing dispos disposition disposit - dispositions disposit dispossess dispossess - dispossessing dispossess disprais disprai - dispraise disprais dispraising disprais - dispraisingly dispraisingli dispropertied disproperti - disproportion disproport disproportioned disproport - disprov disprov disprove disprov - disproved disprov dispursed dispurs - disputable disput disputation disput - disputations disput dispute disput - disputed disput disputes disput - disputing disput disquantity disquant - disquiet disquiet disquietly disquietli - disrelish disrelish disrobe disrob - disseat disseat dissemble dissembl - dissembled dissembl dissembler dissembl - dissemblers dissembl dissembling dissembl - dissembly dissembl dissension dissens - dissensions dissens dissentious dissenti - dissever dissev dissipation dissip - dissolute dissolut dissolutely dissolut - dissolution dissolut dissolutions dissolut - dissolv dissolv dissolve dissolv - dissolved dissolv dissolves dissolv - dissuade dissuad dissuaded dissuad - distaff distaff distaffs distaff - distain distain distains distain - distance distanc distant distant - distaste distast distasted distast - distasteful distast distemp distemp - distemper distemp distemperature distemperatur - distemperatures distemperatur distempered distemp - distempering distemp distil distil - distill distil distillation distil - distilled distil distills distil - distilment distil distinct distinct - distinction distinct distinctly distinctli - distingue distingu distinguish distinguish - distinguishes distinguish distinguishment distinguish - distract distract distracted distract - distractedly distractedli distraction distract - distractions distract distracts distract - distrain distrain distraught distraught - distress distress distressed distress - distresses distress distressful distress - distribute distribut distributed distribut - distribution distribut distrust distrust - distrustful distrust disturb disturb - disturbed disturb disturbers disturb - disturbing disturb disunite disunit - disvalued disvalu disvouch disvouch - dit dit ditch ditch - ditchers ditcher ditches ditch - dites dite ditties ditti - ditty ditti diurnal diurnal - div div dive dive - diver diver divers diver - diversely divers diversity divers - divert divert diverted divert - diverts divert dives dive - divest divest dividable divid - dividant divid divide divid - divided divid divides divid - divideth divideth divin divin - divination divin divine divin - divinely divin divineness divin - diviner divin divines divin - divinest divinest divining divin - divinity divin division divis - divisions divis divorc divorc - divorce divorc divorced divorc - divorcement divorc divorcing divorc - divulg divulg divulge divulg - divulged divulg divulging divulg - dizy dizi dizzy dizzi - do do doating doat - dobbin dobbin dock dock - docks dock doct doct - doctor doctor doctors doctor - doctrine doctrin document document - dodge dodg doe doe - doer doer doers doer - does doe doest doest - doff doff dog dog - dogberry dogberri dogfish dogfish - dogg dogg dogged dog - dogs dog doigts doigt - doing do doings do - doit doit doits doit - dolabella dolabella dole dole - doleful dole doll doll - dollar dollar dollars dollar - dolor dolor dolorous dolor - dolour dolour dolours dolour - dolphin dolphin dolt dolt - dolts dolt domestic domest - domestics domest dominance domin - dominations domin dominator domin - domine domin domineer domin - domineering domin dominical domin - dominion dominion dominions dominion - domitius domitiu dommelton dommelton - don don donalbain donalbain - donation donat donc donc - doncaster doncast done done - dong dong donn donn - donne donn donner donner - donnerai donnerai doom doom - doomsday doomsdai door door - doorkeeper doorkeep doors door - dorcas dorca doreus doreu - doricles doricl dormouse dormous - dorothy dorothi dorset dorset - dorsetshire dorsetshir dost dost - dotage dotag dotant dotant - dotard dotard dotards dotard - dote dote doted dote - doters doter dotes dote - doteth doteth doth doth - doting dote double doubl - doubled doubl doubleness doubl - doubler doubler doublet doublet - doublets doublet doubling doubl - doubly doubli doubt doubt - doubted doubt doubtful doubt - doubtfully doubtfulli doubting doubt - doubtless doubtless doubts doubt - doug doug dough dough - doughty doughti doughy doughi - douglas dougla dout dout - doute dout douts dout - dove dove dovehouse dovehous - dover dover doves dove - dow dow dowager dowag - dowdy dowdi dower dower - dowerless dowerless dowers dower - dowlas dowla dowle dowl - down down downfall downfal - downright downright downs down - downstairs downstair downtrod downtrod - downward downward downwards downward - downy downi dowries dowri - dowry dowri dowsabel dowsabel - doxy doxi dozed doze - dozen dozen dozens dozen - dozy dozi drab drab - drabbing drab drabs drab - drachma drachma drachmas drachma - draff draff drag drag - dragg dragg dragged drag - dragging drag dragon dragon - dragonish dragonish dragons dragon - drain drain drained drain - drains drain drake drake - dram dram dramatis dramati - drank drank draught draught - draughts draught drave drave - draw draw drawbridge drawbridg - drawer drawer drawers drawer - draweth draweth drawing draw - drawling drawl drawn drawn - draws draw drayman drayman - draymen draymen dread dread - dreaded dread dreadful dread - dreadfully dreadfulli dreading dread - dreads dread dream dream - dreamer dreamer dreamers dreamer - dreaming dream dreams dream - dreamt dreamt drearning drearn - dreary dreari dreg dreg - dregs dreg drench drench - drenched drench dress dress - dressed dress dresser dresser - dressing dress dressings dress - drest drest drew drew - dribbling dribbl dried dri - drier drier dries dri - drift drift drily drili - drink drink drinketh drinketh - drinking drink drinkings drink - drinks drink driv driv - drive drive drivelling drivel - driven driven drives drive - driveth driveth driving drive - drizzle drizzl drizzled drizzl - drizzles drizzl droit droit - drollery drolleri dromio dromio - dromios dromio drone drone - drones drone droop droop - droopeth droopeth drooping droop - droops droop drop drop - dropheir dropheir droplets droplet - dropp dropp dropper dropper - droppeth droppeth dropping drop - droppings drop drops drop - dropsied dropsi dropsies dropsi - dropsy dropsi dropt dropt - dross dross drossy drossi - drought drought drove drove - droven droven drovier drovier - drown drown drowned drown - drowning drown drowns drown - drows drow drowse drows - drowsily drowsili drowsiness drowsi - drowsy drowsi drudge drudg - drudgery drudgeri drudges drudg - drug drug drugg drugg - drugs drug drum drum - drumble drumbl drummer drummer - drumming drum drums drum - drunk drunk drunkard drunkard - drunkards drunkard drunken drunken - drunkenly drunkenli drunkenness drunken - dry dry dryness dryness - dst dst du du - dub dub dubb dubb - ducat ducat ducats ducat - ducdame ducdam duchess duchess - duchies duchi duchy duchi - duck duck ducking duck - ducks duck dudgeon dudgeon - due due duellist duellist - duello duello duer duer - dues due duff duff - dug dug dugs dug - duke duke dukedom dukedom - dukedoms dukedom dukes duke - dulcet dulcet dulche dulch - dull dull dullard dullard - duller duller dullest dullest - dulling dull dullness dull - dulls dull dully dulli - dulness dul duly duli - dumain dumain dumb dumb - dumbe dumb dumbly dumbl - dumbness dumb dump dump - dumps dump dun dun - duncan duncan dung dung - dungeon dungeon dungeons dungeon - dunghill dunghil dunghills dunghil - dungy dungi dunnest dunnest - dunsinane dunsinan dunsmore dunsmor - dunstable dunstabl dupp dupp - durance duranc during dure - durst durst dusky duski - dust dust dusted dust - dusty dusti dutch dutch - dutchman dutchman duteous duteou - duties duti dutiful duti - duty duti dwarf dwarf - dwarfish dwarfish dwell dwell - dwellers dweller dwelling dwell - dwells dwell dwelt dwelt - dwindle dwindl dy dy - dye dye dyed dy - dyer dyer dying dy - e e each each - eager eager eagerly eagerli - eagerness eager eagle eagl - eagles eagl eaning ean - eanlings eanl ear ear - earing ear earl earl - earldom earldom earlier earlier - earliest earliest earliness earli - earls earl early earli - earn earn earned earn - earnest earnest earnestly earnestli - earnestness earnest earns earn - ears ear earth earth - earthen earthen earthlier earthlier - earthly earthli earthquake earthquak - earthquakes earthquak earthy earthi - eas ea ease eas - eased eas easeful eas - eases eas easier easier - easiest easiest easiliest easiliest - easily easili easiness easi - easing eas east east - eastcheap eastcheap easter easter - eastern eastern eastward eastward - easy easi eat eat - eaten eaten eater eater - eaters eater eating eat - eats eat eaux eaux - eaves eav ebb ebb - ebbing eb ebbs ebb - ebon ebon ebony eboni - ebrew ebrew ecce ecc - echapper echapp echo echo - echoes echo eclips eclip - eclipse eclips eclipses eclips - ecolier ecoli ecoutez ecoutez - ecstacy ecstaci ecstasies ecstasi - ecstasy ecstasi ecus ecu - eden eden edg edg - edgar edgar edge edg - edged edg edgeless edgeless - edges edg edict edict - edicts edict edifice edific - edifices edific edified edifi - edifies edifi edition edit - edm edm edmund edmund - edmunds edmund edmundsbury edmundsburi - educate educ educated educ - education educ edward edward - eel eel eels eel - effect effect effected effect - effectless effectless effects effect - effectual effectu effectually effectu - effeminate effemin effigies effigi - effus effu effuse effus - effusion effus eftest eftest - egal egal egally egal - eget eget egeus egeu - egg egg eggs egg - eggshell eggshel eglamour eglamour - eglantine eglantin egma egma - ego ego egregious egregi - egregiously egregi egress egress - egypt egypt egyptian egyptian - egyptians egyptian eie eie - eight eight eighteen eighteen - eighth eighth eightpenny eightpenni - eighty eighti eisel eisel - either either eject eject - eke ek el el - elbe elb elbow elbow - elbows elbow eld eld - elder elder elders elder - eldest eldest eleanor eleanor - elect elect elected elect - election elect elegancy eleg - elegies elegi element element - elements element elephant eleph - elephants eleph elevated elev - eleven eleven eleventh eleventh - elf elf elflocks elflock - eliads eliad elinor elinor - elizabeth elizabeth ell ell - elle ell ellen ellen - elm elm eloquence eloqu - eloquent eloqu else els - elsewhere elsewher elsinore elsinor - eltham eltham elves elv - elvish elvish ely eli - elysium elysium em em - emballing embal embalm embalm - embalms embalm embark embark - embarked embark embarquements embarqu - embassade embassad embassage embassag - embassies embassi embassy embassi - embattailed embattail embattl embattl - embattle embattl embay embai - embellished embellish embers ember - emblaze emblaz emblem emblem - emblems emblem embodied embodi - embold embold emboldens embolden - emboss emboss embossed emboss - embounded embound embowel embowel - embowell embowel embrac embrac - embrace embrac embraced embrac - embracement embrac embracements embrac - embraces embrac embracing embrac - embrasures embrasur embroider embroid - embroidery embroideri emhracing emhrac - emilia emilia eminence emin - eminent emin eminently emin - emmanuel emmanuel emnity emniti - empale empal emperal emper - emperess emperess emperial emperi - emperor emperor empery emperi - emphasis emphasi empire empir - empirics empir empiricutic empiricut - empleached empleach employ emploi - employed emploi employer employ - employment employ employments employ - empoison empoison empress empress - emptied empti emptier emptier - empties empti emptiness empti - empty empti emptying empti - emulate emul emulation emul - emulations emul emulator emul - emulous emul en en - enact enact enacted enact - enacts enact enactures enactur - enamell enamel enamelled enamel - enamour enamour enamoured enamour - enanmour enanmour encamp encamp - encamped encamp encave encav - enceladus enceladu enchaf enchaf - enchafed enchaf enchant enchant - enchanted enchant enchanting enchant - enchantingly enchantingli enchantment enchant - enchantress enchantress enchants enchant - enchas encha encircle encircl - encircled encircl enclos enclo - enclose enclos enclosed enclos - encloses enclos encloseth encloseth - enclosing enclos enclouded encloud - encompass encompass encompassed encompass - encompasseth encompasseth encompassment encompass - encore encor encorporal encorpor - encount encount encounter encount - encountered encount encounters encount - encourage encourag encouraged encourag - encouragement encourag encrimsoned encrimson - encroaching encroach encumb encumb - end end endamage endamag - endamagement endamag endanger endang - endart endart endear endear - endeared endear endeavour endeavour - endeavours endeavour ended end - ender ender ending end - endings end endite endit - endless endless endow endow - endowed endow endowments endow - endows endow ends end - endu endu endue endu - endur endur endurance endur - endure endur endured endur - endures endur enduring endur - endymion endymion eneas enea - enemies enemi enemy enemi - enernies enerni enew enew - enfeebled enfeebl enfeebles enfeebl - enfeoff enfeoff enfetter enfett - enfoldings enfold enforc enforc - enforce enforc enforced enforc - enforcedly enforcedli enforcement enforc - enforces enforc enforcest enforcest - enfranched enfranch enfranchis enfranchi - enfranchise enfranchis enfranchised enfranchis - enfranchisement enfranchis enfreed enfre - enfreedoming enfreedom engag engag - engage engag engaged engag - engagements engag engaging engag - engaol engaol engend engend - engender engend engenders engend - engilds engild engine engin - engineer engin enginer engin - engines engin engirt engirt - england england english english - englishman englishman englishmen englishmen - engluts englut englutted englut - engraffed engraf engraft engraft - engrafted engraft engrav engrav - engrave engrav engross engross - engrossed engross engrossest engrossest - engrossing engross engrossments engross - enguard enguard enigma enigma - enigmatical enigmat enjoin enjoin - enjoined enjoin enjoy enjoi - enjoyed enjoi enjoyer enjoy - enjoying enjoi enjoys enjoi - enkindle enkindl enkindled enkindl - enlard enlard enlarg enlarg - enlarge enlarg enlarged enlarg - enlargement enlarg enlargeth enlargeth - enlighten enlighten enlink enlink - enmesh enmesh enmities enmiti - enmity enmiti ennoble ennobl - ennobled ennobl enobarb enobarb - enobarbus enobarbu enon enon - enormity enorm enormous enorm - enough enough enow enow - enpatron enpatron enpierced enpierc - enquir enquir enquire enquir - enquired enquir enrag enrag - enrage enrag enraged enrag - enrages enrag enrank enrank - enrapt enrapt enrich enrich - enriched enrich enriches enrich - enridged enridg enrings enr - enrob enrob enrobe enrob - enroll enrol enrolled enrol - enrooted enroot enrounded enround - enschedul enschedul ensconce ensconc - ensconcing ensconc enseamed enseam - ensear ensear enseigne enseign - enseignez enseignez ensemble ensembl - enshelter enshelt enshielded enshield - enshrines enshrin ensign ensign - ensigns ensign enskied enski - ensman ensman ensnare ensnar - ensnared ensnar ensnareth ensnareth - ensteep ensteep ensu ensu - ensue ensu ensued ensu - ensues ensu ensuing ensu - enswathed enswath ent ent - entail entail entame entam - entangled entangl entangles entangl - entendre entendr enter enter - entered enter entering enter - enterprise enterpris enterprises enterpris - enters enter entertain entertain - entertained entertain entertainer entertain - entertaining entertain entertainment entertain - entertainments entertain enthrall enthral - enthralled enthral enthron enthron - enthroned enthron entice entic - enticements entic enticing entic - entire entir entirely entir - entitle entitl entitled entitl - entitling entitl entomb entomb - entombed entomb entrails entrail - entrance entranc entrances entranc - entrap entrap entrapp entrapp - entre entr entreat entreat - entreated entreat entreaties entreati - entreating entreat entreatments entreat - entreats entreat entreaty entreati - entrench entrench entry entri - entwist entwist envelop envelop - envenom envenom envenomed envenom - envenoms envenom envied envi - envies envi envious enviou - enviously envious environ environ - environed environ envoy envoi - envy envi envying envi - enwheel enwheel enwombed enwomb - enwraps enwrap ephesian ephesian - ephesians ephesian ephesus ephesu - epicure epicur epicurean epicurean - epicures epicur epicurism epicur - epicurus epicuru epidamnum epidamnum - epidaurus epidauru epigram epigram - epilepsy epilepsi epileptic epilept - epilogue epilogu epilogues epilogu - epistles epistl epistrophus epistrophu - epitaph epitaph epitaphs epitaph - epithet epithet epitheton epitheton - epithets epithet epitome epitom - equal equal equalities equal - equality equal equall equal - equally equal equalness equal - equals equal equinoctial equinocti - equinox equinox equipage equipag - equity equiti equivocal equivoc - equivocate equivoc equivocates equivoc - equivocation equivoc equivocator equivoc - er er erbear erbear - erbearing erbear erbears erbear - erbeat erbeat erblows erblow - erboard erboard erborne erborn - ercame ercam ercast ercast - ercharg ercharg ercharged ercharg - ercharging ercharg ercles ercl - ercome ercom ercover ercov - ercrows ercrow erdoing erdo - ere er erebus erebu - erect erect erected erect - erecting erect erection erect - erects erect erewhile erewhil - erflourish erflourish erflow erflow - erflowing erflow erflows erflow - erfraught erfraught erga erga - ergalled ergal erglanced erglanc - ergo ergo ergone ergon - ergrow ergrow ergrown ergrown - ergrowth ergrowth erhang erhang - erhanging erhang erhasty erhasti - erhear erhear erheard erheard - eringoes eringo erjoy erjoi - erleap erleap erleaps erleap - erleavens erleaven erlook erlook - erlooking erlook ermaster ermast - ermengare ermengar ermount ermount - ern ern ernight ernight - eros ero erpaid erpaid - erparted erpart erpast erpast - erpays erpai erpeer erpeer - erperch erperch erpicturing erpictur - erpingham erpingham erposting erpost - erpow erpow erpress erpress - erpressed erpress err err - errand errand errands errand - errant errant errate errat - erraught erraught erreaches erreach - erred er errest errest - erring er erroneous erron - error error errors error - errs err errule errul - errun errun erset erset - ershade ershad ershades ershad - ershine ershin ershot ershot - ersized ersiz erskip erskip - erslips erslip erspreads erspread - erst erst erstare erstar - erstep erstep erstunk erstunk - ersway erswai ersways erswai - erswell erswel erta erta - ertake ertak erteemed erteem - erthrow erthrow erthrown erthrown - erthrows erthrow ertook ertook - ertop ertop ertopping ertop - ertrip ertrip erturn erturn - erudition erudit eruption erupt - eruptions erupt ervalues ervalu - erwalk erwalk erwatch erwatch - erween erween erweens erween - erweigh erweigh erweighs erweigh - erwhelm erwhelm erwhelmed erwhelm - erworn erworn es es - escalus escalu escap escap - escape escap escaped escap - escapes escap eschew eschew - escoted escot esill esil - especial especi especially especi - esperance esper espials espial - espied espi espies espi - espous espou espouse espous - espy espi esquire esquir - esquires esquir essay essai - essays essai essence essenc - essential essenti essentially essenti - esses ess essex essex - est est establish establish - established establish estate estat - estates estat esteem esteem - esteemed esteem esteemeth esteemeth - esteeming esteem esteems esteem - estimable estim estimate estim - estimation estim estimations estim - estime estim estranged estrang - estridge estridg estridges estridg - et et etc etc - etceteras etcetera ete et - eternal etern eternally etern - eterne etern eternity etern - eterniz eterniz etes et - ethiop ethiop ethiope ethiop - ethiopes ethiop ethiopian ethiopian - etna etna eton eton - etre etr eunuch eunuch - eunuchs eunuch euphrates euphrat - euphronius euphroniu euriphile euriphil - europa europa europe europ - ev ev evade evad - evades evad evans evan - evasion evas evasions evas - eve ev even even - evening even evenly evenli - event event eventful event - events event ever ever - everlasting everlast everlastingly everlastingli - evermore evermor every everi - everyone everyon everything everyth - everywhere everywher evidence evid - evidences evid evident evid - evil evil evilly evilli - evils evil evitate evit - ewe ew ewer ewer - ewers ewer ewes ew - exact exact exacted exact - exactest exactest exacting exact - exaction exact exactions exact - exactly exactli exacts exact - exalt exalt exalted exalt - examin examin examination examin - examinations examin examine examin - examined examin examines examin - exampl exampl example exampl - exampled exampl examples exampl - exasperate exasper exasperates exasper - exceed exce exceeded exceed - exceedeth exceedeth exceeding exceed - exceedingly exceedingli exceeds exce - excel excel excelled excel - excellence excel excellencies excel - excellency excel excellent excel - excellently excel excelling excel - excels excel except except - excepted except excepting except - exception except exceptions except - exceptless exceptless excess excess - excessive excess exchang exchang - exchange exchang exchanged exchang - exchequer exchequ exchequers exchequ - excite excit excited excit - excitements excit excites excit - exclaim exclaim exclaims exclaim - exclamation exclam exclamations exclam - excludes exclud excommunicate excommun - excommunication excommun excrement excrement - excrements excrement excursion excurs - excursions excurs excus excu - excusable excus excuse excus - excused excus excuses excus - excusez excusez excusing excus - execrable execr execrations execr - execute execut executed execut - executing execut execution execut - executioner execution executioners execution - executor executor executors executor - exempt exempt exempted exempt - exequies exequi exercise exercis - exercises exercis exeter exet - exeunt exeunt exhal exhal - exhalation exhal exhalations exhal - exhale exhal exhales exhal - exhaust exhaust exhibit exhibit - exhibiters exhibit exhibition exhibit - exhort exhort exhortation exhort - exigent exig exil exil - exile exil exiled exil - exion exion exist exist - exists exist exit exit - exits exit exorciser exorcis - exorcisms exorc exorcist exorcist - expect expect expectance expect - expectancy expect expectation expect - expectations expect expected expect - expecters expect expecting expect - expects expect expedience expedi - expedient expedi expediently expedi - expedition expedit expeditious expediti - expel expel expell expel - expelling expel expels expel - expend expend expense expens - expenses expens experienc experienc - experience experi experiences experi - experiment experi experimental experiment - experiments experi expert expert - expertness expert expiate expiat - expiation expiat expir expir - expiration expir expire expir - expired expir expires expir - expiring expir explication explic - exploit exploit exploits exploit - expos expo expose expos - exposing expos exposition exposit - expositor expositor expostulate expostul - expostulation expostul exposture expostur - exposure exposur expound expound - expounded expound express express - expressed express expresseth expresseth - expressing express expressive express - expressly expressli expressure expressur - expuls expul expulsion expuls - exquisite exquisit exsufflicate exsuffl - extant extant extemporal extempor - extemporally extempor extempore extempor - extend extend extended extend - extends extend extent extent - extenuate extenu extenuated extenu - extenuates extenu extenuation extenu - exterior exterior exteriorly exteriorli - exteriors exterior extermin extermin - extern extern external extern - extinct extinct extincted extinct - extincture extinctur extinguish extinguish - extirp extirp extirpate extirp - extirped extirp extol extol - extoll extol extolment extol - exton exton extort extort - extorted extort extortion extort - extortions extort extra extra - extract extract extracted extract - extracting extract extraordinarily extraordinarili - extraordinary extraordinari extraught extraught - extravagancy extravag extravagant extravag - extreme extrem extremely extrem - extremes extrem extremest extremest - extremities extrem extremity extrem - exuent exuent exult exult - exultation exult ey ey - eyas eya eyases eyas - eye ey eyeball eyebal - eyeballs eyebal eyebrow eyebrow - eyebrows eyebrow eyed ei - eyeless eyeless eyelid eyelid - eyelids eyelid eyes ey - eyesight eyesight eyestrings eyestr - eying ei eyne eyn - eyrie eyri fa fa - fabian fabian fable fabl - fables fabl fabric fabric - fabulous fabul fac fac - face face faced face - facere facer faces face - faciant faciant facile facil - facility facil facinerious facineri - facing face facit facit - fact fact faction faction - factionary factionari factions faction - factious factiou factor factor - factors factor faculties faculti - faculty faculti fade fade - faded fade fadeth fadeth - fadge fadg fading fade - fadings fade fadom fadom - fadoms fadom fagot fagot - fagots fagot fail fail - failing fail fails fail - fain fain faint faint - fainted faint fainter fainter - fainting faint faintly faintli - faintness faint faints faint - fair fair fairer fairer - fairest fairest fairies fairi - fairing fair fairings fair - fairly fairli fairness fair - fairs fair fairwell fairwel - fairy fairi fais fai - fait fait faites fait - faith faith faithful faith - faithfull faithful faithfully faithfulli - faithless faithless faiths faith - faitors faitor fal fal - falchion falchion falcon falcon - falconbridge falconbridg falconer falcon - falconers falcon fall fall - fallacy fallaci fallen fallen - falleth falleth falliable falliabl - fallible fallibl falling fall - fallow fallow fallows fallow - falls fall fally falli - falorous falor false fals - falsehood falsehood falsely fals - falseness fals falser falser - falsify falsifi falsing fals - falstaff falstaff falstaffs falstaff - falter falter fam fam - fame fame famed fame - familiar familiar familiarity familiar - familiarly familiarli familiars familiar - family famili famine famin - famish famish famished famish - famous famou famoused famous - famously famous fan fan - fanatical fanat fancies fanci - fancy fanci fane fane - fanes fane fang fang - fangled fangl fangless fangless - fangs fang fann fann - fanning fan fans fan - fantasied fantasi fantasies fantasi - fantastic fantast fantastical fantast - fantastically fantast fantasticoes fantastico - fantasy fantasi fap fap - far far farborough farborough - farced farc fardel fardel - fardels fardel fare fare - fares fare farewell farewel - farewells farewel fariner farin - faring fare farm farm - farmer farmer farmhouse farmhous - farms farm farre farr - farrow farrow farther farther - farthest farthest farthing farth - farthingale farthingal farthingales farthingal - farthings farth fartuous fartuou - fas fa fashion fashion - fashionable fashion fashioning fashion - fashions fashion fast fast - fasted fast fasten fasten - fastened fasten faster faster - fastest fastest fasting fast - fastly fastli fastolfe fastolf - fasts fast fat fat - fatal fatal fatally fatal - fate fate fated fate - fates fate father father - fathered father fatherless fatherless - fatherly fatherli fathers father - fathom fathom fathomless fathomless - fathoms fathom fatigate fatig - fatness fat fats fat - fatted fat fatter fatter - fattest fattest fatting fat - fatuus fatuu fauconbridge fauconbridg - faulconbridge faulconbridg fault fault - faultiness faulti faultless faultless - faults fault faulty faulti - fausse fauss fauste faust - faustuses faustus faut faut - favor favor favorable favor - favorably favor favors favor - favour favour favourable favour - favoured favour favouredly favouredli - favourer favour favourers favour - favouring favour favourite favourit - favourites favourit favours favour - favout favout fawn fawn - fawneth fawneth fawning fawn - fawns fawn fay fai - fe fe fealty fealti - fear fear feared fear - fearest fearest fearful fear - fearfull fearful fearfully fearfulli - fearfulness fear fearing fear - fearless fearless fears fear - feast feast feasted feast - feasting feast feasts feast - feat feat feated feat - feater feater feather feather - feathered feather feathers feather - featly featli feats feat - featur featur feature featur - featured featur featureless featureless - features featur february februari - fecks feck fed fed - fedary fedari federary federari - fee fee feeble feebl - feebled feebl feebleness feebl - feebling feebl feebly feebli - feed feed feeder feeder - feeders feeder feedeth feedeth - feeding feed feeds feed - feel feel feeler feeler - feeling feel feelingly feelingli - feels feel fees fee - feet feet fehemently fehement - feign feign feigned feign - feigning feign feil feil - feith feith felicitate felicit - felicity felic fell fell - fellest fellest fellies felli - fellow fellow fellowly fellowli - fellows fellow fellowship fellowship - fellowships fellowship fells fell - felon felon felonious feloni - felony feloni felt felt - female femal females femal - feminine feminin fen fen - fenc fenc fence fenc - fencer fencer fencing fenc - fends fend fennel fennel - fenny fenni fens fen - fenton fenton fer fer - ferdinand ferdinand fere fere - fernseed fernse ferrara ferrara - ferrers ferrer ferret ferret - ferry ferri ferryman ferryman - fertile fertil fertility fertil - fervency fervenc fervour fervour - fery feri fest fest - feste fest fester fester - festinate festin festinately festin - festival festiv festivals festiv - fet fet fetch fetch - fetches fetch fetching fetch - fetlock fetlock fetlocks fetlock - fett fett fetter fetter - fettering fetter fetters fetter - fettle fettl feu feu - feud feud fever fever - feverous fever fevers fever - few few fewer fewer - fewest fewest fewness few - fickle fickl fickleness fickl - fico fico fiction fiction - fiddle fiddl fiddler fiddler - fiddlestick fiddlestick fidele fidel - fidelicet fidelicet fidelity fidel - fidius fidiu fie fie - field field fielded field - fields field fiend fiend - fiends fiend fierce fierc - fiercely fierc fierceness fierc - fiery fieri fife fife - fifes fife fifteen fifteen - fifteens fifteen fifteenth fifteenth - fifth fifth fifty fifti - fiftyfold fiftyfold fig fig - fight fight fighter fighter - fightest fightest fighteth fighteth - fighting fight fights fight - figo figo figs fig - figur figur figure figur - figured figur figures figur - figuring figur fike fike - fil fil filberts filbert - filch filch filches filch - filching filch file file - filed file files file - filial filial filius filiu - fill fill filled fill - fillet fillet filling fill - fillip fillip fills fill - filly filli film film - fils fil filth filth - filths filth filthy filthi - fin fin finally final - finch finch find find - finder finder findeth findeth - finding find findings find - finds find fine fine - fineless fineless finely fine - finem finem fineness fine - finer finer fines fine - finest finest fing fing - finger finger fingering finger - fingers finger fingre fingr - fingres fingr finical finic - finish finish finished finish - finisher finish finless finless - finn finn fins fin - finsbury finsburi fir fir - firago firago fire fire - firebrand firebrand firebrands firebrand - fired fire fires fire - firework firework fireworks firework - firing fire firk firk - firm firm firmament firmament - firmly firmli firmness firm - first first firstlings firstl - fish fish fisher fisher - fishermen fishermen fishers fisher - fishes fish fishified fishifi - fishmonger fishmong fishpond fishpond - fisnomy fisnomi fist fist - fisting fist fists fist - fistula fistula fit fit - fitchew fitchew fitful fit - fitly fitli fitment fitment - fitness fit fits fit - fitted fit fitter fitter - fittest fittest fitteth fitteth - fitting fit fitzwater fitzwat - five five fivepence fivep - fives five fix fix - fixed fix fixes fix - fixeth fixeth fixing fix - fixture fixtur fl fl - flag flag flagging flag - flagon flagon flagons flagon - flags flag flail flail - flakes flake flaky flaki - flam flam flame flame - flamen flamen flamens flamen - flames flame flaming flame - flaminius flaminiu flanders flander - flannel flannel flap flap - flaring flare flash flash - flashes flash flashing flash - flask flask flat flat - flatly flatli flatness flat - flats flat flatt flatt - flatter flatter flattered flatter - flatterer flatter flatterers flatter - flatterest flatterest flatteries flatteri - flattering flatter flatters flatter - flattery flatteri flaunts flaunt - flavio flavio flavius flaviu - flaw flaw flaws flaw - flax flax flaxen flaxen - flay flai flaying flai - flea flea fleance fleanc - fleas flea flecked fleck - fled fled fledge fledg - flee flee fleec fleec - fleece fleec fleeces fleec - fleer fleer fleering fleer - fleers fleer fleet fleet - fleeter fleeter fleeting fleet - fleming fleme flemish flemish - flesh flesh fleshes flesh - fleshly fleshli fleshment fleshment - fleshmonger fleshmong flew flew - flexible flexibl flexure flexur - flibbertigibbet flibbertigibbet flickering flicker - flidge flidg fliers flier - flies fli flieth flieth - flight flight flights flight - flighty flighti flinch flinch - fling fling flint flint - flints flint flinty flinti - flirt flirt float float - floated float floating float - flock flock flocks flock - flood flood floodgates floodgat - floods flood floor floor - flora flora florence florenc - florentine florentin florentines florentin - florentius florentiu florizel florizel - flote flote floulish floulish - flour flour flourish flourish - flourishes flourish flourisheth flourisheth - flourishing flourish flout flout - flouted flout flouting flout - flouts flout flow flow - flowed flow flower flower - flowerets floweret flowers flower - flowing flow flown flown - flows flow fluellen fluellen - fluent fluent flung flung - flush flush flushing flush - fluster fluster flute flute - flutes flute flutter flutter - flux flux fluxive fluxiv - fly fly flying fly - fo fo foal foal - foals foal foam foam - foamed foam foaming foam - foams foam foamy foami - fob fob focative foc - fodder fodder foe foe - foeman foeman foemen foemen - foes foe fog fog - foggy foggi fogs fog - foh foh foi foi - foil foil foiled foil - foils foil foin foin - foining foin foins foin - fois foi foison foison - foisons foison foist foist - foix foix fold fold - folded fold folds fold - folio folio folk folk - folks folk follies folli - follow follow followed follow - follower follow followers follow - followest followest following follow - follows follow folly folli - fond fond fonder fonder - fondly fondli fondness fond - font font fontibell fontibel - food food fool fool - fooleries fooleri foolery fooleri - foolhardy foolhardi fooling fool - foolish foolish foolishly foolishli - foolishness foolish fools fool - foot foot football footbal - footboy footboi footboys footboi - footed foot footfall footfal - footing foot footman footman - footmen footmen footpath footpath - footsteps footstep footstool footstool - fopp fopp fopped fop - foppery fopperi foppish foppish - fops fop for for - forage forag foragers forag - forbade forbad forbear forbear - forbearance forbear forbears forbear - forbid forbid forbidden forbidden - forbiddenly forbiddenli forbids forbid - forbod forbod forborne forborn - forc forc force forc - forced forc forceful forc - forceless forceless forces forc - forcible forcibl forcibly forcibl - forcing forc ford ford - fordid fordid fordo fordo - fordoes fordo fordone fordon - fore fore forecast forecast - forefather forefath forefathers forefath - forefinger forefing forego forego - foregone foregon forehand forehand - forehead forehead foreheads forehead - forehorse forehors foreign foreign - foreigner foreign foreigners foreign - foreknowing foreknow foreknowledge foreknowledg - foremost foremost forenamed forenam - forenoon forenoon forerun forerun - forerunner forerunn forerunning forerun - foreruns forerun foresaid foresaid - foresaw foresaw foresay foresai - foresee forese foreseeing forese - foresees forese foreshow foreshow - foreskirt foreskirt forespent foresp - forest forest forestall forestal - forestalled forestal forester forest - foresters forest forests forest - foretell foretel foretelling foretel - foretells foretel forethink forethink - forethought forethought foretold foretold - forever forev foreward foreward - forewarn forewarn forewarned forewarn - forewarning forewarn forfeit forfeit - forfeited forfeit forfeiters forfeit - forfeiting forfeit forfeits forfeit - forfeiture forfeitur forfeitures forfeitur - forfend forfend forfended forfend - forg forg forgave forgav - forge forg forged forg - forgeries forgeri forgery forgeri - forges forg forget forget - forgetful forget forgetfulness forget - forgetive forget forgets forget - forgetting forget forgive forgiv - forgiven forgiven forgiveness forgiv - forgo forgo forgoing forgo - forgone forgon forgot forgot - forgotten forgotten fork fork - forked fork forks fork - forlorn forlorn form form - formal formal formally formal - formed form former former - formerly formerli formless formless - forms form fornication fornic - fornications fornic fornicatress fornicatress - forres forr forrest forrest - forsake forsak forsaken forsaken - forsaketh forsaketh forslow forslow - forsook forsook forsooth forsooth - forspent forspent forspoke forspok - forswear forswear forswearing forswear - forswore forswor forsworn forsworn - fort fort forted fort - forth forth forthcoming forthcom - forthlight forthlight forthright forthright - forthwith forthwith fortification fortif - fortifications fortif fortified fortifi - fortifies fortifi fortify fortifi - fortinbras fortinbra fortitude fortitud - fortnight fortnight fortress fortress - fortresses fortress forts fort - fortun fortun fortuna fortuna - fortunate fortun fortunately fortun - fortune fortun fortuned fortun - fortunes fortun fortward fortward - forty forti forum forum - forward forward forwarding forward - forwardness forward forwards forward - forwearied forweari fosset fosset - fost fost foster foster - fostered foster fought fought - foughten foughten foul foul - fouler fouler foulest foulest - foully foulli foulness foul - found found foundation foundat - foundations foundat founded found - founder founder fount fount - fountain fountain fountains fountain - founts fount four four - fourscore fourscor fourteen fourteen - fourth fourth foutra foutra - fowl fowl fowler fowler - fowling fowl fowls fowl - fox fox foxes fox - foxship foxship fracted fract - fraction fraction fractions fraction - fragile fragil fragment fragment - fragments fragment fragrant fragrant - frail frail frailer frailer - frailties frailti frailty frailti - fram fram frame frame - framed frame frames frame - frampold frampold fran fran - francais francai france franc - frances franc franchise franchis - franchised franchis franchisement franchis - franchises franchis franciae francia - francis franci francisca francisca - franciscan franciscan francisco francisco - frank frank franker franker - frankfort frankfort franklin franklin - franklins franklin frankly frankli - frankness frank frantic frantic - franticly franticli frateretto frateretto - fratrum fratrum fraud fraud - fraudful fraud fraught fraught - fraughtage fraughtag fraughting fraught - fray frai frays frai - freckl freckl freckled freckl - freckles freckl frederick frederick - free free freed freed - freedom freedom freedoms freedom - freehearted freeheart freelier freelier - freely freeli freeman freeman - freemen freemen freeness freeness - freer freer frees free - freestone freeston freetown freetown - freeze freez freezes freez - freezing freez freezings freez - french french frenchman frenchman - frenchmen frenchmen frenchwoman frenchwoman - frenzy frenzi frequent frequent - frequents frequent fresh fresh - fresher fresher freshes fresh - freshest freshest freshly freshli - freshness fresh fret fret - fretful fret frets fret - fretted fret fretten fretten - fretting fret friar friar - friars friar friday fridai - fridays fridai friend friend - friended friend friending friend - friendless friendless friendliness friendli - friendly friendli friends friend - friendship friendship friendships friendship - frieze friez fright fright - frighted fright frightened frighten - frightful fright frighting fright - frights fright fringe fring - fringed fring frippery fripperi - frisk frisk fritters fritter - frivolous frivol fro fro - frock frock frog frog - frogmore frogmor froissart froissart - frolic frolic from from - front front fronted front - frontier frontier frontiers frontier - fronting front frontlet frontlet - fronts front frost frost - frosts frost frosty frosti - froth froth froward froward - frown frown frowning frown - frowningly frowningli frowns frown - froze froze frozen frozen - fructify fructifi frugal frugal - fruit fruit fruiterer fruiter - fruitful fruit fruitfully fruitfulli - fruitfulness fruit fruition fruition - fruitless fruitless fruits fruit - frush frush frustrate frustrat - frutify frutifi fry fry - fubb fubb fuel fuel - fugitive fugit fulfil fulfil - fulfill fulfil fulfilling fulfil - fulfils fulfil full full - fullam fullam fuller fuller - fullers fuller fullest fullest - fullness full fully fulli - fulness ful fulsome fulsom - fulvia fulvia fum fum - fumble fumbl fumbles fumbl - fumblest fumblest fumbling fumbl - fume fume fumes fume - fuming fume fumiter fumit - fumitory fumitori fun fun - function function functions function - fundamental fundament funeral funer - funerals funer fur fur - furbish furbish furies furi - furious furiou furlongs furlong - furnace furnac furnaces furnac - furnish furnish furnished furnish - furnishings furnish furniture furnitur - furnival furniv furor furor - furr furr furrow furrow - furrowed furrow furrows furrow - furth furth further further - furtherance further furtherer further - furthermore furthermor furthest furthest - fury furi furze furz - furzes furz fust fust - fustian fustian fustilarian fustilarian - fusty fusti fut fut - future futur futurity futur - g g gabble gabbl - gaberdine gaberdin gabriel gabriel - gad gad gadding gad - gads gad gadshill gadshil - gag gag gage gage - gaged gage gagg gagg - gaging gage gagne gagn - gain gain gained gain - gainer gainer gaingiving gaingiv - gains gain gainsaid gainsaid - gainsay gainsai gainsaying gainsai - gainsays gainsai gainst gainst - gait gait gaited gait - galathe galath gale gale - galen galen gales gale - gall gall gallant gallant - gallantly gallantli gallantry gallantri - gallants gallant galled gall - gallery galleri galley gallei - galleys gallei gallia gallia - gallian gallian galliard galliard - galliasses galliass gallimaufry gallimaufri - galling gall gallons gallon - gallop gallop galloping gallop - gallops gallop gallow gallow - galloway gallowai gallowglasses gallowglass - gallows gallow gallowses gallows - galls gall gallus gallu - gam gam gambol gambol - gambold gambold gambols gambol - gamboys gamboi game game - gamers gamer games game - gamesome gamesom gamester gamest - gaming game gammon gammon - gamut gamut gan gan - gangren gangren ganymede ganymed - gaol gaol gaoler gaoler - gaolers gaoler gaols gaol - gap gap gape gape - gapes gape gaping gape - gar gar garb garb - garbage garbag garboils garboil - garcon garcon gard gard - garde gard garden garden - gardener garden gardeners garden - gardens garden gardez gardez - gardiner gardin gardon gardon - gargantua gargantua gargrave gargrav - garish garish garland garland - garlands garland garlic garlic - garment garment garments garment - garmet garmet garner garner - garners garner garnish garnish - garnished garnish garret garret - garrison garrison garrisons garrison - gart gart garter garter - garterd garterd gartering garter - garters garter gascony gasconi - gash gash gashes gash - gaskins gaskin gasp gasp - gasping gasp gasted gast - gastness gast gat gat - gate gate gated gate - gates gate gath gath - gather gather gathered gather - gathering gather gathers gather - gatories gatori gatory gatori - gaud gaud gaudeo gaudeo - gaudy gaudi gauge gaug - gaul gaul gaultree gaultre - gaunt gaunt gauntlet gauntlet - gauntlets gauntlet gav gav - gave gave gavest gavest - gawded gawd gawds gawd - gawsey gawsei gay gai - gayness gay gaz gaz - gaze gaze gazed gaze - gazer gazer gazers gazer - gazes gaze gazeth gazeth - gazing gaze gear gear - geck geck geese gees - geffrey geffrei geld geld - gelded geld gelding geld - gelida gelida gelidus gelidu - gelt gelt gem gem - geminy gemini gems gem - gen gen gender gender - genders gender general gener - generally gener generals gener - generation gener generations gener - generative gener generosity generos - generous gener genitive genit - genitivo genitivo genius geniu - gennets gennet genoa genoa - genoux genoux gens gen - gent gent gentilhomme gentilhomm - gentility gentil gentle gentl - gentlefolks gentlefolk gentleman gentleman - gentlemanlike gentlemanlik gentlemen gentlemen - gentleness gentl gentler gentler - gentles gentl gentlest gentlest - gentlewoman gentlewoman gentlewomen gentlewomen - gently gentli gentry gentri - george georg gerard gerard - germaines germain germains germain - german german germane german - germans german germany germani - gertrude gertrud gest gest - gests gest gesture gestur - gestures gestur get get - getrude getrud gets get - getter getter getting get - ghastly ghastli ghost ghost - ghosted ghost ghostly ghostli - ghosts ghost gi gi - giant giant giantess giantess - giantlike giantlik giants giant - gib gib gibber gibber - gibbet gibbet gibbets gibbet - gibe gibe giber giber - gibes gibe gibing gibe - gibingly gibingli giddily giddili - giddiness giddi giddy giddi - gift gift gifts gift - gig gig giglets giglet - giglot giglot gilbert gilbert - gild gild gilded gild - gilding gild gilliams gilliam - gillian gillian gills gill - gillyvors gillyvor gilt gilt - gimmal gimmal gimmers gimmer - gin gin ging ging - ginger ginger gingerbread gingerbread - gingerly gingerli ginn ginn - gins gin gioucestershire gioucestershir - gipes gipe gipsies gipsi - gipsy gipsi gird gird - girded gird girdle girdl - girdled girdl girdles girdl - girdling girdl girl girl - girls girl girt girt - girth girth gis gi - giv giv give give - given given giver giver - givers giver gives give - givest givest giveth giveth - giving give givings give - glad glad gladded glad - gladding glad gladly gladli - gladness glad glamis glami - glanc glanc glance glanc - glanced glanc glances glanc - glancing glanc glanders glander - glansdale glansdal glare glare - glares glare glass glass - glasses glass glassy glassi - glaz glaz glazed glaze - gleams gleam glean glean - gleaned glean gleaning glean - gleeful gleeful gleek gleek - gleeking gleek gleeks gleek - glend glend glendower glendow - glib glib glide glide - glided glide glides glide - glideth glideth gliding glide - glimmer glimmer glimmering glimmer - glimmers glimmer glimpse glimps - glimpses glimps glist glist - glistening glisten glister glister - glistering glister glisters glister - glitt glitt glittering glitter - globe globe globes globe - glooming gloom gloomy gloomi - glories glori glorified glorifi - glorify glorifi glorious gloriou - gloriously glorious glory glori - glose glose gloss gloss - glosses gloss glou glou - glouceste gloucest gloucester gloucest - gloucestershire gloucestershir glove glove - glover glover gloves glove - glow glow glowed glow - glowing glow glowworm glowworm - gloz gloz gloze gloze - glozes gloze glu glu - glue glue glued glu - glues glue glut glut - glutt glutt glutted glut - glutton glutton gluttoning glutton - gluttony gluttoni gnarled gnarl - gnarling gnarl gnat gnat - gnats gnat gnaw gnaw - gnawing gnaw gnawn gnawn - gnaws gnaw go go - goad goad goaded goad - goads goad goal goal - goat goat goatish goatish - goats goat gobbets gobbet - gobbo gobbo goblet goblet - goblets goblet goblin goblin - goblins goblin god god - godded god godden godden - goddess goddess goddesses goddess - goddild goddild godfather godfath - godfathers godfath godhead godhead - godlike godlik godliness godli - godly godli godmother godmoth - gods god godson godson - goer goer goers goer - goes goe goest goest - goeth goeth goffe goff - gogs gog going go - gold gold golden golden - goldenly goldenli goldsmith goldsmith - goldsmiths goldsmith golgotha golgotha - goliases golias goliath goliath - gon gon gondola gondola - gondolier gondoli gone gone - goneril goneril gong gong - gonzago gonzago gonzalo gonzalo - good good goodfellow goodfellow - goodlier goodlier goodliest goodliest - goodly goodli goodman goodman - goodness good goodnight goodnight - goodrig goodrig goods good - goodwife goodwif goodwill goodwil - goodwin goodwin goodwins goodwin - goodyear goodyear goodyears goodyear - goose goos gooseberry gooseberri - goosequills goosequil goot goot - gor gor gorbellied gorbelli - gorboduc gorboduc gordian gordian - gore gore gored gore - gorg gorg gorge gorg - gorgeous gorgeou gorget gorget - gorging gorg gorgon gorgon - gormandize gormand gormandizing gormand - gory gori gosling gosl - gospel gospel gospels gospel - goss goss gossamer gossam - gossip gossip gossiping gossip - gossiplike gossiplik gossips gossip - got got goth goth - goths goth gotten gotten - gourd gourd gout gout - gouts gout gouty gouti - govern govern governance govern - governed govern governess gover - government govern governor governor - governors governor governs govern - gower gower gown gown - gowns gown grac grac - grace grace graced grace - graceful grace gracefully gracefulli - graceless graceless graces grace - gracing grace gracious graciou - graciously gracious gradation gradat - graff graff graffing graf - graft graft grafted graft - grafters grafter grain grain - grained grain grains grain - gramercies gramerci gramercy gramerci - grammar grammar grand grand - grandam grandam grandame grandam - grandchild grandchild grande grand - grandeur grandeur grandfather grandfath - grandjurors grandjuror grandmother grandmoth - grandpre grandpr grandsir grandsir - grandsire grandsir grandsires grandsir - grange grang grant grant - granted grant granting grant - grants grant grape grape - grapes grape grapple grappl - grapples grappl grappling grappl - grasp grasp grasped grasp - grasps grasp grass grass - grasshoppers grasshopp grassy grassi - grate grate grated grate - grateful grate grates grate - gratiano gratiano gratify gratifi - gratii gratii gratillity gratil - grating grate gratis grati - gratitude gratitud gratulate gratul - grav grav grave grave - gravediggers gravedigg gravel gravel - graveless graveless gravell gravel - gravely grave graven graven - graveness grave graver graver - graves grave gravest gravest - gravestone graveston gravities graviti - gravity graviti gravy gravi - gray grai graymalkin graymalkin - graz graz graze graze - grazed graze grazing graze - grease greas greases greas - greasily greasili greasy greasi - great great greater greater - greatest greatest greatly greatli - greatness great grecian grecian - grecians grecian gree gree - greece greec greed greed - greedily greedili greediness greedi - greedy greedi greeing gree - greek greek greekish greekish - greeks greek green green - greener greener greenly greenli - greens green greensleeves greensleev - greenwich greenwich greenwood greenwood - greet greet greeted greet - greeting greet greetings greet - greets greet greg greg - gregory gregori gremio gremio - grew grew grey grei - greybeard greybeard greybeards greybeard - greyhound greyhound greyhounds greyhound - grief grief griefs grief - griev griev grievance grievanc - grievances grievanc grieve griev - grieved griev grieves griev - grievest grievest grieving griev - grievingly grievingli grievous grievou - grievously grievous griffin griffin - griffith griffith grim grim - grime grime grimly grimli - grin grin grind grind - grinding grind grindstone grindston - grinning grin grip grip - gripe gripe gripes gripe - griping gripe grise grise - grisly grisli grissel grissel - grize grize grizzle grizzl - grizzled grizzl groan groan - groaning groan groans groan - groat groat groats groat - groin groin groom groom - grooms groom grop grop - groping grope gros gro - gross gross grosser grosser - grossly grossli grossness gross - ground ground grounded ground - groundlings groundl grounds ground - grove grove grovel grovel - grovelling grovel groves grove - grow grow groweth groweth - growing grow grown grown - grows grow growth growth - grub grub grubb grubb - grubs grub grudge grudg - grudged grudg grudges grudg - grudging grudg gruel gruel - grumble grumbl grumblest grumblest - grumbling grumbl grumblings grumbl - grumio grumio grund grund - grunt grunt gualtier gualtier - guard guard guardage guardag - guardant guardant guarded guard - guardian guardian guardians guardian - guards guard guardsman guardsman - gud gud gudgeon gudgeon - guerdon guerdon guerra guerra - guess guess guesses guess - guessingly guessingli guest guest - guests guest guiana guiana - guichard guichard guide guid - guided guid guider guider - guiderius guideriu guides guid - guiding guid guidon guidon - guienne guienn guil guil - guildenstern guildenstern guilders guilder - guildford guildford guildhall guildhal - guile guil guiled guil - guileful guil guilfords guilford - guilt guilt guiltian guiltian - guiltier guiltier guiltily guiltili - guiltiness guilti guiltless guiltless - guilts guilt guilty guilti - guinea guinea guinever guinev - guise guis gul gul - gules gule gulf gulf - gulfs gulf gull gull - gulls gull gum gum - gumm gumm gums gum - gun gun gunner gunner - gunpowder gunpowd guns gun - gurnet gurnet gurney gurnei - gust gust gusts gust - gusty gusti guts gut - gutter gutter guy gui - guynes guyn guysors guysor - gypsy gypsi gyve gyve - gyved gyve gyves gyve - h h ha ha - haberdasher haberdash habiliment habili - habiliments habili habit habit - habitation habit habited habit - habits habit habitude habitud - hack hack hacket hacket - hackney hacknei hacks hack - had had hadst hadst - haec haec haeres haer - hag hag hagar hagar - haggard haggard haggards haggard - haggish haggish haggled haggl - hags hag hail hail - hailed hail hailstone hailston - hailstones hailston hair hair - hairless hairless hairs hair - hairy hairi hal hal - halberd halberd halberds halberd - halcyon halcyon hale hale - haled hale hales hale - half half halfcan halfcan - halfpence halfpenc halfpenny halfpenni - halfpennyworth halfpennyworth halfway halfwai - halidom halidom hall hall - halloa halloa halloing hallo - hallond hallond halloo halloo - hallooing halloo hallow hallow - hallowed hallow hallowmas hallowma - hallown hallown hals hal - halt halt halter halter - halters halter halting halt - halts halt halves halv - ham ham hames hame - hamlet hamlet hammer hammer - hammered hammer hammering hammer - hammers hammer hamper hamper - hampton hampton hams ham - hamstring hamstr hand hand - handed hand handful hand - handicraft handicraft handicraftsmen handicraftsmen - handing hand handiwork handiwork - handkercher handkerch handkerchers handkerch - handkerchief handkerchief handle handl - handled handl handles handl - handless handless handlest handlest - handling handl handmaid handmaid - handmaids handmaid hands hand - handsaw handsaw handsome handsom - handsomely handsom handsomeness handsom - handwriting handwrit handy handi - hang hang hanged hang - hangers hanger hangeth hangeth - hanging hang hangings hang - hangman hangman hangmen hangmen - hangs hang hannibal hannib - hap hap hapless hapless - haply hapli happ happ - happen happen happened happen - happier happier happies happi - happiest happiest happily happili - happiness happi happy happi - haps hap harbinger harbing - harbingers harbing harbor harbor - harbour harbour harbourage harbourag - harbouring harbour harbours harbour - harcourt harcourt hard hard - harder harder hardest hardest - hardiest hardiest hardiment hardiment - hardiness hardi hardly hardli - hardness hard hardocks hardock - hardy hardi hare hare - harelip harelip hares hare - harfleur harfleur hark hark - harlot harlot harlotry harlotri - harlots harlot harm harm - harmed harm harmful harm - harming harm harmless harmless - harmonious harmoni harmony harmoni - harms harm harness har - harp harp harper harper - harpier harpier harping harp - harpy harpi harried harri - harrow harrow harrows harrow - harry harri harsh harsh - harshly harshli harshness harsh - hart hart harts hart - harum harum harvest harvest - has ha hast hast - haste hast hasted hast - hasten hasten hastes hast - hastily hastili hasting hast - hastings hast hasty hasti - hat hat hatch hatch - hatches hatch hatchet hatchet - hatching hatch hatchment hatchment - hate hate hated hate - hateful hate hater hater - haters hater hates hate - hateth hateth hatfield hatfield - hath hath hating hate - hatred hatr hats hat - haud haud hauf hauf - haught haught haughtiness haughti - haughty haughti haunch haunch - haunches haunch haunt haunt - haunted haunt haunting haunt - haunts haunt hautboy hautboi - hautboys hautboi have have - haven haven havens haven - haver haver having have - havings have havior havior - haviour haviour havoc havoc - hawk hawk hawking hawk - hawks hawk hawthorn hawthorn - hawthorns hawthorn hay hai - hazard hazard hazarded hazard - hazards hazard hazel hazel - hazelnut hazelnut he he - head head headborough headborough - headed head headier headier - heading head headland headland - headless headless headlong headlong - heads head headsman headsman - headstrong headstrong heady headi - heal heal healed heal - healing heal heals heal - health health healthful health - healths health healthsome healthsom - healthy healthi heap heap - heaping heap heaps heap - hear hear heard heard - hearer hearer hearers hearer - hearest hearest heareth heareth - hearing hear hearings hear - heark heark hearken hearken - hearkens hearken hears hear - hearsay hearsai hearse hears - hearsed hears hearst hearst - heart heart heartache heartach - heartbreak heartbreak heartbreaking heartbreak - hearted heart hearten hearten - hearth hearth hearths hearth - heartily heartili heartiness hearti - heartless heartless heartlings heartl - heartly heartli hearts heart - heartsick heartsick heartstrings heartstr - hearty hearti heat heat - heated heat heath heath - heathen heathen heathenish heathenish - heating heat heats heat - heauties heauti heav heav - heave heav heaved heav - heaven heaven heavenly heavenli - heavens heaven heaves heav - heavier heavier heaviest heaviest - heavily heavili heaviness heavi - heaving heav heavings heav - heavy heavi hebona hebona - hebrew hebrew hecate hecat - hectic hectic hector hector - hectors hector hecuba hecuba - hedg hedg hedge hedg - hedgehog hedgehog hedgehogs hedgehog - hedges hedg heed heed - heeded heed heedful heed - heedfull heedful heedfully heedfulli - heedless heedless heel heel - heels heel hefted heft - hefts heft heifer heifer - heifers heifer heigh heigh - height height heighten heighten - heinous heinou heinously heinous - heir heir heiress heiress - heirless heirless heirs heir - held held helen helen - helena helena helenus helenu - helias helia helicons helicon - hell hell hellespont hellespont - hellfire hellfir hellish hellish - helm helm helmed helm - helmet helmet helmets helmet - helms helm help help - helper helper helpers helper - helpful help helping help - helpless helpless helps help - helter helter hem hem - heme heme hemlock hemlock - hemm hemm hemp hemp - hempen hempen hems hem - hen hen hence henc - henceforth henceforth henceforward henceforward - henchman henchman henri henri - henricus henricu henry henri - hens hen hent hent - henton henton her her - herald herald heraldry heraldri - heralds herald herb herb - herbert herbert herblets herblet - herbs herb herculean herculean - hercules hercul herd herd - herds herd herdsman herdsman - herdsmen herdsmen here here - hereabout hereabout hereabouts hereabout - hereafter hereaft hereby herebi - hereditary hereditari hereford hereford - herefordshire herefordshir herein herein - hereof hereof heresies heresi - heresy heresi heretic heret - heretics heret hereto hereto - hereupon hereupon heritage heritag - heritier heriti hermes herm - hermia hermia hermione hermion - hermit hermit hermitage hermitag - hermits hermit herne hern - hero hero herod herod - herods herod heroes hero - heroic heroic heroical heroic - herring her herrings her - hers her herself herself - hesperides hesperid hesperus hesperu - hest hest hests hest - heure heur heureux heureux - hew hew hewgh hewgh - hewing hew hewn hewn - hews hew hey hei - heyday heydai hibocrates hibocr - hic hic hiccups hiccup - hick hick hid hid - hidden hidden hide hide - hideous hideou hideously hideous - hideousness hideous hides hide - hidest hidest hiding hide - hie hie hied hi - hiems hiem hies hi - hig hig high high - higher higher highest highest - highly highli highmost highmost - highness high hight hight - highway highwai highways highwai - hilding hild hildings hild - hill hill hillo hillo - hilloa hilloa hills hill - hilt hilt hilts hilt - hily hili him him - himself himself hinc hinc - hinckley hincklei hind hind - hinder hinder hindered hinder - hinders hinder hindmost hindmost - hinds hind hing hing - hinge hing hinges hing - hint hint hip hip - hipp hipp hipparchus hipparchu - hippolyta hippolyta hips hip - hir hir hire hire - hired hire hiren hiren - hirtius hirtiu his hi - hisperia hisperia hiss hiss - hisses hiss hissing hiss - hist hist historical histor - history histori hit hit - hither hither hitherto hitherto - hitherward hitherward hitherwards hitherward - hits hit hitting hit - hive hive hives hive - hizzing hizz ho ho - hoa hoa hoar hoar - hoard hoard hoarded hoard - hoarding hoard hoars hoar - hoarse hoars hoary hoari - hob hob hobbididence hobbidid - hobby hobbi hobbyhorse hobbyhors - hobgoblin hobgoblin hobnails hobnail - hoc hoc hod hod - hodge hodg hog hog - hogs hog hogshead hogshead - hogsheads hogshead hois hoi - hoise hois hoist hoist - hoisted hoist hoists hoist - holborn holborn hold hold - holden holden holder holder - holdeth holdeth holdfast holdfast - holding hold holds hold - hole hole holes hole - holidam holidam holidame holidam - holiday holidai holidays holidai - holier holier holiest holiest - holily holili holiness holi - holla holla holland holland - hollander holland hollanders holland - holloa holloa holloaing holloa - hollow hollow hollowly hollowli - hollowness hollow holly holli - holmedon holmedon holofernes holofern - holp holp holy holi - homage homag homager homag - home home homely home - homes home homespuns homespun - homeward homeward homewards homeward - homicide homicid homicides homicid - homily homili hominem hominem - hommes homm homo homo - honest honest honester honest - honestest honestest honestly honestli - honesty honesti honey honei - honeycomb honeycomb honeying honei - honeyless honeyless honeysuckle honeysuckl - honeysuckles honeysuckl honi honi - honneur honneur honor honor - honorable honor honorably honor - honorato honorato honorificabilitudinitatibus honorificabilitudinitatibu - honors honor honour honour - honourable honour honourably honour - honoured honour honourest honourest - honourible honour honouring honour - honours honour hoo hoo - hood hood hooded hood - hoodman hoodman hoods hood - hoodwink hoodwink hoof hoof - hoofs hoof hook hook - hooking hook hooks hook - hoop hoop hoops hoop - hoot hoot hooted hoot - hooting hoot hoots hoot - hop hop hope hope - hopeful hope hopeless hopeless - hopes hope hopest hopest - hoping hope hopkins hopkin - hoppedance hopped hor hor - horace horac horatio horatio - horizon horizon horn horn - hornbook hornbook horned horn - horner horner horning horn - hornpipes hornpip horns horn - horologe horolog horrible horribl - horribly horribl horrid horrid - horrider horrid horridly horridli - horror horror horrors horror - hors hor horse hors - horseback horseback horsed hors - horsehairs horsehair horseman horseman - horsemanship horsemanship horsemen horsemen - horses hors horseway horsewai - horsing hors hortensio hortensio - hortensius hortensiu horum horum - hose hose hospitable hospit - hospital hospit hospitality hospit - host host hostage hostag - hostages hostag hostess hostess - hostile hostil hostility hostil - hostilius hostiliu hosts host - hot hot hotly hotli - hotspur hotspur hotter hotter - hottest hottest hound hound - hounds hound hour hour - hourly hourli hours hour - hous hou house hous - household household householder household - householders household households household - housekeeper housekeep housekeepers housekeep - housekeeping housekeep houseless houseless - houses hous housewife housewif - housewifery housewiferi housewives housew - hovel hovel hover hover - hovered hover hovering hover - hovers hover how how - howbeit howbeit howe how - howeer howeer however howev - howl howl howled howl - howlet howlet howling howl - howls howl howsoe howso - howsoever howsoev howsome howsom - hoxes hox hoy hoi - hoyday hoydai hubert hubert - huddled huddl huddling huddl - hue hue hued hu - hues hue hug hug - huge huge hugely huge - hugeness huge hugg hugg - hugger hugger hugh hugh - hugs hug hujus huju - hulk hulk hulks hulk - hull hull hulling hull - hullo hullo hum hum - human human humane human - humanely human humanity human - humble humbl humbled humbl - humbleness humbl humbler humbler - humbles humbl humblest humblest - humbling humbl humbly humbl - hume hume humh humh - humidity humid humility humil - humming hum humor humor - humorous humor humors humor - humour humour humourists humourist - humours humour humphrey humphrei - humphry humphri hums hum - hundred hundr hundreds hundr - hundredth hundredth hung hung - hungarian hungarian hungary hungari - hunger hunger hungerford hungerford - hungerly hungerli hungry hungri - hunt hunt hunted hunt - hunter hunter hunters hunter - hunteth hunteth hunting hunt - huntington huntington huntress huntress - hunts hunt huntsman huntsman - huntsmen huntsmen hurdle hurdl - hurl hurl hurling hurl - hurls hurl hurly hurli - hurlyburly hurlyburli hurricano hurricano - hurricanoes hurricano hurried hurri - hurries hurri hurry hurri - hurt hurt hurting hurt - hurtled hurtl hurtless hurtless - hurtling hurtl hurts hurt - husband husband husbanded husband - husbandless husbandless husbandry husbandri - husbands husband hush hush - hushes hush husht husht - husks husk huswife huswif - huswifes huswif hutch hutch - hybla hybla hydra hydra - hyen hyen hymen hymen - hymenaeus hymenaeu hymn hymn - hymns hymn hyperboles hyperbol - hyperbolical hyperbol hyperion hyperion - hypocrisy hypocrisi hypocrite hypocrit - hypocrites hypocrit hyrcan hyrcan - hyrcania hyrcania hyrcanian hyrcanian - hyssop hyssop hysterica hysterica - i i iachimo iachimo - iaculis iaculi iago iago - iament iament ibat ibat - icarus icaru ice ic - iceland iceland ici ici - icicle icicl icicles icicl - icy ici idea idea - ideas idea idem idem - iden iden ides id - idiot idiot idiots idiot - idle idl idleness idl - idles idl idly idli - idol idol idolatrous idolatr - idolatry idolatri ield ield - if if ifs if - ignis igni ignoble ignobl - ignobly ignobl ignominious ignomini - ignominy ignomini ignomy ignomi - ignorance ignor ignorant ignor - ii ii iii iii - iiii iiii il il - ilbow ilbow ild ild - ilion ilion ilium ilium - ill ill illegitimate illegitim - illiterate illiter illness ill - illo illo ills ill - illume illum illumin illumin - illuminate illumin illumineth illumineth - illusion illus illusions illus - illustrate illustr illustrated illustr - illustrious illustri illyria illyria - illyrian illyrian ils il - im im image imag - imagery imageri images imag - imagin imagin imaginary imaginari - imagination imagin imaginations imagin - imagine imagin imagining imagin - imaginings imagin imbar imbar - imbecility imbecil imbrue imbru - imitari imitari imitate imit - imitated imit imitation imit - imitations imit immaculate immacul - immanity imman immask immask - immaterial immateri immediacy immediaci - immediate immedi immediately immedi - imminence immin imminent immin - immoderate immoder immoderately immoder - immodest immodest immoment immoment - immortal immort immortaliz immortaliz - immortally immort immur immur - immured immur immures immur - imogen imogen imp imp - impaint impaint impair impair - impairing impair impale impal - impaled impal impanelled impanel - impart impart imparted impart - impartial imparti impartment impart - imparts impart impasted impast - impatience impati impatient impati - impatiently impati impawn impawn - impeach impeach impeached impeach - impeachment impeach impeachments impeach - impedes imped impediment impedi - impediments impedi impenetrable impenetr - imperator imper imperceiverant imperceiver - imperfect imperfect imperfection imperfect - imperfections imperfect imperfectly imperfectli - imperial imperi imperious imperi - imperiously imperi impertinency impertin - impertinent impertin impeticos impetico - impetuosity impetuos impetuous impetu - impieties impieti impiety impieti - impious impiou implacable implac - implements implement implies impli - implor implor implorators implor - implore implor implored implor - imploring implor impon impon - import import importance import - importancy import important import - importantly importantli imported import - importeth importeth importing import - importless importless imports import - importun importun importunacy importunaci - importunate importun importune importun - importunes importun importunity importun - impos impo impose impos - imposed impos imposition imposit - impositions imposit impossibilities imposs - impossibility imposs impossible imposs - imposthume imposthum impostor impostor - impostors impostor impotence impot - impotent impot impounded impound - impregnable impregn imprese impres - impress impress impressed impress - impressest impressest impression impress - impressure impressur imprimendum imprimendum - imprimis imprimi imprint imprint - imprinted imprint imprison imprison - imprisoned imprison imprisoning imprison - imprisonment imprison improbable improb - improper improp improve improv - improvident improvid impudence impud - impudency impud impudent impud - impudently impud impudique impudiqu - impugn impugn impugns impugn - impure impur imputation imput - impute imput in in - inaccessible inaccess inaidable inaid - inaudible inaud inauspicious inauspici - incaged incag incantations incant - incapable incap incardinate incardin - incarnadine incarnadin incarnate incarn - incarnation incarn incens incen - incense incens incensed incens - incensement incens incenses incens - incensing incens incertain incertain - incertainties incertainti incertainty incertainti - incessant incess incessantly incessantli - incest incest incestuous incestu - inch inch incharitable incharit - inches inch incidency incid - incident incid incision incis - incite incit incites incit - incivil incivil incivility incivil - inclin inclin inclinable inclin - inclination inclin incline inclin - inclined inclin inclines inclin - inclining inclin inclips inclip - include includ included includ - includes includ inclusive inclus - incomparable incompar incomprehensible incomprehens - inconsiderate inconsider inconstancy inconst - inconstant inconst incontinency incontin - incontinent incontin incontinently incontin - inconvenience inconveni inconveniences inconveni - inconvenient inconveni incony inconi - incorporate incorpor incorps incorp - incorrect incorrect increas increa - increase increas increases increas - increaseth increaseth increasing increas - incredible incred incredulous incredul - incur incur incurable incur - incurr incurr incurred incur - incursions incurs ind ind - inde ind indebted indebt - indeed inde indent indent - indented indent indenture indentur - indentures indentur index index - indexes index india india - indian indian indict indict - indicted indict indictment indict - indies indi indifferency indiffer - indifferent indiffer indifferently indiffer - indigent indig indigest indigest - indigested indigest indign indign - indignation indign indignations indign - indigne indign indignities indign - indignity indign indirect indirect - indirection indirect indirections indirect - indirectly indirectli indiscreet indiscreet - indiscretion indiscret indispos indispo - indisposition indisposit indissoluble indissolubl - indistinct indistinct indistinguish indistinguish - indistinguishable indistinguish indited indit - individable individ indrench indrench - indu indu indubitate indubit - induc induc induce induc - induced induc inducement induc - induction induct inductions induct - indue indu indued indu - indues indu indulgence indulg - indulgences indulg indulgent indulg - indurance indur industrious industri - industriously industri industry industri - inequality inequ inestimable inestim - inevitable inevit inexecrable inexecr - inexorable inexor inexplicable inexplic - infallible infal infallibly infal - infamonize infamon infamous infam - infamy infami infancy infanc - infant infant infants infant - infect infect infected infect - infecting infect infection infect - infections infect infectious infecti - infectiously infecti infects infect - infer infer inference infer - inferior inferior inferiors inferior - infernal infern inferr inferr - inferreth inferreth inferring infer - infest infest infidel infidel - infidels infidel infinite infinit - infinitely infinit infinitive infinit - infirm infirm infirmities infirm - infirmity infirm infixed infix - infixing infix inflam inflam - inflame inflam inflaming inflam - inflammation inflamm inflict inflict - infliction inflict influence influenc - influences influenc infold infold - inform inform informal inform - information inform informations inform - informed inform informer inform - informs inform infortunate infortun - infring infr infringe infring - infringed infring infus infu - infuse infus infused infus - infusing infus infusion infus - ingener ingen ingenious ingeni - ingeniously ingeni inglorious inglori - ingots ingot ingraffed ingraf - ingraft ingraft ingrate ingrat - ingrated ingrat ingrateful ingrat - ingratitude ingratitud ingratitudes ingratitud - ingredient ingredi ingredients ingredi - ingross ingross inhabit inhabit - inhabitable inhabit inhabitants inhabit - inhabited inhabit inhabits inhabit - inhearse inhears inhearsed inhears - inherent inher inherit inherit - inheritance inherit inherited inherit - inheriting inherit inheritor inheritor - inheritors inheritor inheritrix inheritrix - inherits inherit inhibited inhibit - inhibition inhibit inhoop inhoop - inhuman inhuman iniquities iniqu - iniquity iniqu initiate initi - injointed injoint injunction injunct - injunctions injunct injur injur - injure injur injurer injur - injuries injuri injurious injuri - injury injuri injustice injustic - ink ink inkhorn inkhorn - inkle inkl inkles inkl - inkling inkl inky inki - inlaid inlaid inland inland - inlay inlai inly inli - inmost inmost inn inn - inner inner innkeeper innkeep - innocence innoc innocency innoc - innocent innoc innocents innoc - innovation innov innovator innov - inns inn innumerable innumer - inoculate inocul inordinate inordin - inprimis inprimi inquir inquir - inquire inquir inquiry inquiri - inquisition inquisit inquisitive inquisit - inroads inroad insane insan - insanie insani insatiate insati - insconce insconc inscrib inscrib - inscription inscript inscriptions inscript - inscroll inscrol inscrutable inscrut - insculp insculp insculpture insculptur - insensible insens inseparable insepar - inseparate insepar insert insert - inserted insert inset inset - inshell inshel inshipp inshipp - inside insid insinewed insinew - insinuate insinu insinuateth insinuateth - insinuating insinu insinuation insinu - insisted insist insisting insist - insisture insistur insociable insoci - insolence insol insolent insol - insomuch insomuch inspir inspir - inspiration inspir inspirations inspir - inspire inspir inspired inspir - install instal installed instal - instalment instal instance instanc - instances instanc instant instant - instantly instantli instate instat - instead instead insteeped insteep - instigate instig instigated instig - instigation instig instigations instig - instigator instig instinct instinct - instinctively instinct institute institut - institutions institut instruct instruct - instructed instruct instruction instruct - instructions instruct instructs instruct - instrument instrument instrumental instrument - instruments instrument insubstantial insubstanti - insufficience insuffici insufficiency insuffici - insult insult insulted insult - insulting insult insultment insult - insults insult insupportable insupport - insuppressive insuppress insurrection insurrect - insurrections insurrect int int - integer integ integritas integrita - integrity integr intellect intellect - intellects intellect intellectual intellectu - intelligence intellig intelligencer intelligenc - intelligencing intelligenc intelligent intellig - intelligis intelligi intelligo intelligo - intemperance intemper intemperate intemper - intend intend intended intend - intendeth intendeth intending intend - intendment intend intends intend - intenible inten intent intent - intention intent intentively intent - intents intent inter inter - intercept intercept intercepted intercept - intercepter intercept interception intercept - intercepts intercept intercession intercess - intercessors intercessor interchained interchain - interchang interchang interchange interchang - interchangeably interchang interchangement interchang - interchanging interchang interdiction interdict - interest interest interim interim - interims interim interior interior - interjections interject interjoin interjoin - interlude interlud intermingle intermingl - intermission intermiss intermissive intermiss - intermit intermit intermix intermix - intermixed intermix interpose interpos - interposer interpos interposes interpos - interpret interpret interpretation interpret - interpreted interpret interpreter interpret - interpreters interpret interprets interpret - interr interr interred inter - interrogatories interrogatori interrupt interrupt - interrupted interrupt interrupter interrupt - interruptest interruptest interruption interrupt - interrupts interrupt intertissued intertissu - intervallums intervallum interview interview - intestate intest intestine intestin - intil intil intimate intim - intimation intim intitled intitl - intituled intitul into into - intolerable intoler intoxicates intox - intreasured intreasur intreat intreat - intrench intrench intrenchant intrench - intricate intric intrinse intrins - intrinsicate intrins intrude intrud - intruder intrud intruding intrud - intrusion intrus inundation inund - inure inur inurn inurn - invade invad invades invad - invasion invas invasive invas - invectively invect invectives invect - inveigled inveigl invent invent - invented invent invention invent - inventions invent inventor inventor - inventorially inventori inventoried inventori - inventors inventor inventory inventori - inverness inver invert invert - invest invest invested invest - investing invest investments invest - inveterate inveter invincible invinc - inviolable inviol invised invis - invisible invis invitation invit - invite invit invited invit - invites invit inviting invit - invitis inviti invocate invoc - invocation invoc invoke invok - invoked invok invulnerable invulner - inward inward inwardly inwardli - inwardness inward inwards inward - ionia ionia ionian ionian - ipse ips ipswich ipswich - ira ira irae ira - iras ira ire ir - ireful ir ireland ireland - iris iri irish irish - irishman irishman irishmen irishmen - irks irk irksome irksom - iron iron irons iron - irreconcil irreconcil irrecoverable irrecover - irregular irregular irregulous irregul - irreligious irreligi irremovable irremov - irreparable irrepar irresolute irresolut - irrevocable irrevoc is is - isabel isabel isabella isabella - isbel isbel isbels isbel - iscariot iscariot ise is - ish ish isidore isidor - isis isi island island - islander island islanders island - islands island isle isl - isles isl israel israel - issu issu issue issu - issued issu issueless issueless - issues issu issuing issu - ist ist ista ista - it it italian italian - italy itali itch itch - itches itch itching itch - item item items item - iteration iter ithaca ithaca - its it itself itself - itshall itshal iv iv - ivory ivori ivy ivi - iwis iwi ix ix - j j jacet jacet - jack jack jackanapes jackanap - jacks jack jacksauce jacksauc - jackslave jackslav jacob jacob - jade jade jaded jade - jades jade jail jail - jakes jake jamany jamani - james jame jamy jami - jane jane jangled jangl - jangling jangl january januari - janus janu japhet japhet - jaquenetta jaquenetta jaques jaqu - jar jar jarring jar - jars jar jarteer jarteer - jasons jason jaunce jaunc - jauncing jaunc jaundice jaundic - jaundies jaundi jaw jaw - jawbone jawbon jaws jaw - jay jai jays jai - jc jc je je - jealous jealou jealousies jealousi - jealousy jealousi jeer jeer - jeering jeer jelly jelli - jenny jenni jeopardy jeopardi - jephtha jephtha jephthah jephthah - jerkin jerkin jerkins jerkin - jerks jerk jeronimy jeronimi - jerusalem jerusalem jeshu jeshu - jesses jess jessica jessica - jest jest jested jest - jester jester jesters jester - jesting jest jests jest - jesu jesu jesus jesu - jet jet jets jet - jew jew jewel jewel - jeweller jewel jewels jewel - jewess jewess jewish jewish - jewry jewri jews jew - jezebel jezebel jig jig - jigging jig jill jill - jills jill jingling jingl - joan joan job job - jockey jockei jocund jocund - jog jog jogging jog - john john johns john - join join joinder joinder - joined join joiner joiner - joineth joineth joins join - joint joint jointed joint - jointing joint jointly jointli - jointress jointress joints joint - jointure jointur jollity jolliti - jolly jolli jolt jolt - joltheads jolthead jordan jordan - joseph joseph joshua joshua - jot jot jour jour - jourdain jourdain journal journal - journey journei journeying journei - journeyman journeyman journeymen journeymen - journeys journei jove jove - jovem jovem jovial jovial - jowl jowl jowls jowl - joy joi joyed joi - joyful joy joyfully joyfulli - joyless joyless joyous joyou - joys joi juan juan - jud jud judas juda - judases judas jude jude - judg judg judge judg - judged judg judgement judgement - judges judg judgest judgest - judging judg judgment judgment - judgments judgment judicious judici - jug jug juggle juggl - juggled juggl juggler juggler - jugglers juggler juggling juggl - jugs jug juice juic - juiced juic jul jul - jule jule julia julia - juliet juliet julietta julietta - julio julio julius juliu - july juli jump jump - jumpeth jumpeth jumping jump - jumps jump june june - junes june junior junior - junius juniu junkets junket - juno juno jupiter jupit - jure jure jurement jurement - jurisdiction jurisdict juror juror - jurors juror jury juri - jurymen jurymen just just - justeius justeiu justest justest - justice justic justicer justic - justicers justic justices justic - justification justif justified justifi - justify justifi justle justl - justled justl justles justl - justling justl justly justli - justness just justs just - jutting jut jutty jutti - juvenal juven kam kam - kate kate kated kate - kates kate katharine katharin - katherina katherina katherine katherin - kecksies kecksi keech keech - keel keel keels keel - keen keen keenness keen - keep keep keepdown keepdown - keeper keeper keepers keeper - keepest keepest keeping keep - keeps keep keiser keiser - ken ken kendal kendal - kennel kennel kent kent - kentish kentish kentishman kentishman - kentishmen kentishmen kept kept - kerchief kerchief kerely kere - kern kern kernal kernal - kernel kernel kernels kernel - kerns kern kersey kersei - kettle kettl kettledrum kettledrum - kettledrums kettledrum key kei - keys kei kibe kibe - kibes kibe kick kick - kicked kick kickshaws kickshaw - kickshawses kickshaws kicky kicki - kid kid kidney kidnei - kikely kike kildare kildar - kill kill killed kill - killer killer killeth killeth - killing kill killingworth killingworth - kills kill kiln kiln - kimbolton kimbolton kin kin - kind kind kinder kinder - kindest kindest kindle kindl - kindled kindl kindless kindless - kindlier kindlier kindling kindl - kindly kindli kindness kind - kindnesses kind kindred kindr - kindreds kindr kinds kind - kine kine king king - kingdom kingdom kingdoms kingdom - kingly kingli kings king - kinred kinr kins kin - kinsman kinsman kinsmen kinsmen - kinswoman kinswoman kirtle kirtl - kirtles kirtl kiss kiss - kissed kiss kisses kiss - kissing kiss kitchen kitchen - kitchens kitchen kite kite - kites kite kitten kitten - kj kj kl kl - klll klll knack knack - knacks knack knapp knapp - knav knav knave knave - knaveries knaveri knavery knaveri - knaves knave knavish knavish - knead knead kneaded knead - kneading knead knee knee - kneel kneel kneeling kneel - kneels kneel knees knee - knell knell knew knew - knewest knewest knife knife - knight knight knighted knight - knighthood knighthood knighthoods knighthood - knightly knightli knights knight - knit knit knits knit - knitters knitter knitteth knitteth - knives knive knobs knob - knock knock knocking knock - knocks knock knog knog - knoll knoll knot knot - knots knot knotted knot - knotty knotti know know - knower knower knowest knowest - knowing know knowingly knowingli - knowings know knowledge knowledg - known known knows know - l l la la - laban laban label label - labell label labienus labienu - labio labio labor labor - laboring labor labors labor - labour labour laboured labour - labourer labour labourers labour - labouring labour labours labour - laboursome laboursom labras labra - labyrinth labyrinth lac lac - lace lace laced lace - lacedaemon lacedaemon laces lace - lacies laci lack lack - lackbeard lackbeard lacked lack - lackey lackei lackeying lackei - lackeys lackei lacking lack - lacks lack lad lad - ladder ladder ladders ladder - lade lade laden laden - ladies ladi lading lade - lads lad lady ladi - ladybird ladybird ladyship ladyship - ladyships ladyship laer laer - laertes laert lafeu lafeu - lag lag lagging lag - laid laid lain lain - laissez laissez lake lake - lakes lake lakin lakin - lam lam lamb lamb - lambert lambert lambkin lambkin - lambkins lambkin lambs lamb - lame lame lamely lame - lameness lame lament lament - lamentable lament lamentably lament - lamentation lament lamentations lament - lamented lament lamenting lament - lamentings lament laments lament - lames lame laming lame - lammas lamma lammastide lammastid - lamound lamound lamp lamp - lampass lampass lamps lamp - lanc lanc lancaster lancast - lance lanc lances lanc - lanceth lanceth lanch lanch - land land landed land - landing land landless landless - landlord landlord landmen landmen - lands land lane lane - lanes lane langage langag - langley langlei langton langton - language languag languageless languageless - languages languag langues langu - languish languish languished languish - languishes languish languishing languish - languishings languish languishment languish - languor languor lank lank - lantern lantern lanterns lantern - lanthorn lanthorn lap lap - lapis lapi lapland lapland - lapp lapp laps lap - lapse laps lapsed laps - lapsing laps lapwing lapw - laquais laquai larded lard - larder larder larding lard - lards lard large larg - largely larg largeness larg - larger larger largess largess - largest largest lark lark - larks lark larron larron - lartius lartiu larum larum - larums larum las la - lascivious lascivi lash lash - lass lass lasses lass - last last lasted last - lasting last lastly lastli - lasts last latch latch - latches latch late late - lated late lately late - later later latest latest - lath lath latin latin - latten latten latter latter - lattice lattic laud laud - laudable laudabl laudis laudi - laugh laugh laughable laughabl - laughed laugh laugher laugher - laughest laughest laughing laugh - laughs laugh laughter laughter - launce launc launcelot launcelot - launces launc launch launch - laund laund laundress laundress - laundry laundri laur laur - laura laura laurel laurel - laurels laurel laurence laurenc - laus lau lavache lavach - lave lave lavee lave - lavender lavend lavina lavina - lavinia lavinia lavish lavish - lavishly lavishli lavolt lavolt - lavoltas lavolta law law - lawful law lawfully lawfulli - lawless lawless lawlessly lawlessli - lawn lawn lawns lawn - lawrence lawrenc laws law - lawyer lawyer lawyers lawyer - lay lai layer layer - layest layest laying lai - lays lai lazar lazar - lazars lazar lazarus lazaru - lazy lazi lc lc - ld ld ldst ldst - le le lead lead - leaden leaden leader leader - leaders leader leadest leadest - leading lead leads lead - leaf leaf leagu leagu - league leagu leagued leagu - leaguer leaguer leagues leagu - leah leah leak leak - leaky leaki lean lean - leander leander leaner leaner - leaning lean leanness lean - leans lean leap leap - leaped leap leaping leap - leaps leap leapt leapt - lear lear learn learn - learned learn learnedly learnedli - learning learn learnings learn - learns learn learnt learnt - leas lea lease leas - leases leas leash leash - leasing leas least least - leather leather leathern leathern - leav leav leave leav - leaven leaven leavening leaven - leaver leaver leaves leav - leaving leav leavy leavi - lecher lecher lecherous lecher - lechers lecher lechery lecheri - lecon lecon lecture lectur - lectures lectur led led - leda leda leech leech - leeches leech leek leek - leeks leek leer leer - leers leer lees lee - leese lees leet leet - leets leet left left - leg leg legacies legaci - legacy legaci legate legat - legatine legatin lege lege - legerity leger leges lege - legg legg legion legion - legions legion legitimate legitim - legitimation legitim legs leg - leicester leicest leicestershire leicestershir - leiger leiger leigers leiger - leisure leisur leisurely leisur - leisures leisur leman leman - lemon lemon lena lena - lend lend lender lender - lending lend lendings lend - lends lend length length - lengthen lengthen lengthens lengthen - lengths length lenity leniti - lennox lennox lent lent - lenten lenten lentus lentu - leo leo leon leon - leonardo leonardo leonati leonati - leonato leonato leonatus leonatu - leontes leont leopard leopard - leopards leopard leper leper - leperous leper lepidus lepidu - leprosy leprosi lequel lequel - lers ler les le - less less lessen lessen - lessens lessen lesser lesser - lesson lesson lessoned lesson - lessons lesson lest lest - lestrake lestrak let let - lethargied lethargi lethargies lethargi - lethargy lethargi lethe leth - lets let lett lett - letter letter letters letter - letting let lettuce lettuc - leur leur leve leve - level level levell level - levelled level levels level - leven leven levers lever - leviathan leviathan leviathans leviathan - levied levi levies levi - levity leviti levy levi - levying levi lewd lewd - lewdly lewdli lewdness lewd - lewdsters lewdster lewis lewi - liable liabl liar liar - liars liar libbard libbard - libelling libel libels libel - liberal liber liberality liber - liberte libert liberties liberti - libertine libertin libertines libertin - liberty liberti library librari - libya libya licence licenc - licens licen license licens - licentious licenti lichas licha - licio licio lick lick - licked lick licker licker - lictors lictor lid lid - lids lid lie lie - lied li lief lief - liefest liefest liege lieg - liegeman liegeman liegemen liegemen - lien lien lies li - liest liest lieth lieth - lieu lieu lieutenant lieuten - lieutenantry lieutenantri lieutenants lieuten - lieve liev life life - lifeblood lifeblood lifeless lifeless - lifelings lifel lift lift - lifted lift lifter lifter - lifteth lifteth lifting lift - lifts lift lig lig - ligarius ligariu liggens liggen - light light lighted light - lighten lighten lightens lighten - lighter lighter lightest lightest - lightly lightli lightness light - lightning lightn lightnings lightn - lights light lik lik - like like liked like - likeliest likeliest likelihood likelihood - likelihoods likelihood likely like - likeness like liker liker - likes like likest likest - likewise likewis liking like - likings like lilies lili - lily lili lim lim - limander limand limb limb - limbeck limbeck limbecks limbeck - limber limber limbo limbo - limbs limb lime lime - limed lime limehouse limehous - limekilns limekiln limit limit - limitation limit limited limit - limits limit limn limn - limp limp limping limp - limps limp lin lin - lincoln lincoln lincolnshire lincolnshir - line line lineal lineal - lineally lineal lineament lineament - lineaments lineament lined line - linen linen linens linen - lines line ling ling - lingare lingar linger linger - lingered linger lingers linger - linguist linguist lining line - link link links link - linsey linsei linstock linstock - linta linta lion lion - lionel lionel lioness lioness - lions lion lip lip - lipp lipp lips lip - lipsbury lipsburi liquid liquid - liquor liquor liquorish liquorish - liquors liquor lirra lirra - lisbon lisbon lisp lisp - lisping lisp list list - listen listen listening listen - lists list literatured literatur - lither lither litter litter - little littl littlest littlest - liv liv live live - lived live livelier liveli - livelihood livelihood livelong livelong - lively live liver liver - liveries liveri livers liver - livery liveri lives live - livest livest liveth liveth - livia livia living live - livings live lizard lizard - lizards lizard ll ll - lll lll llous llou - lnd lnd lo lo - loa loa loach loach - load load loaden loaden - loading load loads load - loaf loaf loam loam - loan loan loath loath - loathe loath loathed loath - loather loather loathes loath - loathing loath loathly loathli - loathness loath loathsome loathsom - loathsomeness loathsom loathsomest loathsomest - loaves loav lob lob - lobbies lobbi lobby lobbi - local local lochaber lochab - lock lock locked lock - locking lock lockram lockram - locks lock locusts locust - lode lode lodg lodg - lodge lodg lodged lodg - lodgers lodger lodges lodg - lodging lodg lodgings lodg - lodovico lodovico lodowick lodowick - lofty lofti log log - logger logger loggerhead loggerhead - loggerheads loggerhead loggets logget - logic logic logs log - loins loin loiter loiter - loiterer loiter loiterers loiter - loitering loiter lolling loll - lolls loll lombardy lombardi - london london londoners london - lone lone loneliness loneli - lonely lone long long - longaville longavil longboat longboat - longed long longer longer - longest longest longeth longeth - longing long longings long - longly longli longs long - longtail longtail loo loo - loof loof look look - looked look looker looker - lookers looker lookest lookest - looking look looks look - loon loon loop loop - loos loo loose loos - loosed loos loosely loos - loosen loosen loosing loos - lop lop lopp lopp - loquitur loquitur lord lord - lorded lord lording lord - lordings lord lordliness lordli - lordly lordli lords lord - lordship lordship lordships lordship - lorenzo lorenzo lorn lorn - lorraine lorrain lorship lorship - los lo lose lose - loser loser losers loser - loses lose losest losest - loseth loseth losing lose - loss loss losses loss - lost lost lot lot - lots lot lott lott - lottery lotteri loud loud - louder louder loudly loudli - lour lour loureth loureth - louring lour louse lous - louses lous lousy lousi - lout lout louted lout - louts lout louvre louvr - lov lov love love - loved love lovedst lovedst - lovel lovel lovelier loveli - loveliness loveli lovell lovel - lovely love lover lover - lovered lover lovers lover - loves love lovest lovest - loveth loveth loving love - lovingly lovingli low low - lowe low lower lower - lowest lowest lowing low - lowliness lowli lowly lowli - lown lown lowness low - loyal loyal loyally loyal - loyalties loyalti loyalty loyalti - lozel lozel lt lt - lubber lubber lubberly lubberli - luc luc luccicos luccico - luce luce lucentio lucentio - luces luce lucetta lucetta - luciana luciana lucianus lucianu - lucifer lucif lucifier lucifi - lucilius luciliu lucina lucina - lucio lucio lucius luciu - luck luck luckier luckier - luckiest luckiest luckily luckili - luckless luckless lucky lucki - lucre lucr lucrece lucrec - lucretia lucretia lucullius luculliu - lucullus lucullu lucy luci - lud lud ludlow ludlow - lug lug lugg lugg - luggage luggag luke luke - lukewarm lukewarm lull lull - lulla lulla lullaby lullabi - lulls lull lumbert lumbert - lump lump lumpish lumpish - luna luna lunacies lunaci - lunacy lunaci lunatic lunat - lunatics lunat lunes lune - lungs lung lupercal luperc - lurch lurch lure lure - lurk lurk lurketh lurketh - lurking lurk lurks lurk - luscious lusciou lush lush - lust lust lusted lust - luster luster lustful lust - lustier lustier lustiest lustiest - lustig lustig lustihood lustihood - lustily lustili lustre lustr - lustrous lustrou lusts lust - lusty lusti lute lute - lutes lute lutestring lutestr - lutheran lutheran luxurious luxuri - luxuriously luxuri luxury luxuri - ly ly lycaonia lycaonia - lycurguses lycurgus lydia lydia - lye lye lyen lyen - lying ly lym lym - lymoges lymog lynn lynn - lysander lysand m m - ma ma maan maan - mab mab macbeth macbeth - maccabaeus maccabaeu macdonwald macdonwald - macduff macduff mace mace - macedon macedon maces mace - machiavel machiavel machination machin - machinations machin machine machin - mack mack macmorris macmorri - maculate macul maculation macul - mad mad madam madam - madame madam madams madam - madcap madcap madded mad - madding mad made made - madeira madeira madly madli - madman madman madmen madmen - madness mad madonna madonna - madrigals madrig mads mad - maecenas maecena maggot maggot - maggots maggot magic magic - magical magic magician magician - magistrate magistr magistrates magistr - magnanimity magnanim magnanimous magnanim - magni magni magnifi magnifi - magnificence magnific magnificent magnific - magnifico magnifico magnificoes magnifico - magnus magnu mahomet mahomet - mahu mahu maid maid - maiden maiden maidenhead maidenhead - maidenheads maidenhead maidenhood maidenhood - maidenhoods maidenhood maidenliest maidenliest - maidenly maidenli maidens maiden - maidhood maidhood maids maid - mail mail mailed mail - mails mail maim maim - maimed maim maims maim - main main maincourse maincours - maine main mainly mainli - mainmast mainmast mains main - maintain maintain maintained maintain - maintains maintain maintenance mainten - mais mai maison maison - majestas majesta majestee majeste - majestic majest majestical majest - majestically majest majesties majesti - majesty majesti major major - majority major mak mak - make make makeless makeless - maker maker makers maker - makes make makest makest - maketh maketh making make - makings make mal mal - mala mala maladies maladi - malady maladi malapert malapert - malcolm malcolm malcontent malcont - malcontents malcont male male - maledictions maledict malefactions malefact - malefactor malefactor malefactors malefactor - males male malevolence malevol - malevolent malevol malhecho malhecho - malice malic malicious malici - maliciously malici malign malign - malignancy malign malignant malign - malignantly malignantli malkin malkin - mall mall mallard mallard - mallet mallet mallows mallow - malmsey malmsei malt malt - maltworms maltworm malvolio malvolio - mamillius mamilliu mammering mammer - mammet mammet mammets mammet - mammock mammock man man - manacle manacl manacles manacl - manage manag managed manag - manager manag managing manag - manakin manakin manchus manchu - mandate mandat mandragora mandragora - mandrake mandrak mandrakes mandrak - mane mane manent manent - manes mane manet manet - manfully manfulli mangle mangl - mangled mangl mangles mangl - mangling mangl mangy mangi - manhood manhood manhoods manhood - manifest manifest manifested manifest - manifests manifest manifold manifold - manifoldly manifoldli manka manka - mankind mankind manlike manlik - manly manli mann mann - manna manna manner manner - mannerly mannerli manners manner - manningtree manningtre mannish mannish - manor manor manors manor - mans man mansion mansion - mansionry mansionri mansions mansion - manslaughter manslaught mantle mantl - mantled mantl mantles mantl - mantua mantua mantuan mantuan - manual manual manure manur - manured manur manus manu - many mani map map - mapp mapp maps map - mar mar marble marbl - marbled marbl marcade marcad - marcellus marcellu march march - marches march marcheth marcheth - marching march marchioness marchio - marchpane marchpan marcians marcian - marcius marciu marcus marcu - mardian mardian mare mare - mares mare marg marg - margarelon margarelon margaret margaret - marge marg margent margent - margery margeri maria maria - marian marian mariana mariana - maries mari marigold marigold - mariner marin mariners marin - maritime maritim marjoram marjoram - mark mark marked mark - market market marketable market - marketplace marketplac markets market - marking mark markman markman - marks mark marl marl - marle marl marmoset marmoset - marquess marquess marquis marqui - marr marr marriage marriag - marriages marriag married marri - marries marri marring mar - marrow marrow marrowless marrowless - marrows marrow marry marri - marrying marri mars mar - marseilles marseil marsh marsh - marshal marshal marshalsea marshalsea - marshalship marshalship mart mart - marted mart martem martem - martext martext martial martial - martin martin martino martino - martius martiu martlemas martlema - martlet martlet marts mart - martyr martyr martyrs martyr - marullus marullu marv marv - marvel marvel marvell marvel - marvellous marvel marvellously marvel - marvels marvel mary mari - mas ma masculine masculin - masham masham mask mask - masked mask masker masker - maskers masker masking mask - masks mask mason mason - masonry masonri masons mason - masque masqu masquers masquer - masques masqu masquing masqu - mass mass massacre massacr - massacres massacr masses mass - massy massi mast mast - mastcr mastcr master master - masterdom masterdom masterest masterest - masterless masterless masterly masterli - masterpiece masterpiec masters master - mastership mastership mastic mastic - mastiff mastiff mastiffs mastiff - masts mast match match - matches match matcheth matcheth - matching match matchless matchless - mate mate mated mate - mater mater material materi - mates mate mathematics mathemat - matin matin matron matron - matrons matron matter matter - matters matter matthew matthew - mattock mattock mattress mattress - mature matur maturity matur - maud maud maudlin maudlin - maugre maugr maul maul - maund maund mauri mauri - mauritania mauritania mauvais mauvai - maw maw maws maw - maxim maxim may mai - mayday maydai mayest mayest - mayor mayor maypole maypol - mayst mayst maz maz - maze maze mazed maze - mazes maze mazzard mazzard - me me meacock meacock - mead mead meadow meadow - meadows meadow meads mead - meagre meagr meal meal - meals meal mealy meali - mean mean meanders meander - meaner meaner meanest meanest - meaneth meaneth meaning mean - meanings mean meanly meanli - means mean meant meant - meantime meantim meanwhile meanwhil - measles measl measur measur - measurable measur measure measur - measured measur measureless measureless - measures measur measuring measur - meat meat meats meat - mechanic mechan mechanical mechan - mechanicals mechan mechanics mechan - mechante mechant med med - medal medal meddle meddl - meddler meddler meddling meddl - mede mede medea medea - media media mediation mediat - mediators mediat medice medic - medicinal medicin medicine medicin - medicines medicin meditate medit - meditates medit meditating medit - meditation medit meditations medit - mediterranean mediterranean mediterraneum mediterraneum - medlar medlar medlars medlar - meed meed meeds meed - meek meek meekly meekli - meekness meek meet meet - meeter meeter meetest meetest - meeting meet meetings meet - meetly meetli meetness meet - meets meet meg meg - mehercle mehercl meilleur meilleur - meiny meini meisen meisen - melancholies melancholi melancholy melancholi - melford melford mell mell - mellifluous melliflu mellow mellow - mellowing mellow melodious melodi - melody melodi melt melt - melted melt melteth melteth - melting melt melts melt - melun melun member member - members member memento memento - memorable memor memorandums memorandum - memorial memori memorials memori - memories memori memoriz memoriz - memorize memor memory memori - memphis memphi men men - menac menac menace menac - menaces menac menaphon menaphon - menas mena mend mend - mended mend mender mender - mending mend mends mend - menecrates menecr menelaus menelau - menenius meneniu mental mental - menteith menteith mention mention - mentis menti menton menton - mephostophilus mephostophilu mer mer - mercatante mercatant mercatio mercatio - mercenaries mercenari mercenary mercenari - mercer mercer merchandise merchandis - merchandized merchand merchant merchant - merchants merchant mercies merci - merciful merci mercifully mercifulli - merciless merciless mercurial mercuri - mercuries mercuri mercury mercuri - mercutio mercutio mercy merci - mere mere mered mere - merely mere merest merest - meridian meridian merit merit - merited merit meritorious meritori - merits merit merlin merlin - mermaid mermaid mermaids mermaid - merops merop merrier merrier - merriest merriest merrily merrili - merriman merriman merriment merriment - merriments merriment merriness merri - merry merri mervailous mervail - mes me mesh mesh - meshes mesh mesopotamia mesopotamia - mess mess message messag - messages messag messala messala - messaline messalin messenger messeng - messengers messeng messes mess - messina messina met met - metal metal metals metal - metamorphis metamorphi metamorphoses metamorphos - metaphor metaphor metaphysical metaphys - metaphysics metaphys mete mete - metellus metellu meteor meteor - meteors meteor meteyard meteyard - metheglin metheglin metheglins metheglin - methink methink methinks methink - method method methods method - methought methought methoughts methought - metre metr metres metr - metropolis metropoli mette mett - mettle mettl mettled mettl - meus meu mew mew - mewed mew mewling mewl - mexico mexico mi mi - mice mice michael michael - michaelmas michaelma micher micher - miching mich mickle mickl - microcosm microcosm mid mid - midas mida middest middest - middle middl middleham middleham - midnight midnight midriff midriff - midst midst midsummer midsumm - midway midwai midwife midwif - midwives midwiv mienne mienn - might might mightful might - mightier mightier mightiest mightiest - mightily mightili mightiness mighti - mightst mightst mighty mighti - milan milan milch milch - mild mild milder milder - mildest mildest mildew mildew - mildews mildew mildly mildli - mildness mild mile mile - miles mile milford milford - militarist militarist military militari - milk milk milking milk - milkmaid milkmaid milks milk - milksops milksop milky milki - mill mill mille mill - miller miller milliner millin - million million millioned million - millions million mills mill - millstones millston milo milo - mimic mimic minc minc - mince minc minces minc - mincing minc mind mind - minded mind minding mind - mindless mindless minds mind - mine mine mineral miner - minerals miner minerva minerva - mines mine mingle mingl - mingled mingl mingling mingl - minikin minikin minim minim - minime minim minimo minimo - minimus minimu mining mine - minion minion minions minion - minist minist minister minist - ministers minist ministration ministr - minnow minnow minnows minnow - minola minola minority minor - minos mino minotaurs minotaur - minstrel minstrel minstrels minstrel - minstrelsy minstrelsi mint mint - mints mint minute minut - minutely minut minutes minut - minx minx mio mio - mir mir mirable mirabl - miracle miracl miracles miracl - miraculous miracul miranda miranda - mire mire mirror mirror - mirrors mirror mirth mirth - mirthful mirth miry miri - mis mi misadventur misadventur - misadventure misadventur misanthropos misanthropo - misapplied misappli misbecame misbecam - misbecom misbecom misbecome misbecom - misbegot misbegot misbegotten misbegotten - misbeliever misbeliev misbelieving misbeliev - misbhav misbhav miscall miscal - miscalled miscal miscarried miscarri - miscarries miscarri miscarry miscarri - miscarrying miscarri mischance mischanc - mischances mischanc mischief mischief - mischiefs mischief mischievous mischiev - misconceived misconceiv misconst misconst - misconster misconst misconstruction misconstruct - misconstrued misconstru misconstrues misconstru - miscreant miscreant miscreate miscreat - misdeed misde misdeeds misde - misdemean misdemean misdemeanours misdemeanour - misdoubt misdoubt misdoubteth misdoubteth - misdoubts misdoubt misenum misenum - miser miser miserable miser - miserably miser misericorde misericord - miseries miseri misers miser - misery miseri misfortune misfortun - misfortunes misfortun misgive misgiv - misgives misgiv misgiving misgiv - misgoverned misgovern misgovernment misgovern - misgraffed misgraf misguide misguid - mishap mishap mishaps mishap - misheard misheard misinterpret misinterpret - mislead mislead misleader mislead - misleaders mislead misleading mislead - misled misl mislike mislik - misord misord misplac misplac - misplaced misplac misplaces misplac - mispris mispri misprised mispris - misprision mispris misprizing mispriz - misproud misproud misquote misquot - misreport misreport miss miss - missed miss misses miss - misshap misshap misshapen misshapen - missheathed missheath missing miss - missingly missingli missions mission - missive missiv missives missiv - misspoke misspok mist mist - mista mista mistak mistak - mistake mistak mistaken mistaken - mistakes mistak mistaketh mistaketh - mistaking mistak mistakings mistak - mistemp mistemp mistempered mistemp - misterm misterm mistful mist - misthink misthink misthought misthought - mistletoe mistleto mistook mistook - mistreadings mistread mistress mistress - mistresses mistress mistresss mistresss - mistriship mistriship mistrust mistrust - mistrusted mistrust mistrustful mistrust - mistrusting mistrust mists mist - misty misti misus misu - misuse misus misused misus - misuses misus mites mite - mithridates mithrid mitigate mitig - mitigation mitig mix mix - mixed mix mixture mixtur - mixtures mixtur mm mm - mnd mnd moan moan - moans moan moat moat - moated moat mobled mobl - mock mock mockable mockabl - mocker mocker mockeries mockeri - mockers mocker mockery mockeri - mocking mock mocks mock - mockvater mockvat mockwater mockwat - model model modena modena - moderate moder moderately moder - moderation moder modern modern - modest modest modesties modesti - modestly modestli modesty modesti - modicums modicum modo modo - module modul moe moe - moi moi moiety moieti - moist moist moisten moisten - moisture moistur moldwarp moldwarp - mole mole molehill molehil - moles mole molest molest - molestation molest mollification mollif - mollis molli molten molten - molto molto mome mome - moment moment momentary momentari - moming mome mon mon - monachum monachum monarch monarch - monarchies monarchi monarchize monarch - monarcho monarcho monarchs monarch - monarchy monarchi monast monast - monastery monasteri monastic monast - monday mondai monde mond - money monei moneys monei - mong mong monger monger - mongers monger monging mong - mongrel mongrel mongrels mongrel - mongst mongst monk monk - monkey monkei monkeys monkei - monks monk monmouth monmouth - monopoly monopoli mons mon - monsieur monsieur monsieurs monsieur - monster monster monsters monster - monstrous monstrou monstrously monstrous - monstrousness monstrous monstruosity monstruos - montacute montacut montage montag - montague montagu montagues montagu - montano montano montant montant - montez montez montferrat montferrat - montgomery montgomeri month month - monthly monthli months month - montjoy montjoi monument monument - monumental monument monuments monument - mood mood moods mood - moody moodi moon moon - moonbeams moonbeam moonish moonish - moonlight moonlight moons moon - moonshine moonshin moonshines moonshin - moor moor moorfields moorfield - moors moor moorship moorship - mop mop mope mope - moping mope mopping mop - mopsa mopsa moral moral - moraler moral morality moral - moralize moral mordake mordak - more more moreover moreov - mores more morgan morgan - mori mori morisco morisco - morn morn morning morn - mornings morn morocco morocco - morris morri morrow morrow - morrows morrow morsel morsel - morsels morsel mort mort - mortal mortal mortality mortal - mortally mortal mortals mortal - mortar mortar mortgaged mortgag - mortified mortifi mortifying mortifi - mortimer mortim mortimers mortim - mortis morti mortise mortis - morton morton mose mose - moss moss mossgrown mossgrown - most most mote mote - moth moth mother mother - mothers mother moths moth - motion motion motionless motionless - motions motion motive motiv - motives motiv motley motlei - mots mot mought mought - mould mould moulded mould - mouldeth mouldeth moulds mould - mouldy mouldi moult moult - moulten moulten mounch mounch - mounseur mounseur mounsieur mounsieur - mount mount mountain mountain - mountaineer mountain mountaineers mountain - mountainous mountain mountains mountain - mountant mountant mountanto mountanto - mountebank mountebank mountebanks mountebank - mounted mount mounteth mounteth - mounting mount mounts mount - mourn mourn mourned mourn - mourner mourner mourners mourner - mournful mourn mournfully mournfulli - mourning mourn mourningly mourningli - mournings mourn mourns mourn - mous mou mouse mous - mousetrap mousetrap mousing mous - mouth mouth mouthed mouth - mouths mouth mov mov - movables movabl move move - moveable moveabl moveables moveabl - moved move mover mover - movers mover moves move - moveth moveth moving move - movingly movingli movousus movousu - mow mow mowbray mowbrai - mower mower mowing mow - mows mow moy moi - moys moi moyses moys - mrs mr much much - muck muck mud mud - mudded mud muddied muddi - muddy muddi muffins muffin - muffl muffl muffle muffl - muffled muffl muffler muffler - muffling muffl mugger mugger - mugs mug mulberries mulberri - mulberry mulberri mule mule - mules mule muleteers mulet - mulier mulier mulieres mulier - muliteus muliteu mull mull - mulmutius mulmutiu multiplied multipli - multiply multipli multiplying multipli - multipotent multipot multitude multitud - multitudes multitud multitudinous multitudin - mum mum mumble mumbl - mumbling mumbl mummers mummer - mummy mummi mun mun - munch munch muniments muniment - munition munit murd murd - murder murder murdered murder - murderer murder murderers murder - murdering murder murderous murder - murders murder mure mure - murk murk murkiest murkiest - murky murki murmur murmur - murmurers murmur murmuring murmur - murrain murrain murray murrai - murrion murrion murther murther - murtherer murther murtherers murther - murthering murther murtherous murther - murthers murther mus mu - muscadel muscadel muscovites muscovit - muscovits muscovit muscovy muscovi - muse muse muses muse - mush mush mushrooms mushroom - music music musical music - musician musician musicians musician - musics music musing muse - musings muse musk musk - musket musket muskets musket - muskos musko muss muss - mussel mussel mussels mussel - must must mustachio mustachio - mustard mustard mustardseed mustardse - muster muster mustering muster - musters muster musty musti - mutability mutabl mutable mutabl - mutation mutat mutations mutat - mute mute mutes mute - mutest mutest mutine mutin - mutineer mutin mutineers mutin - mutines mutin mutinies mutini - mutinous mutin mutiny mutini - mutius mutiu mutter mutter - muttered mutter mutton mutton - muttons mutton mutual mutual - mutualities mutual mutually mutual - muzzl muzzl muzzle muzzl - muzzled muzzl mv mv - mww mww my my - mynheers mynheer myrmidon myrmidon - myrmidons myrmidon myrtle myrtl - myself myself myst myst - mysteries mysteri mystery mysteri - n n nag nag - nage nage nags nag - naiads naiad nail nail - nails nail nak nak - naked nake nakedness naked - nal nal nam nam - name name named name - nameless nameless namely name - names name namest namest - naming name nan nan - nance nanc nap nap - nape nape napes nape - napkin napkin napkins napkin - naples napl napless napless - napping nap naps nap - narbon narbon narcissus narcissu - narines narin narrow narrow - narrowly narrowli naso naso - nasty nasti nathaniel nathaniel - natifs natif nation nation - nations nation native nativ - nativity nativ natur natur - natural natur naturalize natur - naturally natur nature natur - natured natur natures natur - natus natu naught naught - naughtily naughtili naughty naughti - navarre navarr nave nave - navel navel navigation navig - navy navi nay nai - nayward nayward nayword nayword - nazarite nazarit ne ne - neaf neaf neamnoins neamnoin - neanmoins neanmoin neapolitan neapolitan - neapolitans neapolitan near near - nearer nearer nearest nearest - nearly nearli nearness near - neat neat neatly neatli - neb neb nebour nebour - nebuchadnezzar nebuchadnezzar nec nec - necessaries necessari necessarily necessarili - necessary necessari necessitied necess - necessities necess necessity necess - neck neck necklace necklac - necks neck nectar nectar - ned ned nedar nedar - need need needed need - needer needer needful need - needfull needful needing need - needle needl needles needl - needless needless needly needli - needs need needy needi - neer neer neeze neez - nefas nefa negation negat - negative neg negatives neg - neglect neglect neglected neglect - neglecting neglect neglectingly neglectingli - neglection neglect negligence neglig - negligent neglig negotiate negoti - negotiations negoti negro negro - neigh neigh neighbors neighbor - neighbour neighbour neighbourhood neighbourhood - neighbouring neighbour neighbourly neighbourli - neighbours neighbour neighing neigh - neighs neigh neither neither - nell nell nemean nemean - nemesis nemesi neoptolemus neoptolemu - nephew nephew nephews nephew - neptune neptun ner ner - nereides nereid nerissa nerissa - nero nero neroes nero - ners ner nerve nerv - nerves nerv nervii nervii - nervy nervi nessus nessu - nest nest nestor nestor - nests nest net net - nether nether netherlands netherland - nets net nettle nettl - nettled nettl nettles nettl - neuter neuter neutral neutral - nev nev never never - nevil nevil nevils nevil - new new newborn newborn - newer newer newest newest - newgate newgat newly newli - newness new news new - newsmongers newsmong newt newt - newts newt next next - nibbling nibbl nicanor nicanor - nice nice nicely nice - niceness nice nicer nicer - nicety niceti nicholas nichola - nick nick nickname nicknam - nicks nick niece niec - nieces niec niggard niggard - niggarding niggard niggardly niggardli - nigh nigh night night - nightcap nightcap nightcaps nightcap - nighted night nightgown nightgown - nightingale nightingal nightingales nightingal - nightly nightli nightmare nightmar - nights night nightwork nightwork - nihil nihil nile nile - nill nill nilus nilu - nimble nimbl nimbleness nimbl - nimbler nimbler nimbly nimbl - nine nine nineteen nineteen - ning ning ningly ningli - ninny ninni ninth ninth - ninus ninu niobe niob - niobes niob nip nip - nipp nipp nipping nip - nipple nippl nips nip - nit nit nly nly - nnight nnight nnights nnight - no no noah noah - nob nob nobility nobil - nobis nobi noble nobl - nobleman nobleman noblemen noblemen - nobleness nobl nobler nobler - nobles nobl noblesse nobless - noblest noblest nobly nobli - nobody nobodi noces noce - nod nod nodded nod - nodding nod noddle noddl - noddles noddl noddy noddi - nods nod noes noe - nointed noint nois noi - noise nois noiseless noiseless - noisemaker noisemak noises nois - noisome noisom nole nole - nominate nomin nominated nomin - nomination nomin nominativo nominativo - non non nonage nonag - nonce nonc none none - nonino nonino nonny nonni - nonpareil nonpareil nonsuits nonsuit - nony noni nook nook - nooks nook noon noon - noonday noondai noontide noontid - nor nor norbery norberi - norfolk norfolk norman norman - normandy normandi normans norman - north north northampton northampton - northamptonshire northamptonshir northerly northerli - northern northern northgate northgat - northumberland northumberland northumberlands northumberland - northward northward norway norwai - norways norwai norwegian norwegian - norweyan norweyan nos no - nose nose nosegays nosegai - noseless noseless noses nose - noster noster nostra nostra - nostril nostril nostrils nostril - not not notable notabl - notably notabl notary notari - notch notch note note - notebook notebook noted note - notedly notedli notes note - notest notest noteworthy noteworthi - nothing noth nothings noth - notice notic notify notifi - noting note notion notion - notorious notori notoriously notori - notre notr notwithstanding notwithstand - nought nought noun noun - nouns noun nourish nourish - nourished nourish nourisher nourish - nourishes nourish nourisheth nourisheth - nourishing nourish nourishment nourish - nous nou novel novel - novelties novelti novelty novelti - noverbs noverb novi novi - novice novic novices novic - novum novum now now - nowhere nowher noyance noyanc - ns ns nt nt - nubibus nubibu numa numa - numb numb number number - numbered number numbering number - numberless numberless numbers number - numbness numb nun nun - nuncio nuncio nuncle nuncl - nunnery nunneri nuns nun - nuntius nuntiu nuptial nuptial - nurs nur nurse nurs - nursed nurs nurser nurser - nursery nurseri nurses nurs - nurseth nurseth nursh nursh - nursing nurs nurtur nurtur - nurture nurtur nut nut - nuthook nuthook nutmeg nutmeg - nutmegs nutmeg nutriment nutriment - nuts nut nutshell nutshel - ny ny nym nym - nymph nymph nymphs nymph - o o oak oak - oaken oaken oaks oak - oared oar oars oar - oatcake oatcak oaten oaten - oath oath oathable oathabl - oaths oath oats oat - ob ob obduracy obduraci - obdurate obdur obedience obedi - obedient obedi obeisance obeis - oberon oberon obey obei - obeyed obei obeying obei - obeys obei obidicut obidicut - object object objected object - objections object objects object - oblation oblat oblations oblat - obligation oblig obligations oblig - obliged oblig oblique obliqu - oblivion oblivion oblivious oblivi - obloquy obloqui obscene obscen - obscenely obscen obscur obscur - obscure obscur obscured obscur - obscurely obscur obscures obscur - obscuring obscur obscurity obscur - obsequies obsequi obsequious obsequi - obsequiously obsequi observ observ - observance observ observances observ - observancy observ observant observ - observants observ observation observ - observe observ observed observ - observer observ observers observ - observing observ observingly observingli - obsque obsqu obstacle obstacl - obstacles obstacl obstinacy obstinaci - obstinate obstin obstinately obstin - obstruct obstruct obstruction obstruct - obstructions obstruct obtain obtain - obtained obtain obtaining obtain - occasion occas occasions occas - occident occid occidental occident - occulted occult occupat occupat - occupation occup occupations occup - occupied occupi occupies occupi - occupy occupi occurrence occurr - occurrences occurr occurrents occurr - ocean ocean oceans ocean - octavia octavia octavius octaviu - ocular ocular od od - odd odd oddest oddest - oddly oddli odds odd - ode od odes od - odious odiou odoriferous odorifer - odorous odor odour odour - odours odour ods od - oeillades oeillad oes oe - oeuvres oeuvr of of - ofephesus ofephesu off off - offal offal offence offenc - offenceful offenc offences offenc - offend offend offended offend - offendendo offendendo offender offend - offenders offend offendeth offendeth - offending offend offendress offendress - offends offend offense offens - offenseless offenseless offenses offens - offensive offens offer offer - offered offer offering offer - offerings offer offers offer - offert offert offic offic - office offic officed offic - officer offic officers offic - offices offic official offici - officious offici offspring offspr - oft oft often often - oftener often oftentimes oftentim - oh oh oil oil - oils oil oily oili - old old oldcastle oldcastl - olden olden older older - oldest oldest oldness old - olive oliv oliver oliv - olivers oliv olives oliv - olivia olivia olympian olympian - olympus olympu oman oman - omans oman omen omen - ominous omin omission omiss - omit omit omittance omitt - omitted omit omitting omit - omne omn omnes omn - omnipotent omnipot on on - once onc one on - ones on oneyers oney - ongles ongl onion onion - onions onion only onli - onset onset onward onward - onwards onward oo oo - ooze ooz oozes ooz - oozy oozi op op - opal opal ope op - open open opener open - opening open openly openli - openness open opens open - operant oper operate oper - operation oper operations oper - operative oper opes op - oph oph ophelia ophelia - opinion opinion opinions opinion - opportune opportun opportunities opportun - opportunity opportun oppos oppo - oppose oppos opposed oppos - opposeless opposeless opposer oppos - opposers oppos opposes oppos - opposing oppos opposite opposit - opposites opposit opposition opposit - oppositions opposit oppress oppress - oppressed oppress oppresses oppress - oppresseth oppresseth oppressing oppress - oppression oppress oppressor oppressor - opprest opprest opprobriously opprobri - oppugnancy oppugn opulency opul - opulent opul or or - oracle oracl oracles oracl - orange orang oration orat - orator orat orators orat - oratory oratori orb orb - orbed orb orbs orb - orchard orchard orchards orchard - ord ord ordain ordain - ordained ordain ordaining ordain - order order ordered order - ordering order orderless orderless - orderly orderli orders order - ordinance ordin ordinant ordin - ordinaries ordinari ordinary ordinari - ordnance ordnanc ords ord - ordure ordur ore or - organ organ organs organ - orgillous orgil orient orient - orifex orifex origin origin - original origin orisons orison - ork ork orlando orlando - orld orld orleans orlean - ornament ornament ornaments ornament - orodes orod orphan orphan - orphans orphan orpheus orpheu - orsino orsino ort ort - orthography orthographi orts ort - oscorbidulchos oscorbidulcho osier osier - osiers osier osprey osprei - osr osr osric osric - ossa ossa ost ost - ostent ostent ostentare ostentar - ostentation ostent ostents ostent - ostler ostler ostlers ostler - ostrich ostrich osw osw - oswald oswald othello othello - other other othergates otherg - others other otherwhere otherwher - otherwhiles otherwhil otherwise otherwis - otter otter ottoman ottoman - ottomites ottomit oublie oubli - ouches ouch ought ought - oui oui ounce ounc - ounces ounc ouphes ouph - our our ours our - ourself ourself ourselves ourselv - ousel ousel out out - outbids outbid outbrave outbrav - outbraves outbrav outbreak outbreak - outcast outcast outcries outcri - outcry outcri outdar outdar - outdare outdar outdares outdar - outdone outdon outfac outfac - outface outfac outfaced outfac - outfacing outfac outfly outfli - outfrown outfrown outgo outgo - outgoes outgo outgrown outgrown - outjest outjest outlaw outlaw - outlawry outlawri outlaws outlaw - outliv outliv outlive outliv - outlives outliv outliving outliv - outlook outlook outlustres outlustr - outpriz outpriz outrage outrag - outrageous outrag outrages outrag - outran outran outright outright - outroar outroar outrun outrun - outrunning outrun outruns outrun - outscold outscold outscorn outscorn - outsell outsel outsells outsel - outside outsid outsides outsid - outspeaks outspeak outsport outsport - outstare outstar outstay outstai - outstood outstood outstretch outstretch - outstretched outstretch outstrike outstrik - outstrip outstrip outstripped outstrip - outswear outswear outvenoms outvenom - outward outward outwardly outwardli - outwards outward outwear outwear - outweighs outweigh outwent outwent - outworn outworn outworths outworth - oven oven over over - overawe overaw overbear overbear - overblown overblown overboard overboard - overbold overbold overborne overborn - overbulk overbulk overbuys overbui - overcame overcam overcast overcast - overcharg overcharg overcharged overcharg - overcome overcom overcomes overcom - overdone overdon overearnest overearnest - overfar overfar overflow overflow - overflown overflown overglance overgl - overgo overgo overgone overgon - overgorg overgorg overgrown overgrown - overhead overhead overhear overhear - overheard overheard overhold overhold - overjoyed overjoi overkind overkind - overland overland overleather overleath - overlive overl overlook overlook - overlooking overlook overlooks overlook - overmaster overmast overmounting overmount - overmuch overmuch overpass overpass - overpeer overp overpeering overp - overplus overplu overrul overrul - overrun overrun overscutch overscutch - overset overset overshades overshad - overshine overshin overshines overshin - overshot overshot oversights oversight - overspread overspread overstain overstain - overswear overswear overt overt - overta overta overtake overtak - overtaketh overtaketh overthrow overthrow - overthrown overthrown overthrows overthrow - overtook overtook overtopp overtopp - overture overtur overturn overturn - overwatch overwatch overween overween - overweening overween overweigh overweigh - overwhelm overwhelm overwhelming overwhelm - overworn overworn ovid ovid - ovidius ovidiu ow ow - owe ow owed ow - owedst owedst owen owen - owes ow owest owest - oweth oweth owing ow - owl owl owls owl - own own owner owner - owners owner owning own - owns own owy owi - ox ox oxen oxen - oxford oxford oxfordshire oxfordshir - oxlips oxlip oyes oy - oyster oyster p p - pabble pabbl pabylon pabylon - pac pac pace pace - paced pace paces pace - pacified pacifi pacify pacifi - pacing pace pack pack - packet packet packets packet - packhorses packhors packing pack - packings pack packs pack - packthread packthread pacorus pacoru - paction paction pad pad - paddle paddl paddling paddl - paddock paddock padua padua - pagan pagan pagans pagan - page page pageant pageant - pageants pageant pages page - pah pah paid paid - pail pail pailfuls pail - pails pail pain pain - pained pain painful pain - painfully painfulli pains pain - paint paint painted paint - painter painter painting paint - paintings paint paints paint - pair pair paired pair - pairs pair pajock pajock - pal pal palabras palabra - palace palac palaces palac - palamedes palamed palate palat - palates palat palatine palatin - palating palat pale pale - paled pale paleness pale - paler paler pales pale - palestine palestin palfrey palfrei - palfreys palfrei palisadoes palisado - pall pall pallabris pallabri - pallas palla pallets pallet - palm palm palmer palmer - palmers palmer palms palm - palmy palmi palpable palpabl - palsied palsi palsies palsi - palsy palsi palt palt - palter palter paltry paltri - paly pali pamp pamp - pamper pamper pamphlets pamphlet - pan pan pancackes pancack - pancake pancak pancakes pancak - pandar pandar pandars pandar - pandarus pandaru pander pander - panderly panderli panders pander - pandulph pandulph panel panel - pang pang panging pang - pangs pang pannier pannier - pannonians pannonian pansa pansa - pansies pansi pant pant - pantaloon pantaloon panted pant - pantheon pantheon panther panther - panthino panthino panting pant - pantingly pantingli pantler pantler - pantry pantri pants pant - pap pap papal papal - paper paper papers paper - paphlagonia paphlagonia paphos papho - papist papist paps pap - par par parable parabl - paracelsus paracelsu paradise paradis - paradox paradox paradoxes paradox - paragon paragon paragons paragon - parallel parallel parallels parallel - paramour paramour paramours paramour - parapets parapet paraquito paraquito - parasite parasit parasites parasit - parca parca parcel parcel - parcell parcel parcels parcel - parch parch parched parch - parching parch parchment parchment - pard pard pardon pardon - pardona pardona pardoned pardon - pardoner pardon pardoning pardon - pardonne pardonn pardonner pardonn - pardonnez pardonnez pardons pardon - pare pare pared pare - parel parel parent parent - parentage parentag parents parent - parfect parfect paring pare - parings pare paris pari - parish parish parishioners parishion - parisians parisian paritors paritor - park park parks park - parle parl parler parler - parles parl parley parlei - parlez parlez parliament parliament - parlors parlor parlour parlour - parlous parlou parmacity parmac - parolles parol parricide parricid - parricides parricid parrot parrot - parrots parrot parsley parslei - parson parson part part - partake partak partaken partaken - partaker partak partakers partak - parted part parthia parthia - parthian parthian parthians parthian - parti parti partial partial - partialize partial partially partial - participate particip participation particip - particle particl particular particular - particularities particular particularize particular - particularly particularli particulars particular - parties parti parting part - partisan partisan partisans partisan - partition partit partizan partizan - partlet partlet partly partli - partner partner partners partner - partridge partridg parts part - party parti pas pa - pash pash pashed pash - pashful pash pass pass - passable passabl passado passado - passage passag passages passag - passant passant passed pass - passenger passeng passengers passeng - passes pass passeth passeth - passing pass passio passio - passion passion passionate passion - passioning passion passions passion - passive passiv passport passport - passy passi past past - paste past pasterns pastern - pasties pasti pastime pastim - pastimes pastim pastoral pastor - pastorals pastor pastors pastor - pastry pastri pasture pastur - pastures pastur pasty pasti - pat pat patay patai - patch patch patchery patcheri - patches patch pate pate - pated pate patent patent - patents patent paternal patern - pates pate path path - pathetical pathet paths path - pathway pathwai pathways pathwai - patience patienc patient patient - patiently patient patients patient - patines patin patrician patrician - patricians patrician patrick patrick - patrimony patrimoni patroclus patroclu - patron patron patronage patronag - patroness patro patrons patron - patrum patrum patter patter - pattern pattern patterns pattern - pattle pattl pauca pauca - paucas pauca paul paul - paulina paulina paunch paunch - paunches paunch pause paus - pauser pauser pauses paus - pausingly pausingli pauvres pauvr - pav pav paved pave - pavement pavement pavilion pavilion - pavilions pavilion pavin pavin - paw paw pawn pawn - pawns pawn paws paw - pax pax pay pai - payest payest paying pai - payment payment payments payment - pays pai paysan paysan - paysans paysan pe pe - peace peac peaceable peaceabl - peaceably peaceabl peaceful peac - peacemakers peacemak peaces peac - peach peach peaches peach - peacock peacock peacocks peacock - peak peak peaking peak - peal peal peals peal - pear pear peard peard - pearl pearl pearls pearl - pears pear peas pea - peasant peasant peasantry peasantri - peasants peasant peascod peascod - pease peas peaseblossom peaseblossom - peat peat peaten peaten - peating peat pebble pebbl - pebbled pebbl pebbles pebbl - peck peck pecks peck - peculiar peculiar pecus pecu - pedant pedant pedantical pedant - pedascule pedascul pede pede - pedestal pedest pedigree pedigre - pedlar pedlar pedlars pedlar - pedro pedro peds ped - peel peel peep peep - peeped peep peeping peep - peeps peep peer peer - peereth peereth peering peer - peerless peerless peers peer - peesel peesel peevish peevish - peevishly peevishli peflur peflur - peg peg pegasus pegasu - pegs peg peise peis - peised peis peize peiz - pelf pelf pelican pelican - pelion pelion pell pell - pella pella pelleted pellet - peloponnesus peloponnesu pelt pelt - pelting pelt pembroke pembrok - pen pen penalties penalti - penalty penalti penance penanc - pence penc pencil pencil - pencill pencil pencils pencil - pendant pendant pendent pendent - pendragon pendragon pendulous pendul - penelope penelop penetrable penetr - penetrate penetr penetrative penetr - penitence penit penitent penit - penitential penitenti penitently penit - penitents penit penker penker - penknife penknif penn penn - penned pen penning pen - pennons pennon penny penni - pennyworth pennyworth pennyworths pennyworth - pens pen pense pens - pension pension pensioners pension - pensive pensiv pensived pensiv - pensively pensiv pent pent - pentecost pentecost penthesilea penthesilea - penthouse penthous penurious penuri - penury penuri peopl peopl - people peopl peopled peopl - peoples peopl pepin pepin - pepper pepper peppercorn peppercorn - peppered pepper per per - peradventure peradventur peradventures peradventur - perceiv perceiv perceive perceiv - perceived perceiv perceives perceiv - perceiveth perceiveth perch perch - perchance perchanc percies perci - percussion percuss percy perci - perdie perdi perdita perdita - perdition perdit perdonato perdonato - perdu perdu perdurable perdur - perdurably perdur perdy perdi - pere pere peregrinate peregrin - peremptorily peremptorili peremptory peremptori - perfect perfect perfected perfect - perfecter perfect perfectest perfectest - perfection perfect perfections perfect - perfectly perfectli perfectness perfect - perfidious perfidi perfidiously perfidi - perforce perforc perform perform - performance perform performances perform - performed perform performer perform - performers perform performing perform - performs perform perfum perfum - perfume perfum perfumed perfum - perfumer perfum perfumes perfum - perge perg perhaps perhap - periapts periapt perigort perigort - perigouna perigouna peril peril - perilous peril perils peril - period period periods period - perish perish perished perish - perishest perishest perisheth perisheth - perishing perish periwig periwig - perjur perjur perjure perjur - perjured perjur perjuries perjuri - perjury perjuri perk perk - perkes perk permafoy permafoi - permanent perman permission permiss - permissive permiss permit permit - permitted permit pernicious pernici - perniciously pernici peroration peror - perpend perpend perpendicular perpendicular - perpendicularly perpendicularli perpetual perpetu - perpetually perpetu perpetuity perpetu - perplex perplex perplexed perplex - perplexity perplex pers per - persecuted persecut persecutions persecut - persecutor persecutor perseus perseu - persever persev perseverance persever - persevers persev persia persia - persian persian persist persist - persisted persist persistency persist - persistive persist persists persist - person person personae persona - personage personag personages personag - personal person personally person - personate person personated person - personates person personating person - persons person perspective perspect - perspectively perspect perspectives perspect - perspicuous perspicu persuade persuad - persuaded persuad persuades persuad - persuading persuad persuasion persuas - persuasions persuas pert pert - pertain pertain pertaining pertain - pertains pertain pertaunt pertaunt - pertinent pertin pertly pertli - perturb perturb perturbation perturb - perturbations perturb perturbed perturb - perus peru perusal perus - peruse perus perused perus - perusing perus perverse pervers - perversely pervers perverseness pervers - pervert pervert perverted pervert - peseech peseech pest pest - pester pester pestiferous pestifer - pestilence pestil pestilent pestil - pet pet petar petar - peter peter petit petit - petition petit petitionary petitionari - petitioner petition petitioners petition - petitions petit peto peto - petrarch petrarch petruchio petruchio - petter petter petticoat petticoat - petticoats petticoat pettiness petti - pettish pettish pettitoes pettito - petty petti peu peu - pew pew pewter pewter - pewterer pewter phaethon phaethon - phaeton phaeton phantasime phantasim - phantasimes phantasim phantasma phantasma - pharamond pharamond pharaoh pharaoh - pharsalia pharsalia pheasant pheasant - pheazar pheazar phebe phebe - phebes phebe pheebus pheebu - pheeze pheez phibbus phibbu - philadelphos philadelpho philario philario - philarmonus philarmonu philemon philemon - philip philip philippan philippan - philippe philipp philippi philippi - phillida phillida philo philo - philomel philomel philomela philomela - philosopher philosoph philosophers philosoph - philosophical philosoph philosophy philosophi - philostrate philostr philotus philotu - phlegmatic phlegmat phoebe phoeb - phoebus phoebu phoenicia phoenicia - phoenicians phoenician phoenix phoenix - phorbus phorbu photinus photinu - phrase phrase phraseless phraseless - phrases phrase phrygia phrygia - phrygian phrygian phrynia phrynia - physic physic physical physic - physician physician physicians physician - physics physic pia pia - pibble pibbl pible pibl - picardy picardi pick pick - pickaxe pickax pickaxes pickax - pickbone pickbon picked pick - pickers picker picking pick - pickle pickl picklock picklock - pickpurse pickpurs picks pick - pickt pickt pickthanks pickthank - pictur pictur picture pictur - pictured pictur pictures pictur - pid pid pie pie - piec piec piece piec - pieces piec piecing piec - pied pi piedness pied - pier pier pierc pierc - pierce pierc pierced pierc - pierces pierc pierceth pierceth - piercing pierc piercy pierci - piers pier pies pi - piety pieti pig pig - pigeon pigeon pigeons pigeon - pight pight pigmy pigmi - pigrogromitus pigrogromitu pike pike - pikes pike pil pil - pilate pilat pilates pilat - pilchers pilcher pile pile - piles pile pilf pilf - pilfering pilfer pilgrim pilgrim - pilgrimage pilgrimag pilgrims pilgrim - pill pill pillage pillag - pillagers pillag pillar pillar - pillars pillar pillicock pillicock - pillory pillori pillow pillow - pillows pillow pills pill - pilot pilot pilots pilot - pimpernell pimpernel pin pin - pinch pinch pinched pinch - pinches pinch pinching pinch - pindarus pindaru pine pine - pined pine pines pine - pinfold pinfold pining pine - pinion pinion pink pink - pinn pinn pinnace pinnac - pins pin pinse pins - pint pint pintpot pintpot - pioned pion pioneers pioneer - pioner pioner pioners pioner - pious piou pip pip - pipe pipe piper piper - pipers piper pipes pipe - piping pipe pippin pippin - pippins pippin pirate pirat - pirates pirat pisa pisa - pisanio pisanio pish pish - pismires pismir piss piss - pissing piss pistol pistol - pistols pistol pit pit - pitch pitch pitched pitch - pitcher pitcher pitchers pitcher - pitchy pitchi piteous piteou - piteously piteous pitfall pitfal - pith pith pithless pithless - pithy pithi pitie piti - pitied piti pities piti - pitiful piti pitifully pitifulli - pitiless pitiless pits pit - pittance pittanc pittie pitti - pittikins pittikin pity piti - pitying piti pius piu - plac plac place place - placed place placentio placentio - places place placeth placeth - placid placid placing place - plack plack placket placket - plackets placket plagu plagu - plague plagu plagued plagu - plagues plagu plaguing plagu - plaguy plagui plain plain - plainer plainer plainest plainest - plaining plain plainings plain - plainly plainli plainness plain - plains plain plainsong plainsong - plaintful plaint plaintiff plaintiff - plaintiffs plaintiff plaints plaint - planched planch planet planet - planetary planetari planets planet - planks plank plant plant - plantage plantag plantagenet plantagenet - plantagenets plantagenet plantain plantain - plantation plantat planted plant - planteth planteth plants plant - plash plash plashy plashi - plast plast plaster plaster - plasterer plaster plat plat - plate plate plated plate - plates plate platform platform - platforms platform plats plat - platted plat plausible plausibl - plausive plausiv plautus plautu - play plai played plai - player player players player - playeth playeth playfellow playfellow - playfellows playfellow playhouse playhous - playing plai plays plai - plea plea pleach pleach - pleached pleach plead plead - pleaded plead pleader pleader - pleaders pleader pleading plead - pleads plead pleas plea - pleasance pleasanc pleasant pleasant - pleasantly pleasantli please pleas - pleased pleas pleaser pleaser - pleasers pleaser pleases pleas - pleasest pleasest pleaseth pleaseth - pleasing pleas pleasure pleasur - pleasures pleasur plebeians plebeian - plebeii plebeii plebs pleb - pledge pledg pledges pledg - pleines plein plenitude plenitud - plenteous plenteou plenteously plenteous - plenties plenti plentiful plenti - plentifully plentifulli plenty plenti - pless pless plessed pless - plessing pless pliant pliant - plied pli plies pli - plight plight plighted plight - plighter plighter plod plod - plodded plod plodders plodder - plodding plod plods plod - plood plood ploody ploodi - plot plot plots plot - plotted plot plotter plotter - plough plough ploughed plough - ploughman ploughman ploughmen ploughmen - plow plow plows plow - pluck pluck plucked pluck - plucker plucker plucking pluck - plucks pluck plue plue - plum plum plume plume - plumed plume plumes plume - plummet plummet plump plump - plumpy plumpi plums plum - plung plung plunge plung - plunged plung plural plural - plurisy plurisi plus plu - pluto pluto plutus plutu - ply ply po po - pocket pocket pocketing pocket - pockets pocket pocky pocki - pody podi poem poem - poesy poesi poet poet - poetical poetic poetry poetri - poets poet poictiers poictier - poinards poinard poins poin - point point pointblank pointblank - pointed point pointing point - points point pois poi - poise pois poising pois - poison poison poisoned poison - poisoner poison poisoning poison - poisonous poison poisons poison - poke poke poking poke - pol pol polack polack - polacks polack poland poland - pold pold pole pole - poleaxe poleax polecat polecat - polecats polecat polemon polemon - poles pole poli poli - policies polici policy polici - polish polish polished polish - politic polit politician politician - politicians politician politicly politicli - polixenes polixen poll poll - polluted pollut pollution pollut - polonius poloniu poltroons poltroon - polusion polus polydamus polydamu - polydore polydor polyxena polyxena - pomander pomand pomegranate pomegran - pomewater pomewat pomfret pomfret - pomgarnet pomgarnet pommel pommel - pomp pomp pompeius pompeiu - pompey pompei pompion pompion - pompous pompou pomps pomp - pond pond ponder ponder - ponderous ponder ponds pond - poniard poniard poniards poniard - pont pont pontic pontic - pontifical pontif ponton ponton - pooh pooh pool pool - poole pool poop poop - poor poor poorer poorer - poorest poorest poorly poorli - pop pop pope pope - popedom popedom popilius popiliu - popingay popingai popish popish - popp popp poppy poppi - pops pop popular popular - popularity popular populous popul - porch porch porches porch - pore pore poring pore - pork pork porn porn - porpentine porpentin porridge porridg - porringer porring port port - portable portabl portage portag - portal portal portance portanc - portcullis portculli portend portend - portends portend portent portent - portentous portent portents portent - porter porter porters porter - portia portia portion portion - portly portli portotartarossa portotartarossa - portrait portrait portraiture portraitur - ports port portugal portug - pose pose posied posi - posies posi position posit - positive posit positively posit - posse poss possess possess - possessed possess possesses possess - possesseth possesseth possessing possess - possession possess possessions possess - possessor possessor posset posset - possets posset possibilities possibl - possibility possibl possible possibl - possibly possibl possitable possit - post post poste post - posted post posterior posterior - posteriors posterior posterity poster - postern postern posterns postern - posters poster posthorse posthors - posthorses posthors posthumus posthumu - posting post postmaster postmast - posts post postscript postscript - posture postur postures postur - posy posi pot pot - potable potabl potations potat - potato potato potatoes potato - potch potch potency potenc - potent potent potentates potent - potential potenti potently potent - potents potent pothecary pothecari - pother pother potion potion - potions potion potpan potpan - pots pot potter potter - potting pot pottle pottl - pouch pouch poulter poulter - poultice poultic poultney poultnei - pouncet pouncet pound pound - pounds pound pour pour - pourest pourest pouring pour - pourquoi pourquoi pours pour - pout pout poverty poverti - pow pow powd powd - powder powder power power - powerful power powerfully powerfulli - powerless powerless powers power - pox pox poys poi - poysam poysam prabbles prabbl - practic practic practice practic - practiced practic practicer practic - practices practic practicing practic - practis practi practisants practis - practise practis practiser practis - practisers practis practises practis - practising practis praeclarissimus praeclarissimu - praemunire praemunir praetor praetor - praetors praetor pragging prag - prague pragu prain prain - prains prain prais prai - praise prais praised prais - praises prais praisest praisest - praiseworthy praiseworthi praising prais - prancing pranc prank prank - pranks prank prat prat - prate prate prated prate - prater prater prating prate - prattle prattl prattler prattler - prattling prattl prave prave - prawls prawl prawns prawn - pray prai prayer prayer - prayers prayer praying prai - prays prai pre pre - preach preach preached preach - preachers preacher preaches preach - preaching preach preachment preachment - pread pread preambulate preambul - precedence preced precedent preced - preceding preced precept precept - preceptial precepti precepts precept - precinct precinct precious preciou - preciously precious precipice precipic - precipitating precipit precipitation precipit - precise precis precisely precis - preciseness precis precisian precisian - precor precor precurse precurs - precursors precursor predeceased predeceas - predecessor predecessor predecessors predecessor - predestinate predestin predicament predica - predict predict prediction predict - predictions predict predominance predomin - predominant predomin predominate predomin - preeches preech preeminence preemin - preface prefac prefer prefer - preferment prefer preferments prefer - preferr preferr preferreth preferreth - preferring prefer prefers prefer - prefiguring prefigur prefix prefix - prefixed prefix preformed preform - pregnancy pregnanc pregnant pregnant - pregnantly pregnantli prejudicates prejud - prejudice prejudic prejudicial prejudici - prelate prelat premeditated premedit - premeditation premedit premised premis - premises premis prenez prenez - prenominate prenomin prentice prentic - prentices prentic preordinance preordin - prepar prepar preparation prepar - preparations prepar prepare prepar - prepared prepar preparedly preparedli - prepares prepar preparing prepar - prepost prepost preposterous preposter - preposterously preposter prerogatifes prerogatif - prerogative prerog prerogatived prerogativ - presage presag presagers presag - presages presag presageth presageth - presaging presag prescience prescienc - prescribe prescrib prescript prescript - prescription prescript prescriptions prescript - prescripts prescript presence presenc - presences presenc present present - presentation present presented present - presenter present presenters present - presenteth presenteth presenting present - presently present presentment present - presents present preserv preserv - preservation preserv preservative preserv - preserve preserv preserved preserv - preserver preserv preservers preserv - preserving preserv president presid - press press pressed press - presser presser presses press - pressing press pressure pressur - pressures pressur prest prest - prester prester presume presum - presumes presum presuming presum - presumption presumpt presumptuous presumptu - presuppos presuppo pret pret - pretence pretenc pretences pretenc - pretend pretend pretended pretend - pretending pretend pretense pretens - pretext pretext pretia pretia - prettier prettier prettiest prettiest - prettily prettili prettiness pretti - pretty pretti prevail prevail - prevailed prevail prevaileth prevaileth - prevailing prevail prevailment prevail - prevails prevail prevent prevent - prevented prevent prevention prevent - preventions prevent prevents prevent - prey prei preyful prey - preys prei priam priam - priami priami priamus priamu - pribbles pribbl price price - prick prick pricked prick - pricket pricket pricking prick - pricks prick pricksong pricksong - pride pride prides pride - pridge pridg prie prie - pried pri prief prief - pries pri priest priest - priesthood priesthood priests priest - prig prig primal primal - prime prime primer primer - primero primero primest primest - primitive primit primo primo - primogenity primogen primrose primros - primroses primros primy primi - prince princ princely princ - princes princ princess princess - principal princip principalities princip - principality princip principle principl - principles principl princox princox - prings pring print print - printed print printing print - printless printless prints print - prioress prioress priories priori - priority prioriti priory priori - priscian priscian prison prison - prisoner prison prisoners prison - prisonment prison prisonnier prisonni - prisons prison pristine pristin - prithe prith prithee prithe - privacy privaci private privat - privately privat privates privat - privilage privilag privileg privileg - privilege privileg privileged privileg - privileges privileg privilegio privilegio - privily privili privity priviti - privy privi priz priz - prize prize prized prize - prizer prizer prizes prize - prizest prizest prizing prize - pro pro probable probabl - probal probal probation probat - proceed proce proceeded proceed - proceeders proceed proceeding proceed - proceedings proceed proceeds proce - process process procession process - proclaim proclaim proclaimed proclaim - proclaimeth proclaimeth proclaims proclaim - proclamation proclam proclamations proclam - proconsul proconsul procrastinate procrastin - procreant procreant procreants procreant - procreation procreat procrus procru - proculeius proculeiu procur procur - procurator procur procure procur - procured procur procures procur - procuring procur prodigal prodig - prodigality prodig prodigally prodig - prodigals prodig prodigies prodigi - prodigious prodigi prodigiously prodigi - prodigy prodigi proditor proditor - produc produc produce produc - produced produc produces produc - producing produc proface profac - profan profan profanation profan - profane profan profaned profan - profanely profan profaneness profan - profaners profan profaning profan - profess profess professed profess - professes profess profession profess - professions profess professors professor - proffer proffer proffered proffer - profferer proffer proffers proffer - proficient profici profit profit - profitable profit profitably profit - profited profit profiting profit - profitless profitless profits profit - profound profound profoundest profoundest - profoundly profoundli progenitors progenitor - progeny progeni progne progn - prognosticate prognost prognostication prognost - progress progress progression progress - prohibit prohibit prohibition prohibit - project project projection project - projects project prolixious prolixi - prolixity prolix prologue prologu - prologues prologu prolong prolong - prolongs prolong promethean promethean - prometheus prometheu promis promi - promise promis promised promis - promises promis promiseth promiseth - promising promis promontory promontori - promotion promot promotions promot - prompt prompt prompted prompt - promptement promptement prompter prompter - prompting prompt prompts prompt - prompture promptur promulgate promulg - prone prone prononcer prononc - prononcez prononcez pronoun pronoun - pronounc pronounc pronounce pronounc - pronounced pronounc pronouncing pronounc - pronouns pronoun proof proof - proofs proof prop prop - propagate propag propagation propag - propend propend propension propens - proper proper properer proper - properly properli propertied properti - properties properti property properti - prophecies propheci prophecy propheci - prophesied prophesi prophesier prophesi - prophesy prophesi prophesying prophesi - prophet prophet prophetess prophetess - prophetic prophet prophetically prophet - prophets prophet propinquity propinqu - propontic propont proportion proport - proportionable proportion proportions proport - propos propo propose propos - proposed propos proposer propos - proposes propos proposing propos - proposition proposit propositions proposit - propounded propound propp propp - propre propr propriety proprieti - props prop propugnation propugn - prorogue prorogu prorogued prorogu - proscription proscript proscriptions proscript - prose prose prosecute prosecut - prosecution prosecut proselytes proselyt - proserpina proserpina prosp prosp - prospect prospect prosper prosper - prosperity prosper prospero prospero - prosperous prosper prosperously prosper - prospers prosper prostitute prostitut - prostrate prostrat protect protect - protected protect protection protect - protector protector protectors protector - protectorship protectorship protectress protectress - protects protect protest protest - protestation protest protestations protest - protested protest protester protest - protesting protest protests protest - proteus proteu protheus protheu - protract protract protractive protract - proud proud prouder prouder - proudest proudest proudlier proudlier - proudly proudli prouds proud - prov prov provand provand - prove prove proved prove - provender provend proverb proverb - proverbs proverb proves prove - proveth proveth provide provid - provided provid providence provid - provident provid providently provid - provider provid provides provid - province provinc provinces provinc - provincial provinci proving prove - provision provis proviso proviso - provocation provoc provok provok - provoke provok provoked provok - provoker provok provokes provok - provoketh provoketh provoking provok - provost provost prowess prowess - prudence prudenc prudent prudent - prun prun prune prune - prunes prune pruning prune - pry pry prying pry - psalm psalm psalmist psalmist - psalms psalm psalteries psalteri - ptolemies ptolemi ptolemy ptolemi - public public publican publican - publication public publicly publicli - publicola publicola publish publish - published publish publisher publish - publishing publish publius publiu - pucelle pucel puck puck - pudder pudder pudding pud - puddings pud puddle puddl - puddled puddl pudency pudenc - pueritia pueritia puff puff - puffing puf puffs puff - pugging pug puis pui - puissance puissanc puissant puissant - puke puke puking puke - pulcher pulcher puling pule - pull pull puller puller - pullet pullet pulling pull - pulls pull pulpit pulpit - pulpiter pulpit pulpits pulpit - pulse puls pulsidge pulsidg - pump pump pumpion pumpion - pumps pump pun pun - punched punch punish punish - punished punish punishes punish - punishment punish punishments punish - punk punk punto punto - puny puni pupil pupil - pupils pupil puppet puppet - puppets puppet puppies puppi - puppy puppi pur pur - purblind purblind purchas purcha - purchase purchas purchased purchas - purchases purchas purchaseth purchaseth - purchasing purchas pure pure - purely pure purer purer - purest purest purg purg - purgation purgat purgative purg - purgatory purgatori purge purg - purged purg purgers purger - purging purg purifies purifi - purifying purifi puritan puritan - purity puriti purlieus purlieu - purple purpl purpled purpl - purples purpl purport purport - purpos purpo purpose purpos - purposed purpos purposely purpos - purposes purpos purposeth purposeth - purposing purpos purr purr - purs pur purse purs - pursents pursent purses purs - pursu pursu pursue pursu - pursued pursu pursuers pursuer - pursues pursu pursuest pursuest - pursueth pursueth pursuing pursu - pursuit pursuit pursuivant pursuiv - pursuivants pursuiv pursy pursi - purus puru purveyor purveyor - push push pushes push - pusillanimity pusillanim put put - putrefy putrefi putrified putrifi - puts put putter putter - putting put puttock puttock - puzzel puzzel puzzle puzzl - puzzled puzzl puzzles puzzl - py py pygmalion pygmalion - pygmies pygmi pygmy pygmi - pyramid pyramid pyramides pyramid - pyramids pyramid pyramis pyrami - pyramises pyramis pyramus pyramu - pyrenean pyrenean pyrrhus pyrrhu - pythagoras pythagora qu qu - quadrangle quadrangl quae quae - quaff quaff quaffing quaf - quagmire quagmir quail quail - quailing quail quails quail - quaint quaint quaintly quaintli - quak quak quake quak - quakes quak qualification qualif - qualified qualifi qualifies qualifi - qualify qualifi qualifying qualifi - qualite qualit qualities qualiti - quality qualiti qualm qualm - qualmish qualmish quam quam - quand quand quando quando - quantities quantiti quantity quantiti - quare quar quarrel quarrel - quarrell quarrel quarreller quarrel - quarrelling quarrel quarrelous quarrel - quarrels quarrel quarrelsome quarrelsom - quarries quarri quarry quarri - quart quart quarter quarter - quartered quarter quartering quarter - quarters quarter quarts quart - quasi quasi quat quat - quatch quatch quay quai - que que quean quean - queas quea queasiness queasi - queasy queasi queen queen - queens queen quell quell - queller queller quench quench - quenched quench quenching quench - quenchless quenchless quern quern - quest quest questant questant - question question questionable question - questioned question questioning question - questionless questionless questions question - questrists questrist quests quest - queubus queubu qui qui - quick quick quicken quicken - quickens quicken quicker quicker - quicklier quicklier quickly quickli - quickness quick quicksand quicksand - quicksands quicksand quicksilverr quicksilverr - quid quid quiddities quidditi - quiddits quiddit quier quier - quiet quiet quieter quieter - quietly quietli quietness quiet - quietus quietu quill quill - quillets quillet quills quill - quilt quilt quinapalus quinapalu - quince quinc quinces quinc - quintain quintain quintessence quintess - quintus quintu quip quip - quips quip quire quir - quiring quir quirk quirk - quirks quirk quis qui - quit quit quite quit - quits quit quittance quittanc - quitted quit quitting quit - quiver quiver quivering quiver - quivers quiver quo quo - quod quod quoifs quoif - quoint quoint quoit quoit - quoits quoit quondam quondam - quoniam quoniam quote quot - quoted quot quotes quot - quoth quoth quotidian quotidian - r r rabbit rabbit - rabble rabbl rabblement rabblement - race race rack rack - rackers racker racket racket - rackets racket racking rack - racks rack radiance radianc - radiant radiant radish radish - rafe rafe raft raft - rag rag rage rage - rages rage rageth rageth - ragg ragg ragged rag - raggedness ragged raging rage - ragozine ragozin rags rag - rah rah rail rail - railed rail railer railer - railest railest raileth raileth - railing rail rails rail - raiment raiment rain rain - rainbow rainbow raineth raineth - raining rain rainold rainold - rains rain rainy raini - rais rai raise rais - raised rais raises rais - raising rais raisins raisin - rak rak rake rake - rakers raker rakes rake - ral ral rald rald - ralph ralph ram ram - rambures rambur ramm ramm - rampallian rampallian rampant rampant - ramping ramp rampir rampir - ramps ramp rams ram - ramsey ramsei ramston ramston - ran ran rance ranc - rancorous rancor rancors rancor - rancour rancour random random - rang rang range rang - ranged rang rangers ranger - ranges rang ranging rang - rank rank ranker ranker - rankest rankest ranking rank - rankle rankl rankly rankli - rankness rank ranks rank - ransack ransack ransacking ransack - ransom ransom ransomed ransom - ransoming ransom ransomless ransomless - ransoms ransom rant rant - ranting rant rap rap - rape rape rapes rape - rapier rapier rapiers rapier - rapine rapin raps rap - rapt rapt rapture raptur - raptures raptur rar rar - rare rare rarely rare - rareness rare rarer rarer - rarest rarest rarities rariti - rarity rariti rascal rascal - rascalliest rascalliest rascally rascal - rascals rascal rased rase - rash rash rasher rasher - rashly rashli rashness rash - rat rat ratcatcher ratcatch - ratcliff ratcliff rate rate - rated rate rately rate - rates rate rather rather - ratherest ratherest ratified ratifi - ratifiers ratifi ratify ratifi - rating rate rational ration - ratolorum ratolorum rats rat - ratsbane ratsban rattle rattl - rattles rattl rattling rattl - rature ratur raught raught - rav rav rave rave - ravel ravel raven raven - ravening raven ravenous raven - ravens raven ravenspurgh ravenspurgh - raves rave ravin ravin - raving rave ravish ravish - ravished ravish ravisher ravish - ravishing ravish ravishments ravish - raw raw rawer rawer - rawly rawli rawness raw - ray rai rayed rai - rays rai raz raz - raze raze razed raze - razes raze razeth razeth - razing raze razor razor - razorable razor razors razor - razure razur re re - reach reach reaches reach - reacheth reacheth reaching reach - read read reader reader - readiest readiest readily readili - readiness readi reading read - readins readin reads read - ready readi real real - really realli realm realm - realms realm reap reap - reapers reaper reaping reap - reaps reap rear rear - rears rear rearward rearward - reason reason reasonable reason - reasonably reason reasoned reason - reasoning reason reasonless reasonless - reasons reason reave reav - rebate rebat rebato rebato - rebeck rebeck rebel rebel - rebell rebel rebelling rebel - rebellion rebellion rebellious rebelli - rebels rebel rebound rebound - rebuk rebuk rebuke rebuk - rebukeable rebuk rebuked rebuk - rebukes rebuk rebus rebu - recall recal recant recant - recantation recant recanter recant - recanting recant receipt receipt - receipts receipt receiv receiv - receive receiv received receiv - receiver receiv receives receiv - receivest receivest receiveth receiveth - receiving receiv receptacle receptacl - rechate rechat reciprocal reciproc - reciprocally reciproc recite recit - recited recit reciterai reciterai - reck reck recking reck - reckless reckless reckon reckon - reckoned reckon reckoning reckon - reckonings reckon recks reck - reclaim reclaim reclaims reclaim - reclusive reclus recognizance recogniz - recognizances recogniz recoil recoil - recoiling recoil recollected recollect - recomforted recomfort recomforture recomfortur - recommend recommend recommended recommend - recommends recommend recompens recompen - recompense recompens reconcil reconcil - reconcile reconcil reconciled reconcil - reconcilement reconcil reconciler reconcil - reconciles reconcil reconciliation reconcili - record record recordation record - recorded record recorder record - recorders record records record - recount recount recounted recount - recounting recount recountments recount - recounts recount recourse recours - recov recov recover recov - recoverable recover recovered recov - recoveries recoveri recovers recov - recovery recoveri recreant recreant - recreants recreant recreate recreat - recreation recreat rectify rectifi - rector rector rectorship rectorship - recure recur recured recur - red red redbreast redbreast - redder redder reddest reddest - rede rede redeem redeem - redeemed redeem redeemer redeem - redeeming redeem redeems redeem - redeliver redeliv redemption redempt - redime redim redness red - redoubled redoubl redoubted redoubt - redound redound redress redress - redressed redress redresses redress - reduce reduc reechy reechi - reed reed reeds reed - reek reek reeking reek - reeks reek reeky reeki - reel reel reeleth reeleth - reeling reel reels reel - refell refel refer refer - reference refer referr referr - referred refer refigured refigur - refin refin refined refin - reflect reflect reflecting reflect - reflection reflect reflex reflex - reform reform reformation reform - reformed reform refractory refractori - refrain refrain refresh refresh - refreshing refresh reft reft - refts reft refuge refug - refus refu refusal refus - refuse refus refused refus - refusest refusest refusing refus - reg reg regal regal - regalia regalia regan regan - regard regard regardance regard - regarded regard regardfully regardfulli - regarding regard regards regard - regenerate regener regent regent - regentship regentship regia regia - regiment regiment regiments regiment - regina regina region region - regions region regist regist - register regist registers regist - regreet regreet regreets regreet - regress regress reguerdon reguerdon - regular regular rehears rehear - rehearsal rehears rehearse rehears - reign reign reigned reign - reignier reignier reigning reign - reigns reign rein rein - reinforc reinforc reinforce reinforc - reinforcement reinforc reins rein - reiterate reiter reject reject - rejected reject rejoic rejoic - rejoice rejoic rejoices rejoic - rejoiceth rejoiceth rejoicing rejoic - rejoicingly rejoicingli rejoindure rejoindur - rejourn rejourn rel rel - relapse relaps relate relat - relates relat relation relat - relations relat relative rel - releas relea release releas - released releas releasing releas - relent relent relenting relent - relents relent reliances relianc - relics relic relief relief - reliev reliev relieve reliev - relieved reliev relieves reliev - relieving reliev religion religion - religions religion religious religi - religiously religi relinquish relinquish - reliques reliqu reliquit reliquit - relish relish relume relum - rely reli relying reli - remain remain remainder remaind - remainders remaind remained remain - remaineth remaineth remaining remain - remains remain remark remark - remarkable remark remediate remedi - remedied remedi remedies remedi - remedy remedi rememb rememb - remember rememb remembered rememb - remembers rememb remembrance remembr - remembrancer remembranc remembrances remembr - remercimens remercimen remiss remiss - remission remiss remissness remiss - remit remit remnant remnant - remnants remnant remonstrance remonstr - remorse remors remorseful remors - remorseless remorseless remote remot - remotion remot remov remov - remove remov removed remov - removedness removed remover remov - removes remov removing remov - remunerate remuner remuneration remuner - rence renc rend rend - render render rendered render - renders render rendezvous rendezv - renegado renegado renege reneg - reneges reneg renew renew - renewed renew renewest renewest - renounce renounc renouncement renounc - renouncing renounc renowmed renowm - renown renown renowned renown - rent rent rents rent - repaid repaid repair repair - repaired repair repairing repair - repairs repair repass repass - repast repast repasture repastur - repay repai repaying repai - repays repai repeal repeal - repealing repeal repeals repeal - repeat repeat repeated repeat - repeating repeat repeats repeat - repel repel repent repent - repentance repent repentant repent - repented repent repenting repent - repents repent repetition repetit - repetitions repetit repin repin - repine repin repining repin - replant replant replenish replenish - replenished replenish replete replet - replication replic replied repli - replies repli repliest repliest - reply repli replying repli - report report reported report - reporter report reportest reportest - reporting report reportingly reportingli - reports report reposal repos - repose repos reposeth reposeth - reposing repos repossess repossess - reprehend reprehend reprehended reprehend - reprehending reprehend represent repres - representing repres reprieve repriev - reprieves repriev reprisal repris - reproach reproach reproaches reproach - reproachful reproach reproachfully reproachfulli - reprobate reprob reprobation reprob - reproof reproof reprov reprov - reprove reprov reproveable reprov - reproves reprov reproving reprov - repugn repugn repugnancy repugn - repugnant repugn repulse repuls - repulsed repuls repurchas repurcha - repured repur reputation reput - repute reput reputed reput - reputeless reputeless reputes reput - reputing reput request request - requested request requesting request - requests request requiem requiem - requir requir require requir - required requir requires requir - requireth requireth requiring requir - requisite requisit requisites requisit - requit requit requital requit - requite requit requited requit - requites requit rer rer - rere rere rers rer - rescu rescu rescue rescu - rescued rescu rescues rescu - rescuing rescu resemblance resembl - resemble resembl resembled resembl - resembles resembl resembleth resembleth - resembling resembl reserv reserv - reservation reserv reserve reserv - reserved reserv reserves reserv - reside resid residence resid - resident resid resides resid - residing resid residue residu - resign resign resignation resign - resist resist resistance resist - resisted resist resisting resist - resists resist resolute resolut - resolutely resolut resolutes resolut - resolution resolut resolv resolv - resolve resolv resolved resolv - resolvedly resolvedli resolves resolv - resolveth resolveth resort resort - resorted resort resounding resound - resounds resound respeaking respeak - respect respect respected respect - respecting respect respective respect - respectively respect respects respect - respice respic respite respit - respites respit responsive respons - respose respos ress ress - rest rest rested rest - resteth resteth restful rest - resting rest restitution restitut - restless restless restor restor - restoration restor restorative restor - restore restor restored restor - restores restor restoring restor - restrain restrain restrained restrain - restraining restrain restrains restrain - restraint restraint rests rest - resty resti resum resum - resume resum resumes resum - resurrections resurrect retail retail - retails retail retain retain - retainers retain retaining retain - retell retel retention retent - retentive retent retinue retinu - retir retir retire retir - retired retir retirement retir - retires retir retiring retir - retold retold retort retort - retorts retort retourne retourn - retract retract retreat retreat - retrograde retrograd rets ret - return return returned return - returnest returnest returneth returneth - returning return returns return - revania revania reveal reveal - reveals reveal revel revel - reveler revel revell revel - reveller revel revellers revel - revelling revel revelry revelri - revels revel reveng reveng - revenge reveng revenged reveng - revengeful reveng revengement reveng - revenger reveng revengers reveng - revenges reveng revenging reveng - revengingly revengingli revenue revenu - revenues revenu reverb reverb - reverberate reverber reverbs reverb - reverenc reverenc reverence rever - reverend reverend reverent rever - reverently rever revers rever - reverse revers reversion revers - reverted revert review review - reviewest reviewest revil revil - revile revil revisits revisit - reviv reviv revive reviv - revives reviv reviving reviv - revok revok revoke revok - revokement revok revolt revolt - revolted revolt revolting revolt - revolts revolt revolution revolut - revolutions revolut revolve revolv - revolving revolv reward reward - rewarded reward rewarder reward - rewarding reward rewards reward - reword reword reworded reword - rex rex rey rei - reynaldo reynaldo rford rford - rful rful rfull rfull - rhapsody rhapsodi rheims rheim - rhenish rhenish rhesus rhesu - rhetoric rhetor rheum rheum - rheumatic rheumat rheums rheum - rheumy rheumi rhinoceros rhinocero - rhodes rhode rhodope rhodop - rhubarb rhubarb rhym rhym - rhyme rhyme rhymers rhymer - rhymes rhyme rhyming rhyme - rialto rialto rib rib - ribald ribald riband riband - ribands riband ribaudred ribaudr - ribb ribb ribbed rib - ribbon ribbon ribbons ribbon - ribs rib rice rice - rich rich richard richard - richer richer riches rich - richest richest richly richli - richmond richmond richmonds richmond - rid rid riddance riddanc - ridden ridden riddle riddl - riddles riddl riddling riddl - ride ride rider rider - riders rider rides ride - ridest ridest rideth rideth - ridge ridg ridges ridg - ridiculous ridicul riding ride - rids rid rien rien - ries ri rifle rifl - rift rift rifted rift - rig rig rigg rigg - riggish riggish right right - righteous righteou righteously righteous - rightful right rightfully rightfulli - rightly rightli rights right - rigol rigol rigorous rigor - rigorously rigor rigour rigour - ril ril rim rim - rin rin rinaldo rinaldo - rind rind ring ring - ringing ring ringleader ringlead - ringlets ringlet rings ring - ringwood ringwood riot riot - rioter rioter rioting riot - riotous riotou riots riot - rip rip ripe ripe - ripely ripe ripen ripen - ripened ripen ripeness ripe - ripening ripen ripens ripen - riper riper ripest ripest - riping ripe ripp ripp - ripping rip rise rise - risen risen rises rise - riseth riseth rish rish - rising rise rite rite - rites rite rivage rivag - rival rival rivality rival - rivall rival rivals rival - rive rive rived rive - rivelled rivel river river - rivers river rivet rivet - riveted rivet rivets rivet - rivo rivo rj rj - rless rless road road - roads road roam roam - roaming roam roan roan - roar roar roared roar - roarers roarer roaring roar - roars roar roast roast - roasted roast rob rob - roba roba robas roba - robb robb robbed rob - robber robber robbers robber - robbery robberi robbing rob - robe robe robed robe - robert robert robes robe - robin robin robs rob - robustious robusti rochester rochest - rochford rochford rock rock - rocks rock rocky rocki - rod rod rode rode - roderigo roderigo rods rod - roe roe roes roe - roger roger rogero rogero - rogue rogu roguery rogueri - rogues rogu roguish roguish - roi roi roisting roist - roll roll rolled roll - rolling roll rolls roll - rom rom romage romag - roman roman romano romano - romanos romano romans roman - rome rome romeo romeo - romish romish rondure rondur - ronyon ronyon rood rood - roof roof roofs roof - rook rook rooks rook - rooky rooki room room - rooms room root root - rooted root rootedly rootedli - rooteth rooteth rooting root - roots root rope rope - ropery roperi ropes rope - roping rope ros ro - rosalind rosalind rosalinda rosalinda - rosalinde rosalind rosaline rosalin - roscius rosciu rose rose - rosed rose rosemary rosemari - rosencrantz rosencrantz roses rose - ross ross rosy rosi - rot rot rote rote - roted rote rother rother - rotherham rotherham rots rot - rotted rot rotten rotten - rottenness rotten rotting rot - rotundity rotund rouen rouen - rough rough rougher rougher - roughest roughest roughly roughli - roughness rough round round - rounded round roundel roundel - rounder rounder roundest roundest - rounding round roundly roundli - rounds round roundure roundur - rous rou rouse rous - roused rous rousillon rousillon - rously rousli roussi roussi - rout rout routed rout - routs rout rove rove - rover rover row row - rowel rowel rowland rowland - rowlands rowland roy roi - royal royal royalize royal - royally royal royalties royalti - royalty royalti roynish roynish - rs rs rt rt - rub rub rubb rubb - rubbing rub rubbish rubbish - rubies rubi rubious rubiou - rubs rub ruby rubi - rud rud rudand rudand - rudder rudder ruddiness ruddi - ruddock ruddock ruddy ruddi - rude rude rudely rude - rudeness rude ruder ruder - rudesby rudesbi rudest rudest - rudiments rudiment rue rue - rued ru ruff ruff - ruffian ruffian ruffians ruffian - ruffle ruffl ruffling ruffl - ruffs ruff rug rug - rugby rugbi rugemount rugemount - rugged rug ruin ruin - ruinate ruinat ruined ruin - ruining ruin ruinous ruinou - ruins ruin rul rul - rule rule ruled rule - ruler ruler rulers ruler - rules rule ruling rule - rumble rumbl ruminaies ruminai - ruminat ruminat ruminate rumin - ruminated rumin ruminates rumin - rumination rumin rumor rumor - rumour rumour rumourer rumour - rumours rumour rump rump - run run runagate runag - runagates runag runaway runawai - runaways runawai rung rung - runn runn runner runner - runners runner running run - runs run rupture ruptur - ruptures ruptur rural rural - rush rush rushes rush - rushing rush rushling rushl - rushy rushi russet russet - russia russia russian russian - russians russian rust rust - rusted rust rustic rustic - rustically rustic rustics rustic - rustle rustl rustling rustl - rusts rust rusty rusti - rut rut ruth ruth - ruthful ruth ruthless ruthless - rutland rutland ruttish ruttish - ry ry rye rye - rything ryth s s - sa sa saba saba - sabbath sabbath sable sabl - sables sabl sack sack - sackbuts sackbut sackcloth sackcloth - sacked sack sackerson sackerson - sacks sack sacrament sacrament - sacred sacr sacrific sacrif - sacrifice sacrific sacrificers sacrific - sacrifices sacrific sacrificial sacrifici - sacrificing sacrif sacrilegious sacrilegi - sacring sacr sad sad - sadder sadder saddest saddest - saddle saddl saddler saddler - saddles saddl sadly sadli - sadness sad saf saf - safe safe safeguard safeguard - safely safe safer safer - safest safest safeties safeti - safety safeti saffron saffron - sag sag sage sage - sagittary sagittari said said - saidst saidst sail sail - sailing sail sailmaker sailmak - sailor sailor sailors sailor - sails sail sain sain - saint saint sainted saint - saintlike saintlik saints saint - saith saith sake sake - sakes sake sala sala - salad salad salamander salamand - salary salari sale sale - salerio salerio salicam salicam - salique saliqu salisbury salisburi - sall sall sallet sallet - sallets sallet sallies salli - sallow sallow sally salli - salmon salmon salmons salmon - salt salt salter salter - saltiers saltier saltness salt - saltpetre saltpetr salutation salut - salutations salut salute salut - saluted salut salutes salut - saluteth saluteth salv salv - salvation salvat salve salv - salving salv same same - samingo samingo samp samp - sampire sampir sample sampl - sampler sampler sampson sampson - samson samson samsons samson - sancta sancta sanctified sanctifi - sanctifies sanctifi sanctify sanctifi - sanctimonies sanctimoni sanctimonious sanctimoni - sanctimony sanctimoni sanctities sanctiti - sanctity sanctiti sanctuarize sanctuar - sanctuary sanctuari sand sand - sandal sandal sandbag sandbag - sanded sand sands sand - sandy sandi sandys sandi - sang sang sanguine sanguin - sanguis sangui sanity saniti - sans san santrailles santrail - sap sap sapient sapient - sapit sapit sapless sapless - sapling sapl sapphire sapphir - sapphires sapphir saracens saracen - sarcenet sarcenet sard sard - sardians sardian sardinia sardinia - sardis sardi sarum sarum - sat sat satan satan - satchel satchel sate sate - sated sate satiate satiat - satiety satieti satin satin - satire satir satirical satir - satis sati satisfaction satisfact - satisfied satisfi satisfies satisfi - satisfy satisfi satisfying satisfi - saturday saturdai saturdays saturdai - saturn saturn saturnine saturnin - saturninus saturninu satyr satyr - satyrs satyr sauc sauc - sauce sauc sauced sauc - saucers saucer sauces sauc - saucily saucili sauciness sauci - saucy sauci sauf sauf - saunder saunder sav sav - savage savag savagely savag - savageness savag savagery savageri - savages savag save save - saved save saves save - saving save saviour saviour - savory savori savour savour - savouring savour savours savour - savoury savouri savoy savoi - saw saw sawed saw - sawest sawest sawn sawn - sawpit sawpit saws saw - sawyer sawyer saxons saxon - saxony saxoni saxton saxton - say sai sayest sayest - saying sai sayings sai - says sai sayst sayst - sblood sblood sc sc - scab scab scabbard scabbard - scabs scab scaffold scaffold - scaffoldage scaffoldag scal scal - scald scald scalded scald - scalding scald scale scale - scaled scale scales scale - scaling scale scall scall - scalp scalp scalps scalp - scaly scali scamble scambl - scambling scambl scamels scamel - scan scan scandal scandal - scandaliz scandaliz scandalous scandal - scandy scandi scann scann - scant scant scanted scant - scanter scanter scanting scant - scantling scantl scants scant - scap scap scape scape - scaped scape scapes scape - scapeth scapeth scar scar - scarce scarc scarcely scarc - scarcity scarciti scare scare - scarecrow scarecrow scarecrows scarecrow - scarf scarf scarfed scarf - scarfs scarf scaring scare - scarlet scarlet scarr scarr - scarre scarr scars scar - scarus scaru scath scath - scathe scath scathful scath - scatt scatt scatter scatter - scattered scatter scattering scatter - scatters scatter scelera scelera - scelerisque scelerisqu scene scene - scenes scene scent scent - scented scent scept scept - scepter scepter sceptre sceptr - sceptred sceptr sceptres sceptr - schedule schedul schedules schedul - scholar scholar scholarly scholarli - scholars scholar school school - schoolboy schoolboi schoolboys schoolboi - schoolfellows schoolfellow schooling school - schoolmaster schoolmast schoolmasters schoolmast - schools school sciatica sciatica - sciaticas sciatica science scienc - sciences scienc scimitar scimitar - scion scion scions scion - scissors scissor scoff scoff - scoffer scoffer scoffing scof - scoffs scoff scoggin scoggin - scold scold scolding scold - scolds scold sconce sconc - scone scone scope scope - scopes scope scorch scorch - scorched scorch score score - scored score scores score - scoring score scorn scorn - scorned scorn scornful scorn - scornfully scornfulli scorning scorn - scorns scorn scorpion scorpion - scorpions scorpion scot scot - scotch scotch scotches scotch - scotland scotland scots scot - scottish scottish scoundrels scoundrel - scour scour scoured scour - scourg scourg scourge scourg - scouring scour scout scout - scouts scout scowl scowl - scrap scrap scrape scrape - scraping scrape scraps scrap - scratch scratch scratches scratch - scratching scratch scream scream - screams scream screech screech - screeching screech screen screen - screens screen screw screw - screws screw scribbl scribbl - scribbled scribbl scribe scribe - scribes scribe scrimers scrimer - scrip scrip scrippage scrippag - scripture scriptur scriptures scriptur - scrivener scriven scroll scroll - scrolls scroll scroop scroop - scrowl scrowl scroyles scroyl - scrubbed scrub scruple scrupl - scruples scrupl scrupulous scrupul - scuffles scuffl scuffling scuffl - scullion scullion sculls scull - scum scum scurril scurril - scurrility scurril scurrilous scurril - scurvy scurvi scuse scuse - scut scut scutcheon scutcheon - scutcheons scutcheon scylla scylla - scythe scyth scythed scyth - scythia scythia scythian scythian - sdeath sdeath se se - sea sea seacoal seacoal - seafaring seafar seal seal - sealed seal sealing seal - seals seal seam seam - seamen seamen seamy seami - seaport seaport sear sear - searce searc search search - searchers searcher searches search - searcheth searcheth searching search - seared sear seas sea - seasick seasick seaside seasid - season season seasoned season - seasons season seat seat - seated seat seats seat - sebastian sebastian second second - secondarily secondarili secondary secondari - seconded second seconds second - secrecy secreci secret secret - secretaries secretari secretary secretari - secretly secretli secrets secret - sect sect sectary sectari - sects sect secundo secundo - secure secur securely secur - securing secur security secur - sedg sedg sedge sedg - sedges sedg sedgy sedgi - sedition sedit seditious sediti - seduc seduc seduce seduc - seduced seduc seducer seduc - seducing seduc see see - seed seed seeded seed - seedness seed seeds seed - seedsman seedsman seein seein - seeing see seek seek - seeking seek seeks seek - seel seel seeling seel - seely seeli seem seem - seemed seem seemers seemer - seemest seemest seemeth seemeth - seeming seem seemingly seemingli - seemly seemli seems seem - seen seen seer seer - sees see seese sees - seest seest seethe seeth - seethes seeth seething seeth - seeting seet segregation segreg - seigneur seigneur seigneurs seigneur - seiz seiz seize seiz - seized seiz seizes seiz - seizeth seizeth seizing seiz - seizure seizur seld seld - seldom seldom select select - seleucus seleucu self self - selfsame selfsam sell sell - seller seller selling sell - sells sell selves selv - semblable semblabl semblably semblabl - semblance semblanc semblances semblanc - semblative sembl semi semi - semicircle semicircl semiramis semirami - semper semper sempronius semproniu - senate senat senator senat - senators senat send send - sender sender sendeth sendeth - sending send sends send - seneca seneca senior senior - seniory seniori senis seni - sennet sennet senoys senoi - sense sens senseless senseless - senses sens sensible sensibl - sensibly sensibl sensual sensual - sensuality sensual sent sent - sentenc sentenc sentence sentenc - sentences sentenc sententious sententi - sentinel sentinel sentinels sentinel - separable separ separate separ - separated separ separates separ - separation separ septentrion septentrion - sepulchre sepulchr sepulchres sepulchr - sepulchring sepulchr sequel sequel - sequence sequenc sequent sequent - sequest sequest sequester sequest - sequestration sequestr sere sere - serenis sereni serge serg - sergeant sergeant serious seriou - seriously serious sermon sermon - sermons sermon serpent serpent - serpentine serpentin serpents serpent - serpigo serpigo serv serv - servant servant servanted servant - servants servant serve serv - served serv server server - serves serv serveth serveth - service servic serviceable servic - services servic servile servil - servility servil servilius serviliu - serving serv servingman servingman - servingmen servingmen serviteur serviteur - servitor servitor servitors servitor - servitude servitud sessa sessa - session session sessions session - sestos sesto set set - setebos setebo sets set - setter setter setting set - settle settl settled settl - settlest settlest settling settl - sev sev seven seven - sevenfold sevenfold sevennight sevennight - seventeen seventeen seventh seventh - seventy seventi sever sever - several sever severally sever - severals sever severe sever - severed sever severely sever - severest severest severing sever - severity sever severn severn - severs sever sew sew - seward seward sewer sewer - sewing sew sex sex - sexes sex sexton sexton - sextus sextu seymour seymour - seyton seyton sfoot sfoot - sh sh shackle shackl - shackles shackl shade shade - shades shade shadow shadow - shadowed shadow shadowing shadow - shadows shadow shadowy shadowi - shady shadi shafalus shafalu - shaft shaft shafts shaft - shag shag shak shak - shake shake shaked shake - shaken shaken shakes shake - shaking shake shales shale - shall shall shallenge shalleng - shallow shallow shallowest shallowest - shallowly shallowli shallows shallow - shalt shalt sham sham - shambles shambl shame shame - shamed shame shameful shame - shamefully shamefulli shameless shameless - shames shame shamest shamest - shaming shame shank shank - shanks shank shap shap - shape shape shaped shape - shapeless shapeless shapen shapen - shapes shape shaping shape - shar shar shard shard - sharded shard shards shard - share share shared share - sharers sharer shares share - sharing share shark shark - sharp sharp sharpen sharpen - sharpened sharpen sharpens sharpen - sharper sharper sharpest sharpest - sharply sharpli sharpness sharp - sharps sharp shatter shatter - shav shav shave shave - shaven shaven shaw shaw - she she sheaf sheaf - sheal sheal shear shear - shearers shearer shearing shear - shearman shearman shears shear - sheath sheath sheathe sheath - sheathed sheath sheathes sheath - sheathing sheath sheaved sheav - sheaves sheav shed shed - shedding shed sheds shed - sheen sheen sheep sheep - sheepcote sheepcot sheepcotes sheepcot - sheeps sheep sheepskins sheepskin - sheer sheer sheet sheet - sheeted sheet sheets sheet - sheffield sheffield shelf shelf - shell shell shells shell - shelt shelt shelter shelter - shelters shelter shelves shelv - shelving shelv shelvy shelvi - shent shent shepherd shepherd - shepherdes shepherd shepherdess shepherdess - shepherdesses shepherdess shepherds shepherd - sher sher sheriff sheriff - sherris sherri shes she - sheweth sheweth shield shield - shielded shield shields shield - shift shift shifted shift - shifting shift shifts shift - shilling shill shillings shill - shin shin shine shine - shines shine shineth shineth - shining shine shins shin - shiny shini ship ship - shipboard shipboard shipman shipman - shipmaster shipmast shipmen shipmen - shipp shipp shipped ship - shipping ship ships ship - shipt shipt shipwreck shipwreck - shipwrecking shipwreck shipwright shipwright - shipwrights shipwright shire shire - shirley shirlei shirt shirt - shirts shirt shive shive - shiver shiver shivering shiver - shivers shiver shoal shoal - shoals shoal shock shock - shocks shock shod shod - shoe shoe shoeing shoe - shoemaker shoemak shoes shoe - shog shog shone shone - shook shook shoon shoon - shoot shoot shooter shooter - shootie shooti shooting shoot - shoots shoot shop shop - shops shop shore shore - shores shore shorn shorn - short short shortcake shortcak - shorten shorten shortened shorten - shortens shorten shorter shorter - shortly shortli shortness short - shot shot shotten shotten - shoughs shough should should - shoulder shoulder shouldering shoulder - shoulders shoulder shouldst shouldst - shout shout shouted shout - shouting shout shouts shout - shov shov shove shove - shovel shovel shovels shovel - show show showed show - shower shower showers shower - showest showest showing show - shown shown shows show - shreds shred shrew shrew - shrewd shrewd shrewdly shrewdli - shrewdness shrewd shrewish shrewish - shrewishly shrewishli shrewishness shrewish - shrews shrew shrewsbury shrewsburi - shriek shriek shrieking shriek - shrieks shriek shrieve shriev - shrift shrift shrill shrill - shriller shriller shrills shrill - shrilly shrilli shrimp shrimp - shrine shrine shrink shrink - shrinking shrink shrinks shrink - shriv shriv shrive shrive - shriver shriver shrives shrive - shriving shrive shroud shroud - shrouded shroud shrouding shroud - shrouds shroud shrove shrove - shrow shrow shrows shrow - shrub shrub shrubs shrub - shrug shrug shrugs shrug - shrunk shrunk shudd shudd - shudders shudder shuffl shuffl - shuffle shuffl shuffled shuffl - shuffling shuffl shun shun - shunless shunless shunn shunn - shunned shun shunning shun - shuns shun shut shut - shuts shut shuttle shuttl - shy shy shylock shylock - si si sibyl sibyl - sibylla sibylla sibyls sibyl - sicil sicil sicilia sicilia - sicilian sicilian sicilius siciliu - sicils sicil sicily sicili - sicinius siciniu sick sick - sicken sicken sickens sicken - sicker sicker sickle sickl - sicklemen sicklemen sicklied sickli - sickliness sickli sickly sickli - sickness sick sicles sicl - sicyon sicyon side side - sided side sides side - siege sieg sieges sieg - sienna sienna sies si - sieve siev sift sift - sifted sift sigeia sigeia - sigh sigh sighed sigh - sighing sigh sighs sigh - sight sight sighted sight - sightless sightless sightly sightli - sights sight sign sign - signal signal signet signet - signieur signieur significant signific - significants signific signified signifi - signifies signifi signify signifi - signifying signifi signior signior - signiories signiori signiors signior - signiory signiori signor signor - signories signori signs sign - signum signum silenc silenc - silence silenc silenced silenc - silencing silenc silent silent - silently silent silius siliu - silk silk silken silken - silkman silkman silks silk - silliest silliest silliness silli - silling sill silly silli - silva silva silver silver - silvered silver silverly silverli - silvia silvia silvius silviu - sima sima simile simil - similes simil simois simoi - simon simon simony simoni - simp simp simpcox simpcox - simple simpl simpleness simpl - simpler simpler simples simpl - simplicity simplic simply simpli - simular simular simulation simul - sin sin since sinc - sincere sincer sincerely sincer - sincerity sincer sinel sinel - sinew sinew sinewed sinew - sinews sinew sinewy sinewi - sinful sin sinfully sinfulli - sing sing singe sing - singeing sing singer singer - singes sing singeth singeth - singing sing single singl - singled singl singleness singl - singly singli sings sing - singular singular singulariter singularit - singularities singular singularity singular - singuled singul sinister sinist - sink sink sinking sink - sinks sink sinn sinn - sinner sinner sinners sinner - sinning sin sinon sinon - sins sin sip sip - sipping sip sir sir - sire sire siren siren - sirrah sirrah sirs sir - sist sist sister sister - sisterhood sisterhood sisterly sisterli - sisters sister sit sit - sith sith sithence sithenc - sits sit sitting sit - situate situat situation situat - situations situat siward siward - six six sixpence sixpenc - sixpences sixpenc sixpenny sixpenni - sixteen sixteen sixth sixth - sixty sixti siz siz - size size sizes size - sizzle sizzl skains skain - skamble skambl skein skein - skelter skelter skies ski - skilful skil skilfully skilfulli - skill skill skilless skilless - skillet skillet skillful skill - skills skill skim skim - skimble skimbl skin skin - skinker skinker skinny skinni - skins skin skip skip - skipp skipp skipper skipper - skipping skip skirmish skirmish - skirmishes skirmish skirr skirr - skirted skirt skirts skirt - skittish skittish skulking skulk - skull skull skulls skull - sky sky skyey skyei - skyish skyish slab slab - slack slack slackly slackli - slackness slack slain slain - slake slake sland sland - slander slander slandered slander - slanderer slander slanderers slander - slandering slander slanderous slander - slanders slander slash slash - slaught slaught slaughter slaughter - slaughtered slaughter slaughterer slaughter - slaughterman slaughterman slaughtermen slaughtermen - slaughterous slaughter slaughters slaughter - slave slave slaver slaver - slavery slaveri slaves slave - slavish slavish slay slai - slayeth slayeth slaying slai - slays slai sleave sleav - sledded sled sleek sleek - sleekly sleekli sleep sleep - sleeper sleeper sleepers sleeper - sleepest sleepest sleeping sleep - sleeps sleep sleepy sleepi - sleeve sleev sleeves sleev - sleid sleid sleided sleid - sleight sleight sleights sleight - slender slender slenderer slender - slenderly slenderli slept slept - slew slew slewest slewest - slice slice slid slid - slide slide slides slide - sliding slide slight slight - slighted slight slightest slightest - slightly slightli slightness slight - slights slight slily slili - slime slime slimy slimi - slings sling slink slink - slip slip slipp slipp - slipper slipper slippers slipper - slippery slipperi slips slip - slish slish slit slit - sliver sliver slobb slobb - slomber slomber slop slop - slope slope slops slop - sloth sloth slothful sloth - slough slough slovenly slovenli - slovenry slovenri slow slow - slower slower slowly slowli - slowness slow slubber slubber - slug slug sluggard sluggard - sluggardiz sluggardiz sluggish sluggish - sluic sluic slumb slumb - slumber slumber slumbers slumber - slumbery slumberi slunk slunk - slut slut sluts slut - sluttery slutteri sluttish sluttish - sluttishness sluttish sly sly - slys sly smack smack - smacking smack smacks smack - small small smaller smaller - smallest smallest smallness small - smalus smalu smart smart - smarting smart smartly smartli - smatch smatch smatter smatter - smear smear smell smell - smelling smell smells smell - smelt smelt smil smil - smile smile smiled smile - smiles smile smilest smilest - smilets smilet smiling smile - smilingly smilingli smirch smirch - smirched smirch smit smit - smite smite smites smite - smith smith smithfield smithfield - smock smock smocks smock - smok smok smoke smoke - smoked smoke smokes smoke - smoking smoke smoky smoki - smooth smooth smoothed smooth - smoothing smooth smoothly smoothli - smoothness smooth smooths smooth - smote smote smoth smoth - smother smother smothered smother - smothering smother smug smug - smulkin smulkin smutch smutch - snaffle snaffl snail snail - snails snail snake snake - snakes snake snaky snaki - snap snap snapp snapp - snapper snapper snar snar - snare snare snares snare - snarl snarl snarleth snarleth - snarling snarl snatch snatch - snatchers snatcher snatches snatch - snatching snatch sneak sneak - sneaking sneak sneap sneap - sneaping sneap sneck sneck - snip snip snipe snipe - snipt snipt snore snore - snores snore snoring snore - snorting snort snout snout - snow snow snowballs snowbal - snowed snow snowy snowi - snuff snuff snuffs snuff - snug snug so so - soak soak soaking soak - soaks soak soar soar - soaring soar soars soar - sob sob sobbing sob - sober sober soberly soberli - sobriety sobrieti sobs sob - sociable sociabl societies societi - society societi socks sock - socrates socrat sod sod - sodden sodden soe soe - soever soever soft soft - soften soften softens soften - softer softer softest softest - softly softli softness soft - soil soil soiled soil - soilure soilur soit soit - sojourn sojourn sol sol - sola sola solace solac - solanio solanio sold sold - soldat soldat solder solder - soldest soldest soldier soldier - soldiers soldier soldiership soldiership - sole sole solely sole - solem solem solemn solemn - solemness solem solemnities solemn - solemnity solemn solemniz solemniz - solemnize solemn solemnized solemn - solemnly solemnli soles sole - solicit solicit solicitation solicit - solicited solicit soliciting solicit - solicitings solicit solicitor solicitor - solicits solicit solid solid - solidares solidar solidity solid - solinus solinu solitary solitari - solomon solomon solon solon - solum solum solus solu - solyman solyman some some - somebody somebodi someone someon - somerset somerset somerville somervil - something someth sometime sometim - sometimes sometim somever somev - somewhat somewhat somewhere somewher - somewhither somewhith somme somm - son son sonance sonanc - song song songs song - sonnet sonnet sonneting sonnet - sonnets sonnet sons son - sont sont sonties sonti - soon soon sooner sooner - soonest soonest sooth sooth - soothe sooth soothers soother - soothing sooth soothsay soothsai - soothsayer soothsay sooty sooti - sop sop sophister sophist - sophisticated sophist sophy sophi - sops sop sorcerer sorcer - sorcerers sorcer sorceress sorceress - sorceries sorceri sorcery sorceri - sore sore sorel sorel - sorely sore sorer sorer - sores sore sorrier sorrier - sorriest sorriest sorrow sorrow - sorrowed sorrow sorrowest sorrowest - sorrowful sorrow sorrowing sorrow - sorrows sorrow sorry sorri - sort sort sortance sortanc - sorted sort sorting sort - sorts sort sossius sossiu - sot sot soto soto - sots sot sottish sottish - soud soud sought sought - soul soul sould sould - soulless soulless souls soul - sound sound sounded sound - sounder sounder soundest soundest - sounding sound soundless soundless - soundly soundli soundness sound - soundpost soundpost sounds sound - sour sour source sourc - sources sourc sourest sourest - sourly sourli sours sour - sous sou souse sous - south south southam southam - southampton southampton southerly southerli - southern southern southward southward - southwark southwark southwell southwel - souviendrai souviendrai sov sov - sovereign sovereign sovereignest sovereignest - sovereignly sovereignli sovereignty sovereignti - sovereignvours sovereignvour sow sow - sowing sow sowl sowl - sowter sowter space space - spaces space spacious spaciou - spade spade spades spade - spain spain spak spak - spake spake spakest spakest - span span spangle spangl - spangled spangl spaniard spaniard - spaniel spaniel spaniels spaniel - spanish spanish spann spann - spans span spar spar - spare spare spares spare - sparing spare sparingly sparingli - spark spark sparkle sparkl - sparkles sparkl sparkling sparkl - sparks spark sparrow sparrow - sparrows sparrow sparta sparta - spartan spartan spavin spavin - spavins spavin spawn spawn - speak speak speaker speaker - speakers speaker speakest speakest - speaketh speaketh speaking speak - speaks speak spear spear - speargrass speargrass spears spear - special special specialities special - specially special specialties specialti - specialty specialti specify specifi - speciously specious spectacle spectacl - spectacled spectacl spectacles spectacl - spectators spectat spectatorship spectatorship - speculation specul speculations specul - speculative specul sped sped - speech speech speeches speech - speechless speechless speed speed - speeded speed speedier speedier - speediest speediest speedily speedili - speediness speedi speeding speed - speeds speed speedy speedi - speens speen spell spell - spelling spell spells spell - spelt spelt spencer spencer - spend spend spendest spendest - spending spend spends spend - spendthrift spendthrift spent spent - sperato sperato sperm sperm - spero spero sperr sperr - spher spher sphere sphere - sphered sphere spheres sphere - spherical spheric sphery spheri - sphinx sphinx spice spice - spiced spice spicery spiceri - spices spice spider spider - spiders spider spied spi - spies spi spieth spieth - spightfully spightfulli spigot spigot - spill spill spilling spill - spills spill spilt spilt - spilth spilth spin spin - spinii spinii spinners spinner - spinster spinster spinsters spinster - spire spire spirit spirit - spirited spirit spiritless spiritless - spirits spirit spiritual spiritu - spiritualty spiritualti spirt spirt - spit spit spital spital - spite spite spited spite - spiteful spite spites spite - spits spit spitted spit - spitting spit splay splai - spleen spleen spleenful spleen - spleens spleen spleeny spleeni - splendour splendour splenitive splenit - splinter splinter splinters splinter - split split splits split - splitted split splitting split - spoil spoil spoils spoil - spok spok spoke spoke - spoken spoken spokes spoke - spokesman spokesman sponge spong - spongy spongi spoon spoon - spoons spoon sport sport - sportful sport sporting sport - sportive sportiv sports sport - spot spot spotless spotless - spots spot spotted spot - spousal spousal spouse spous - spout spout spouting spout - spouts spout sprag sprag - sprang sprang sprat sprat - sprawl sprawl spray sprai - sprays sprai spread spread - spreading spread spreads spread - sprighted spright sprightful spright - sprightly sprightli sprigs sprig - spring spring springe spring - springes spring springeth springeth - springhalt springhalt springing spring - springs spring springtime springtim - sprinkle sprinkl sprinkles sprinkl - sprite sprite sprited sprite - spritely sprite sprites sprite - spriting sprite sprout sprout - spruce spruce sprung sprung - spun spun spur spur - spurio spurio spurn spurn - spurns spurn spurr spurr - spurrer spurrer spurring spur - spurs spur spy spy - spying spy squabble squabbl - squadron squadron squadrons squadron - squand squand squar squar - square squar squarer squarer - squares squar squash squash - squeak squeak squeaking squeak - squeal squeal squealing squeal - squeezes squeez squeezing squeez - squele squel squier squier - squints squint squiny squini - squire squir squires squir - squirrel squirrel st st - stab stab stabb stabb - stabbed stab stabbing stab - stable stabl stableness stabl - stables stabl stablish stablish - stablishment stablish stabs stab - stacks stack staff staff - stafford stafford staffords stafford - staffordshire staffordshir stag stag - stage stage stages stage - stagger stagger staggering stagger - staggers stagger stags stag - staid staid staider staider - stain stain stained stain - staines stain staineth staineth - staining stain stainless stainless - stains stain stair stair - stairs stair stake stake - stakes stake stale stale - staled stale stalk stalk - stalking stalk stalks stalk - stall stall stalling stall - stalls stall stamford stamford - stammer stammer stamp stamp - stamped stamp stamps stamp - stanch stanch stanchless stanchless - stand stand standard standard - standards standard stander stander - standers stander standest standest - standeth standeth standing stand - stands stand staniel staniel - stanley stanlei stanze stanz - stanzo stanzo stanzos stanzo - staple stapl staples stapl - star star stare stare - stared stare stares stare - staring stare starings stare - stark stark starkly starkli - starlight starlight starling starl - starr starr starry starri - stars star start start - started start starting start - startingly startingli startle startl - startles startl starts start - starv starv starve starv - starved starv starvelackey starvelackei - starveling starvel starveth starveth - starving starv state state - statelier stateli stately state - states state statesman statesman - statesmen statesmen statilius statiliu - station station statist statist - statists statist statue statu - statues statu stature statur - statures statur statute statut - statutes statut stave stave - staves stave stay stai - stayed stai stayest stayest - staying stai stays stai - stead stead steaded stead - steadfast steadfast steadier steadier - steads stead steal steal - stealer stealer stealers stealer - stealing steal steals steal - stealth stealth stealthy stealthi - steed steed steeds steed - steel steel steeled steel - steely steeli steep steep - steeped steep steeple steepl - steeples steepl steeps steep - steepy steepi steer steer - steerage steerag steering steer - steers steer stelled stell - stem stem stemming stem - stench stench step step - stepdame stepdam stephano stephano - stephen stephen stepmothers stepmoth - stepp stepp stepping step - steps step sterile steril - sterility steril sterling sterl - stern stern sternage sternag - sterner sterner sternest sternest - sternness stern steterat steterat - stew stew steward steward - stewards steward stewardship stewardship - stewed stew stews stew - stick stick sticking stick - stickler stickler sticks stick - stiff stiff stiffen stiffen - stiffly stiffli stifle stifl - stifled stifl stifles stifl - stigmatic stigmat stigmatical stigmat - stile stile still still - stiller stiller stillest stillest - stillness still stilly stilli - sting sting stinging sting - stingless stingless stings sting - stink stink stinking stink - stinkingly stinkingli stinks stink - stint stint stinted stint - stints stint stir stir - stirr stirr stirred stir - stirrer stirrer stirrers stirrer - stirreth stirreth stirring stir - stirrup stirrup stirrups stirrup - stirs stir stitchery stitcheri - stitches stitch stithied stithi - stithy stithi stoccadoes stoccado - stoccata stoccata stock stock - stockfish stockfish stocking stock - stockings stock stockish stockish - stocks stock stog stog - stogs stog stoics stoic - stokesly stokesli stol stol - stole stole stolen stolen - stolest stolest stomach stomach - stomachers stomach stomaching stomach - stomachs stomach ston ston - stone stone stonecutter stonecutt - stones stone stonish stonish - stony stoni stood stood - stool stool stools stool - stoop stoop stooping stoop - stoops stoop stop stop - stope stope stopp stopp - stopped stop stopping stop - stops stop stor stor - store store storehouse storehous - storehouses storehous stores store - stories stori storm storm - stormed storm storming storm - storms storm stormy stormi - story stori stoup stoup - stoups stoup stout stout - stouter stouter stoutly stoutli - stoutness stout stover stover - stow stow stowage stowag - stowed stow strachy strachi - stragglers straggler straggling straggl - straight straight straightest straightest - straightway straightwai strain strain - strained strain straining strain - strains strain strait strait - straited strait straiter straiter - straitly straitli straitness strait - straits strait strand strand - strange strang strangely strang - strangeness strang stranger stranger - strangers stranger strangest strangest - strangle strangl strangled strangl - strangler strangler strangles strangl - strangling strangl strappado strappado - straps strap stratagem stratagem - stratagems stratagem stratford stratford - strato strato straw straw - strawberries strawberri strawberry strawberri - straws straw strawy strawi - stray strai straying strai - strays strai streak streak - streaks streak stream stream - streamers streamer streaming stream - streams stream streching strech - street street streets street - strength strength strengthen strengthen - strengthened strengthen strengthless strengthless - strengths strength stretch stretch - stretched stretch stretches stretch - stretching stretch strew strew - strewing strew strewings strew - strewments strewment stricken stricken - strict strict stricter stricter - strictest strictest strictly strictli - stricture strictur stride stride - strides stride striding stride - strife strife strifes strife - strik strik strike strike - strikers striker strikes strike - strikest strikest striking strike - string string stringless stringless - strings string strip strip - stripes stripe stripling stripl - striplings stripl stripp stripp - stripping strip striv striv - strive strive strives strive - striving strive strok strok - stroke stroke strokes stroke - strond strond stronds strond - strong strong stronger stronger - strongest strongest strongly strongli - strooke strook strossers strosser - strove strove strown strown - stroy stroi struck struck - strucken strucken struggle struggl - struggles struggl struggling struggl - strumpet strumpet strumpeted strumpet - strumpets strumpet strung strung - strut strut struts strut - strutted strut strutting strut - stubble stubbl stubborn stubborn - stubbornest stubbornest stubbornly stubbornli - stubbornness stubborn stuck stuck - studded stud student student - students student studied studi - studies studi studious studiou - studiously studious studs stud - study studi studying studi - stuff stuff stuffing stuf - stuffs stuff stumble stumbl - stumbled stumbl stumblest stumblest - stumbling stumbl stump stump - stumps stump stung stung - stupefy stupefi stupid stupid - stupified stupifi stuprum stuprum - sturdy sturdi sty sty - styga styga stygian stygian - styl styl style style - styx styx su su - sub sub subcontracted subcontract - subdu subdu subdue subdu - subdued subdu subduements subduement - subdues subdu subduing subdu - subject subject subjected subject - subjection subject subjects subject - submerg submerg submission submiss - submissive submiss submit submit - submits submit submitting submit - suborn suborn subornation suborn - suborned suborn subscrib subscrib - subscribe subscrib subscribed subscrib - subscribes subscrib subscription subscript - subsequent subsequ subsidies subsidi - subsidy subsidi subsist subsist - subsisting subsist substance substanc - substances substanc substantial substanti - substitute substitut substituted substitut - substitutes substitut substitution substitut - subtile subtil subtilly subtilli - subtle subtl subtleties subtleti - subtlety subtleti subtly subtli - subtractors subtractor suburbs suburb - subversion subvers subverts subvert - succedant succed succeed succe - succeeded succeed succeeders succeed - succeeding succeed succeeds succe - success success successantly successantli - successes success successful success - successfully successfulli succession success - successive success successively success - successor successor successors successor - succour succour succours succour - such such suck suck - sucker sucker suckers sucker - sucking suck suckle suckl - sucks suck sudden sudden - suddenly suddenli sue sue - sued su suerly suerli - sues sue sueth sueth - suff suff suffer suffer - sufferance suffer sufferances suffer - suffered suffer suffering suffer - suffers suffer suffic suffic - suffice suffic sufficed suffic - suffices suffic sufficeth sufficeth - sufficiency suffici sufficient suffici - sufficiently suffici sufficing suffic - sufficit sufficit suffigance suffig - suffocate suffoc suffocating suffoc - suffocation suffoc suffolk suffolk - suffrage suffrag suffrages suffrag - sug sug sugar sugar - sugarsop sugarsop suggest suggest - suggested suggest suggesting suggest - suggestion suggest suggestions suggest - suggests suggest suis sui - suit suit suitable suitabl - suited suit suiting suit - suitor suitor suitors suitor - suits suit suivez suivez - sullen sullen sullens sullen - sullied sulli sullies sulli - sully sulli sulph sulph - sulpherous sulpher sulphur sulphur - sulphurous sulphur sultan sultan - sultry sultri sum sum - sumless sumless summ summ - summa summa summary summari - summer summer summers summer - summit summit summon summon - summoners summon summons summon - sumpter sumpter sumptuous sumptuou - sumptuously sumptuous sums sum - sun sun sunbeams sunbeam - sunburning sunburn sunburnt sunburnt - sund sund sunday sundai - sundays sundai sunder sunder - sunders sunder sundry sundri - sung sung sunk sunk - sunken sunken sunny sunni - sunrising sunris suns sun - sunset sunset sunshine sunshin - sup sup super super - superficial superfici superficially superfici - superfluity superflu superfluous superflu - superfluously superflu superflux superflux - superior superior supernal supern - supernatural supernatur superpraise superprais - superscript superscript superscription superscript - superserviceable superservic superstition superstit - superstitious superstiti superstitiously superstiti - supersubtle supersubtl supervise supervis - supervisor supervisor supp supp - supper supper suppers supper - suppertime suppertim supping sup - supplant supplant supple suppl - suppler suppler suppliance supplianc - suppliant suppliant suppliants suppliant - supplicant supplic supplication supplic - supplications supplic supplie suppli - supplied suppli supplies suppli - suppliest suppliest supply suppli - supplyant supplyant supplying suppli - supplyment supplyment support support - supportable support supportance support - supported support supporter support - supporters support supporting support - supportor supportor suppos suppo - supposal suppos suppose suppos - supposed suppos supposes suppos - supposest supposest supposing suppos - supposition supposit suppress suppress - suppressed suppress suppresseth suppresseth - supremacy supremaci supreme suprem - sups sup sur sur - surance suranc surcease surceas - surd surd sure sure - surecard surecard surely sure - surer surer surest surest - sureties sureti surety sureti - surfeit surfeit surfeited surfeit - surfeiter surfeit surfeiting surfeit - surfeits surfeit surge surg - surgeon surgeon surgeons surgeon - surgere surger surgery surgeri - surges surg surly surli - surmis surmi surmise surmis - surmised surmis surmises surmis - surmount surmount surmounted surmount - surmounts surmount surnam surnam - surname surnam surnamed surnam - surpasseth surpasseth surpassing surpass - surplice surplic surplus surplu - surpris surpri surprise surpris - surprised surpris surrender surrend - surrey surrei surreys surrei - survey survei surveyest surveyest - surveying survei surveyor surveyor - surveyors surveyor surveys survei - survive surviv survives surviv - survivor survivor susan susan - suspect suspect suspected suspect - suspecting suspect suspects suspect - suspend suspend suspense suspens - suspicion suspicion suspicions suspicion - suspicious suspici suspiration suspir - suspire suspir sust sust - sustain sustain sustaining sustain - sutler sutler sutton sutton - suum suum swabber swabber - swaddling swaddl swag swag - swagg swagg swagger swagger - swaggerer swagger swaggerers swagger - swaggering swagger swain swain - swains swain swallow swallow - swallowed swallow swallowing swallow - swallows swallow swam swam - swan swan swans swan - sward sward sware sware - swarm swarm swarming swarm - swart swart swarth swarth - swarths swarth swarthy swarthi - swashers swasher swashing swash - swath swath swathing swath - swathling swathl sway swai - swaying swai sways swai - swear swear swearer swearer - swearers swearer swearest swearest - swearing swear swearings swear - swears swear sweat sweat - sweaten sweaten sweating sweat - sweats sweat sweaty sweati - sweep sweep sweepers sweeper - sweeps sweep sweet sweet - sweeten sweeten sweetens sweeten - sweeter sweeter sweetest sweetest - sweetheart sweetheart sweeting sweet - sweetly sweetli sweetmeats sweetmeat - sweetness sweet sweets sweet - swell swell swelling swell - swellings swell swells swell - swelter swelter sweno sweno - swept swept swerve swerv - swerver swerver swerving swerv - swift swift swifter swifter - swiftest swiftest swiftly swiftli - swiftness swift swill swill - swills swill swim swim - swimmer swimmer swimmers swimmer - swimming swim swims swim - swine swine swineherds swineherd - swing swing swinge swing - swinish swinish swinstead swinstead - switches switch swits swit - switzers switzer swol swol - swoll swoll swoln swoln - swoon swoon swooned swoon - swooning swoon swoons swoon - swoop swoop swoopstake swoopstak - swor swor sword sword - sworder sworder swords sword - swore swore sworn sworn - swounded swound swounds swound - swum swum swung swung - sy sy sycamore sycamor - sycorax sycorax sylla sylla - syllable syllabl syllables syllabl - syllogism syllog symbols symbol - sympathise sympathis sympathiz sympathiz - sympathize sympath sympathized sympath - sympathy sympathi synagogue synagogu - synod synod synods synod - syracuse syracus syracusian syracusian - syracusians syracusian syria syria - syrups syrup t t - ta ta taber taber - table tabl tabled tabl - tables tabl tablet tablet - tabor tabor taborer tabor - tabors tabor tabourines tabourin - taciturnity taciturn tack tack - tackle tackl tackled tackl - tackles tackl tackling tackl - tacklings tackl taddle taddl - tadpole tadpol taffeta taffeta - taffety taffeti tag tag - tagrag tagrag tah tah - tail tail tailor tailor - tailors tailor tails tail - taint taint tainted taint - tainting taint taints taint - tainture taintur tak tak - take take taken taken - taker taker takes take - takest takest taketh taketh - taking take tal tal - talbot talbot talbotites talbotit - talbots talbot tale tale - talent talent talents talent - taleporter taleport tales tale - talk talk talked talk - talker talker talkers talker - talkest talkest talking talk - talks talk tall tall - taller taller tallest tallest - tallies talli tallow tallow - tally talli talons talon - tam tam tambourines tambourin - tame tame tamed tame - tamely tame tameness tame - tamer tamer tames tame - taming tame tamora tamora - tamworth tamworth tan tan - tang tang tangle tangl - tangled tangl tank tank - tanlings tanl tann tann - tanned tan tanner tanner - tanquam tanquam tanta tanta - tantaene tantaen tap tap - tape tape taper taper - tapers taper tapestries tapestri - tapestry tapestri taphouse taphous - tapp tapp tapster tapster - tapsters tapster tar tar - tardied tardi tardily tardili - tardiness tardi tardy tardi - tarentum tarentum targe targ - targes targ target target - targets target tarpeian tarpeian - tarquin tarquin tarquins tarquin - tarr tarr tarre tarr - tarriance tarrianc tarried tarri - tarries tarri tarry tarri - tarrying tarri tart tart - tartar tartar tartars tartar - tartly tartli tartness tart - task task tasker tasker - tasking task tasks task - tassel tassel taste tast - tasted tast tastes tast - tasting tast tatt tatt - tatter tatter tattered tatter - tatters tatter tattle tattl - tattling tattl tattlings tattl - taught taught taunt taunt - taunted taunt taunting taunt - tauntingly tauntingli taunts taunt - taurus tauru tavern tavern - taverns tavern tavy tavi - tawdry tawdri tawny tawni - tax tax taxation taxat - taxations taxat taxes tax - taxing tax tc tc - te te teach teach - teacher teacher teachers teacher - teaches teach teachest teachest - teacheth teacheth teaching teach - team team tear tear - tearful tear tearing tear - tears tear tearsheet tearsheet - teat teat tedious tediou - tediously tedious tediousness tedious - teem teem teeming teem - teems teem teen teen - teeth teeth teipsum teipsum - telamon telamon telamonius telamoniu - tell tell teller teller - telling tell tells tell - tellus tellu temp temp - temper temper temperality temper - temperance temper temperate temper - temperately temper tempers temper - tempest tempest tempests tempest - tempestuous tempestu temple templ - temples templ temporal tempor - temporary temporari temporiz temporiz - temporize tempor temporizer tempor - temps temp tempt tempt - temptation temptat temptations temptat - tempted tempt tempter tempter - tempters tempter tempteth tempteth - tempting tempt tempts tempt - ten ten tenable tenabl - tenant tenant tenantius tenantiu - tenantless tenantless tenants tenant - tench tench tend tend - tendance tendanc tended tend - tender tender tendered tender - tenderly tenderli tenderness tender - tenders tender tending tend - tends tend tenedos tenedo - tenement tenement tenements tenement - tenfold tenfold tennis tenni - tenour tenour tenours tenour - tens ten tent tent - tented tent tenth tenth - tenths tenth tents tent - tenure tenur tenures tenur - tercel tercel tereus tereu - term term termagant termag - termed term terminations termin - termless termless terms term - terra terra terrace terrac - terram terram terras terra - terre terr terrene terren - terrestrial terrestri terrible terribl - terribly terribl territories territori - territory territori terror terror - terrors terror tertian tertian - tertio tertio test test - testament testament tested test - tester tester testern testern - testify testifi testimonied testimoni - testimonies testimoni testimony testimoni - testiness testi testril testril - testy testi tetchy tetchi - tether tether tetter tetter - tevil tevil tewksbury tewksburi - text text tgv tgv - th th thaes thae - thames thame than than - thane thane thanes thane - thank thank thanked thank - thankful thank thankfully thankfulli - thankfulness thank thanking thank - thankings thank thankless thankless - thanks thank thanksgiving thanksgiv - thasos thaso that that - thatch thatch thaw thaw - thawing thaw thaws thaw - the the theatre theatr - theban theban thebes thebe - thee thee theft theft - thefts theft thein thein - their their theirs their - theise theis them them - theme theme themes theme - themselves themselv then then - thence thenc thenceforth thenceforth - theoric theoric there there - thereabout thereabout thereabouts thereabout - thereafter thereaft thereat thereat - thereby therebi therefore therefor - therein therein thereof thereof - thereon thereon thereto thereto - thereunto thereunto thereupon thereupon - therewith therewith therewithal therewith - thersites thersit these these - theseus theseu thessalian thessalian - thessaly thessali thetis theti - thews thew they thei - thick thick thicken thicken - thickens thicken thicker thicker - thickest thickest thicket thicket - thickskin thickskin thief thief - thievery thieveri thieves thiev - thievish thievish thigh thigh - thighs thigh thimble thimbl - thimbles thimbl thin thin - thine thine thing thing - things thing think think - thinkest thinkest thinking think - thinkings think thinks think - thinkst thinkst thinly thinli - third third thirdly thirdli - thirds third thirst thirst - thirsting thirst thirsts thirst - thirsty thirsti thirteen thirteen - thirties thirti thirtieth thirtieth - thirty thirti this thi - thisby thisbi thisne thisn - thistle thistl thistles thistl - thither thither thitherward thitherward - thoas thoa thomas thoma - thorn thorn thorns thorn - thorny thorni thorough thorough - thoroughly thoroughli those those - thou thou though though - thought thought thoughtful thought - thoughts thought thousand thousand - thousands thousand thracian thracian - thraldom thraldom thrall thrall - thralled thrall thralls thrall - thrash thrash thrasonical thrason - thread thread threadbare threadbar - threaden threaden threading thread - threat threat threaten threaten - threatening threaten threatens threaten - threatest threatest threats threat - three three threefold threefold - threepence threepenc threepile threepil - threes three threescore threescor - thresher thresher threshold threshold - threw threw thrice thrice - thrift thrift thriftless thriftless - thrifts thrift thrifty thrifti - thrill thrill thrilling thrill - thrills thrill thrive thrive - thrived thrive thrivers thriver - thrives thrive thriving thrive - throat throat throats throat - throbbing throb throbs throb - throca throca throe throe - throes throe thromuldo thromuldo - thron thron throne throne - throned throne thrones throne - throng throng thronging throng - throngs throng throstle throstl - throttle throttl through through - throughfare throughfar throughfares throughfar - throughly throughli throughout throughout - throw throw thrower thrower - throwest throwest throwing throw - thrown thrown throws throw - thrum thrum thrumm thrumm - thrush thrush thrust thrust - thrusteth thrusteth thrusting thrust - thrusts thrust thumb thumb - thumbs thumb thump thump - thund thund thunder thunder - thunderbolt thunderbolt thunderbolts thunderbolt - thunderer thunder thunders thunder - thunderstone thunderston thunderstroke thunderstrok - thurio thurio thursday thursdai - thus thu thwack thwack - thwart thwart thwarted thwart - thwarting thwart thwartings thwart - thy thy thyme thyme - thymus thymu thyreus thyreu - thyself thyself ti ti - tib tib tiber tiber - tiberio tiberio tibey tibei - ticed tice tick tick - tickl tickl tickle tickl - tickled tickl tickles tickl - tickling tickl ticklish ticklish - tiddle tiddl tide tide - tides tide tidings tide - tidy tidi tie tie - tied ti ties ti - tiff tiff tiger tiger - tigers tiger tight tight - tightly tightli tike tike - til til tile tile - till till tillage tillag - tilly tilli tilt tilt - tilter tilter tilth tilth - tilting tilt tilts tilt - tiltyard tiltyard tim tim - timandra timandra timber timber - time time timeless timeless - timelier timeli timely time - times time timon timon - timor timor timorous timor - timorously timor tinct tinct - tincture tinctur tinctures tinctur - tinder tinder tingling tingl - tinker tinker tinkers tinker - tinsel tinsel tiny tini - tip tip tipp tipp - tippling tippl tips tip - tipsy tipsi tiptoe tipto - tir tir tire tire - tired tire tires tire - tirest tirest tiring tire - tirra tirra tirrits tirrit - tis ti tish tish - tisick tisick tissue tissu - titan titan titania titania - tithe tith tithed tith - tithing tith titinius titiniu - title titl titled titl - titleless titleless titles titl - tittle tittl tittles tittl - titular titular titus titu - tn tn to to - toad toad toads toad - toadstool toadstool toast toast - toasted toast toasting toast - toasts toast toaze toaz - toby tobi tock tock - tod tod today todai - todpole todpol tods tod - toe toe toes toe - tofore tofor toge toge - toged toge together togeth - toil toil toiled toil - toiling toil toils toil - token token tokens token - told told toledo toledo - tolerable toler toll toll - tolling toll tom tom - tomb tomb tombe tomb - tombed tomb tombless tombless - tomboys tomboi tombs tomb - tomorrow tomorrow tomyris tomyri - ton ton tongs tong - tongu tongu tongue tongu - tongued tongu tongueless tongueless - tongues tongu tonight tonight - too too took took - tool tool tools tool - tooth tooth toothache toothach - toothpick toothpick toothpicker toothpick - top top topas topa - topful top topgallant topgal - topless topless topmast topmast - topp topp topping top - topple toppl topples toppl - tops top topsail topsail - topsy topsi torch torch - torchbearer torchbear torchbearers torchbear - torcher torcher torches torch - torchlight torchlight tore tore - torment torment tormenta tormenta - tormente torment tormented torment - tormenting torment tormentors tormentor - torments torment torn torn - torrent torrent tortive tortiv - tortoise tortois tortur tortur - torture tortur tortured tortur - torturer tortur torturers tortur - tortures tortur torturest torturest - torturing tortur toryne toryn - toss toss tossed toss - tosseth tosseth tossing toss - tot tot total total - totally total tott tott - tottered totter totters totter - tou tou touch touch - touched touch touches touch - toucheth toucheth touching touch - touchstone touchston tough tough - tougher tougher toughness tough - touraine tourain tournaments tournament - tours tour tous tou - tout tout touze touz - tow tow toward toward - towardly towardli towards toward - tower tower towering tower - towers tower town town - towns town township township - townsman townsman townsmen townsmen - towton towton toy toi - toys toi trace trace - traces trace track track - tract tract tractable tractabl - trade trade traded trade - traders trader trades trade - tradesman tradesman tradesmen tradesmen - trading trade tradition tradit - traditional tradit traduc traduc - traduced traduc traducement traduc - traffic traffic traffickers traffick - traffics traffic tragedian tragedian - tragedians tragedian tragedies tragedi - tragedy tragedi tragic tragic - tragical tragic trail trail - train train trained train - training train trains train - trait trait traitor traitor - traitorly traitorli traitorous traitor - traitorously traitor traitors traitor - traitress traitress traject traject - trammel trammel trample trampl - trampled trampl trampling trampl - tranc tranc trance tranc - tranio tranio tranquil tranquil - tranquillity tranquil transcendence transcend - transcends transcend transferred transfer - transfigur transfigur transfix transfix - transform transform transformation transform - transformations transform transformed transform - transgress transgress transgresses transgress - transgressing transgress transgression transgress - translate translat translated translat - translates translat translation translat - transmigrates transmigr transmutation transmut - transparent transpar transport transport - transportance transport transported transport - transporting transport transports transport - transpose transpos transshape transshap - trap trap trapp trapp - trappings trap traps trap - trash trash travail travail - travails travail travel travel - traveler travel traveling travel - travell travel travelled travel - traveller travel travellers travel - travellest travellest travelling travel - travels travel travers traver - traverse travers tray trai - treacherous treacher treacherously treacher - treachers treacher treachery treacheri - tread tread treading tread - treads tread treason treason - treasonable treason treasonous treason - treasons treason treasure treasur - treasurer treasur treasures treasur - treasuries treasuri treasury treasuri - treat treat treaties treati - treatise treatis treats treat - treaty treati treble trebl - trebled trebl trebles trebl - trebonius treboniu tree tree - trees tree tremble trembl - trembled trembl trembles trembl - tremblest tremblest trembling trembl - tremblingly tremblingli tremor tremor - trempling trempl trench trench - trenchant trenchant trenched trench - trencher trencher trenchering trencher - trencherman trencherman trenchers trencher - trenches trench trenching trench - trent trent tres tre - trespass trespass trespasses trespass - tressel tressel tresses tress - treys trei trial trial - trials trial trib trib - tribe tribe tribes tribe - tribulation tribul tribunal tribun - tribune tribun tribunes tribun - tributaries tributari tributary tributari - tribute tribut tributes tribut - trice trice trick trick - tricking trick trickling trickl - tricks trick tricksy tricksi - trident trident tried tri - trier trier trifle trifl - trifled trifl trifler trifler - trifles trifl trifling trifl - trigon trigon trill trill - trim trim trimly trimli - trimm trimm trimmed trim - trimming trim trims trim - trinculo trinculo trinculos trinculo - trinkets trinket trip trip - tripartite tripartit tripe tripe - triple tripl triplex triplex - tripoli tripoli tripolis tripoli - tripp tripp tripping trip - trippingly trippingli trips trip - tristful trist triton triton - triumph triumph triumphant triumphant - triumphantly triumphantli triumpher triumpher - triumphers triumpher triumphing triumph - triumphs triumph triumvir triumvir - triumvirate triumvir triumvirs triumvir - triumviry triumviri trivial trivial - troat troat trod trod - trodden trodden troiant troiant - troien troien troilus troilu - troiluses troilus trojan trojan - trojans trojan troll troll - tromperies tromperi trompet trompet - troop troop trooping troop - troops troop trop trop - trophies trophi trophy trophi - tropically tropic trot trot - troth troth trothed troth - troths troth trots trot - trotting trot trouble troubl - troubled troubl troubler troubler - troubles troubl troublesome troublesom - troublest troublest troublous troublou - trough trough trout trout - trouts trout trovato trovato - trow trow trowel trowel - trowest trowest troy troi - troyan troyan troyans troyan - truant truant truce truce - truckle truckl trudge trudg - true true trueborn trueborn - truepenny truepenni truer truer - truest truest truie truie - trull trull trulls trull - truly truli trump trump - trumpery trumperi trumpet trumpet - trumpeter trumpet trumpeters trumpet - trumpets trumpet truncheon truncheon - truncheoners truncheon trundle trundl - trunk trunk trunks trunk - trust trust trusted trust - truster truster trusters truster - trusting trust trusts trust - trusty trusti truth truth - truths truth try try - ts ts tu tu - tuae tuae tub tub - tubal tubal tubs tub - tuck tuck tucket tucket - tuesday tuesdai tuft tuft - tufts tuft tug tug - tugg tugg tugging tug - tuition tuition tullus tullu - tully tulli tumble tumbl - tumbled tumbl tumbler tumbler - tumbling tumbl tumult tumult - tumultuous tumultu tun tun - tune tune tuneable tuneabl - tuned tune tuners tuner - tunes tune tunis tuni - tuns tun tupping tup - turban turban turbans turban - turbulence turbul turbulent turbul - turd turd turf turf - turfy turfi turk turk - turkey turkei turkeys turkei - turkish turkish turks turk - turlygod turlygod turmoil turmoil - turmoiled turmoil turn turn - turnbull turnbul turncoat turncoat - turncoats turncoat turned turn - turneth turneth turning turn - turnips turnip turns turn - turph turph turpitude turpitud - turquoise turquois turret turret - turrets turret turtle turtl - turtles turtl turvy turvi - tuscan tuscan tush tush - tut tut tutor tutor - tutored tutor tutors tutor - tutto tutto twain twain - twang twang twangling twangl - twas twa tway twai - tweaks tweak tween tween - twelfth twelfth twelve twelv - twelvemonth twelvemonth twentieth twentieth - twenty twenti twere twere - twice twice twig twig - twiggen twiggen twigs twig - twilight twilight twill twill - twilled twill twin twin - twine twine twink twink - twinkle twinkl twinkled twinkl - twinkling twinkl twinn twinn - twins twin twire twire - twist twist twisted twist - twit twit twits twit - twitting twit twixt twixt - two two twofold twofold - twopence twopenc twopences twopenc - twos two twould twould - tyb tyb tybalt tybalt - tybalts tybalt tyburn tyburn - tying ty tyke tyke - tymbria tymbria type type - types type typhon typhon - tyrannical tyrann tyrannically tyrann - tyrannize tyrann tyrannous tyrann - tyranny tyranni tyrant tyrant - tyrants tyrant tyrian tyrian - tyrrel tyrrel u u - ubique ubiqu udders udder - udge udg uds ud - uglier uglier ugliest ugliest - ugly ugli ulcer ulcer - ulcerous ulcer ulysses ulyss - um um umber umber - umbra umbra umbrage umbrag - umfrevile umfrevil umpire umpir - umpires umpir un un - unable unabl unaccommodated unaccommod - unaccompanied unaccompani unaccustom unaccustom - unaching unach unacquainted unacquaint - unactive unact unadvis unadvi - unadvised unadvis unadvisedly unadvisedli - unagreeable unagre unanel unanel - unanswer unansw unappeas unappea - unapproved unapprov unapt unapt - unaptness unapt unarm unarm - unarmed unarm unarms unarm - unassail unassail unassailable unassail - unattainted unattaint unattempted unattempt - unattended unattend unauspicious unauspici - unauthorized unauthor unavoided unavoid - unawares unawar unback unback - unbak unbak unbanded unband - unbar unbar unbarb unbarb - unbashful unbash unbated unbat - unbatter unbatt unbecoming unbecom - unbefitting unbefit unbegot unbegot - unbegotten unbegotten unbelieved unbeliev - unbend unbend unbent unbent - unbewail unbewail unbid unbid - unbidden unbidden unbind unbind - unbinds unbind unbitted unbit - unbless unbless unblest unblest - unbloodied unbloodi unblown unblown - unbodied unbodi unbolt unbolt - unbolted unbolt unbonneted unbonnet - unbookish unbookish unborn unborn - unbosom unbosom unbound unbound - unbounded unbound unbow unbow - unbowed unbow unbrac unbrac - unbraced unbrac unbraided unbraid - unbreathed unbreath unbred unbr - unbreech unbreech unbridled unbridl - unbroke unbrok unbruis unbrui - unbruised unbruis unbuckle unbuckl - unbuckles unbuckl unbuckling unbuckl - unbuild unbuild unburden unburden - unburdens unburden unburied unburi - unburnt unburnt unburthen unburthen - unbutton unbutton unbuttoning unbutton - uncapable uncap uncape uncap - uncase uncas uncasing uncas - uncaught uncaught uncertain uncertain - uncertainty uncertainti unchain unchain - unchanging unchang uncharge uncharg - uncharged uncharg uncharitably uncharit - unchary unchari unchaste unchast - uncheck uncheck unchilded unchild - uncivil uncivil unclaim unclaim - unclasp unclasp uncle uncl - unclean unclean uncleanliness uncleanli - uncleanly uncleanli uncleanness unclean - uncles uncl unclew unclew - unclog unclog uncoined uncoin - uncolted uncolt uncomeliness uncomeli - uncomfortable uncomfort uncompassionate uncompassion - uncomprehensive uncomprehens unconfinable unconfin - unconfirm unconfirm unconfirmed unconfirm - unconquer unconqu unconquered unconqu - unconsidered unconsid unconstant unconst - unconstrain unconstrain unconstrained unconstrain - uncontemn uncontemn uncontroll uncontrol - uncorrected uncorrect uncounted uncount - uncouple uncoupl uncourteous uncourt - uncouth uncouth uncover uncov - uncovered uncov uncropped uncrop - uncross uncross uncrown uncrown - unction unction unctuous unctuou - uncuckolded uncuckold uncurable uncur - uncurbable uncurb uncurbed uncurb - uncurls uncurl uncurrent uncurr - uncurse uncurs undaunted undaunt - undeaf undeaf undeck undeck - undeeded undeed under under - underbearing underbear underborne underborn - undercrest undercrest underfoot underfoot - undergo undergo undergoes undergo - undergoing undergo undergone undergon - underground underground underhand underhand - underlings underl undermine undermin - underminers undermin underneath underneath - underprizing underpr underprop underprop - understand understand understandeth understandeth - understanding understand understandings understand - understands understand understood understood - underta underta undertake undertak - undertakeing undertak undertaker undertak - undertakes undertak undertaking undertak - undertakings undertak undertook undertook - undervalu undervalu undervalued undervalu - underwent underw underwrit underwrit - underwrite underwrit undescried undescri - undeserved undeserv undeserver undeserv - undeservers undeserv undeserving undeserv - undetermin undetermin undid undid - undinted undint undiscernible undiscern - undiscover undiscov undishonoured undishonour - undispos undispo undistinguishable undistinguish - undistinguished undistinguish undividable undivid - undivided undivid undivulged undivulg - undo undo undoes undo - undoing undo undone undon - undoubted undoubt undoubtedly undoubtedli - undream undream undress undress - undressed undress undrown undrown - unduteous undut undutiful unduti - une un uneared unear - unearned unearn unearthly unearthli - uneasines uneasin uneasy uneasi - uneath uneath uneducated uneduc - uneffectual uneffectu unelected unelect - unequal unequ uneven uneven - unexamin unexamin unexecuted unexecut - unexpected unexpect unexperienc unexperienc - unexperient unexperi unexpressive unexpress - unfair unfair unfaithful unfaith - unfallible unfal unfam unfam - unfashionable unfashion unfasten unfasten - unfather unfath unfathered unfath - unfed unf unfeed unfe - unfeeling unfeel unfeigned unfeign - unfeignedly unfeignedli unfellowed unfellow - unfelt unfelt unfenced unfenc - unfilial unfili unfill unfil - unfinish unfinish unfirm unfirm - unfit unfit unfitness unfit - unfix unfix unfledg unfledg - unfold unfold unfolded unfold - unfoldeth unfoldeth unfolding unfold - unfolds unfold unfool unfool - unforc unforc unforced unforc - unforfeited unforfeit unfortified unfortifi - unfortunate unfortun unfought unfought - unfrequented unfrequ unfriended unfriend - unfurnish unfurnish ungain ungain - ungalled ungal ungart ungart - ungarter ungart ungenitur ungenitur - ungentle ungentl ungentleness ungentl - ungently ungent ungird ungird - ungodly ungodli ungor ungor - ungot ungot ungotten ungotten - ungovern ungovern ungracious ungraci - ungrateful ungrat ungravely ungrav - ungrown ungrown unguarded unguard - unguem unguem unguided unguid - unhack unhack unhair unhair - unhallow unhallow unhallowed unhallow - unhand unhand unhandled unhandl - unhandsome unhandsom unhang unhang - unhappied unhappi unhappily unhappili - unhappiness unhappi unhappy unhappi - unhardened unharden unharm unharm - unhatch unhatch unheard unheard - unhearts unheart unheedful unheed - unheedfully unheedfulli unheedy unheedi - unhelpful unhelp unhidden unhidden - unholy unholi unhop unhop - unhopefullest unhopefullest unhorse unhors - unhospitable unhospit unhous unhou - unhoused unhous unhurtful unhurt - unicorn unicorn unicorns unicorn - unimproved unimprov uninhabitable uninhabit - uninhabited uninhabit unintelligent unintellig - union union unions union - unite unit united unit - unity uniti universal univers - universe univers universities univers - university univers unjointed unjoint - unjust unjust unjustice unjustic - unjustly unjustli unkennel unkennel - unkept unkept unkind unkind - unkindest unkindest unkindly unkindli - unkindness unkind unking unk - unkinglike unkinglik unkiss unkiss - unknit unknit unknowing unknow - unknown unknown unlace unlac - unlaid unlaid unlawful unlaw - unlawfully unlawfulli unlearn unlearn - unlearned unlearn unless unless - unlesson unlesson unletter unlett - unlettered unlett unlick unlick - unlike unlik unlikely unlik - unlimited unlimit unlineal unlin - unlink unlink unload unload - unloaded unload unloading unload - unloads unload unlock unlock - unlocks unlock unlook unlook - unlooked unlook unloos unloo - unloose unloos unlov unlov - unloving unlov unluckily unluckili - unlucky unlucki unmade unmad - unmake unmak unmanly unmanli - unmann unmann unmanner unmann - unmannerd unmannerd unmannerly unmannerli - unmarried unmarri unmask unmask - unmasked unmask unmasking unmask - unmasks unmask unmast unmast - unmatch unmatch unmatchable unmatch - unmatched unmatch unmeasurable unmeasur - unmeet unmeet unmellowed unmellow - unmerciful unmerci unmeritable unmerit - unmeriting unmerit unminded unmind - unmindfull unmindful unmingled unmingl - unmitigable unmitig unmitigated unmitig - unmix unmix unmoan unmoan - unmov unmov unmoved unmov - unmoving unmov unmuffles unmuffl - unmuffling unmuffl unmusical unmus - unmuzzle unmuzzl unmuzzled unmuzzl - unnatural unnatur unnaturally unnatur - unnaturalness unnatur unnecessarily unnecessarili - unnecessary unnecessari unneighbourly unneighbourli - unnerved unnerv unnoble unnobl - unnoted unnot unnumb unnumb - unnumber unnumb unowed unow - unpack unpack unpaid unpaid - unparagon unparagon unparallel unparallel - unpartial unparti unpath unpath - unpaved unpav unpay unpai - unpeaceable unpeac unpeg unpeg - unpeople unpeopl unpeopled unpeopl - unperfect unperfect unperfectness unperfect - unpick unpick unpin unpin - unpink unpink unpitied unpiti - unpitifully unpitifulli unplagu unplagu - unplausive unplaus unpleas unplea - unpleasant unpleas unpleasing unpleas - unpolicied unpolici unpolish unpolish - unpolished unpolish unpolluted unpollut - unpossess unpossess unpossessing unpossess - unpossible unposs unpractis unpracti - unpregnant unpregn unpremeditated unpremedit - unprepar unprepar unprepared unprepar - unpress unpress unprevailing unprevail - unprevented unprev unpriz unpriz - unprizable unpriz unprofitable unprofit - unprofited unprofit unproper unprop - unproperly unproperli unproportion unproport - unprovide unprovid unprovided unprovid - unprovident unprovid unprovokes unprovok - unprun unprun unpruned unprun - unpublish unpublish unpurged unpurg - unpurpos unpurpo unqualitied unqual - unqueen unqueen unquestion unquest - unquestionable unquestion unquiet unquiet - unquietly unquietli unquietness unquiet - unraised unrais unrak unrak - unread unread unready unreadi - unreal unreal unreasonable unreason - unreasonably unreason unreclaimed unreclaim - unreconciled unreconcil unreconciliable unreconcili - unrecounted unrecount unrecuring unrecur - unregarded unregard unregist unregist - unrelenting unrel unremovable unremov - unremovably unremov unreprievable unrepriev - unresolv unresolv unrespected unrespect - unrespective unrespect unrest unrest - unrestor unrestor unrestrained unrestrain - unreveng unreveng unreverend unreverend - unreverent unrever unrevers unrev - unrewarded unreward unrighteous unright - unrightful unright unripe unrip - unripp unripp unrivall unrival - unroll unrol unroof unroof - unroosted unroost unroot unroot - unrough unrough unruly unruli - unsafe unsaf unsaluted unsalut - unsanctified unsanctifi unsatisfied unsatisfi - unsavoury unsavouri unsay unsai - unscalable unscal unscann unscann - unscarr unscarr unschool unschool - unscorch unscorch unscour unscour - unscratch unscratch unseal unseal - unseam unseam unsearch unsearch - unseason unseason unseasonable unseason - unseasonably unseason unseasoned unseason - unseconded unsecond unsecret unsecret - unseduc unseduc unseeing unse - unseeming unseem unseemly unseemli - unseen unseen unseminar unseminar - unseparable unsepar unserviceable unservic - unset unset unsettle unsettl - unsettled unsettl unsever unsev - unsex unsex unshak unshak - unshaked unshak unshaken unshaken - unshaped unshap unshapes unshap - unsheath unsheath unsheathe unsheath - unshorn unshorn unshout unshout - unshown unshown unshrinking unshrink - unshrubb unshrubb unshunn unshunn - unshunnable unshunn unsifted unsift - unsightly unsightli unsinew unsinew - unsisting unsist unskilful unskil - unskilfully unskilfulli unskillful unskil - unslipping unslip unsmirched unsmirch - unsoil unsoil unsolicited unsolicit - unsorted unsort unsought unsought - unsound unsound unsounded unsound - unspeak unspeak unspeakable unspeak - unspeaking unspeak unsphere unspher - unspoke unspok unspoken unspoken - unspotted unspot unsquar unsquar - unstable unstabl unstaid unstaid - unstain unstain unstained unstain - unstanched unstanch unstate unstat - unsteadfast unsteadfast unstooping unstoop - unstringed unstring unstuff unstuff - unsubstantial unsubstanti unsuitable unsuit - unsuiting unsuit unsullied unsulli - unsunn unsunn unsur unsur - unsure unsur unsuspected unsuspect - unsway unswai unswayable unsway - unswayed unswai unswear unswear - unswept unswept unsworn unsworn - untainted untaint untalk untalk - untangle untangl untangled untangl - untasted untast untaught untaught - untempering untemp untender untend - untent untent untented untent - unthankful unthank unthankfulness unthank - unthink unthink unthought unthought - unthread unthread unthrift unthrift - unthrifts unthrift unthrifty unthrifti - untie unti untied unti - until until untimber untimb - untimely untim untir untir - untirable untir untired untir - untitled untitl unto unto - untold untold untouch untouch - untoward untoward untowardly untowardli - untraded untrad untrain untrain - untrained untrain untread untread - untreasur untreasur untried untri - untrimmed untrim untrod untrod - untrodden untrodden untroubled untroubl - untrue untru untrussing untruss - untruth untruth untruths untruth - untucked untuck untun untun - untune untun untuneable untun - untutor untutor untutored untutor - untwine untwin unurg unurg - unus unu unused unus - unusual unusu unvalued unvalu - unvanquish unvanquish unvarnish unvarnish - unveil unveil unveiling unveil - unvenerable unvener unvex unvex - unviolated unviol unvirtuous unvirtu - unvisited unvisit unvulnerable unvulner - unwares unwar unwarily unwarili - unwash unwash unwatch unwatch - unwearied unweari unwed unw - unwedgeable unwedg unweeded unweed - unweighed unweigh unweighing unweigh - unwelcome unwelcom unwept unwept - unwhipp unwhipp unwholesome unwholesom - unwieldy unwieldi unwilling unwil - unwillingly unwillingli unwillingness unwilling - unwind unwind unwiped unwip - unwise unwis unwisely unwis - unwish unwish unwished unwish - unwitted unwit unwittingly unwittingli - unwonted unwont unwooed unwoo - unworthier unworthi unworthiest unworthiest - unworthily unworthili unworthiness unworthi - unworthy unworthi unwrung unwrung - unyok unyok unyoke unyok - up up upbraid upbraid - upbraided upbraid upbraidings upbraid - upbraids upbraid uphoarded uphoard - uphold uphold upholdeth upholdeth - upholding uphold upholds uphold - uplift uplift uplifted uplift - upmost upmost upon upon - upper upper uprear uprear - upreared uprear upright upright - uprighteously upright uprightness upright - uprise upris uprising upris - uproar uproar uproars uproar - uprous uprou upshoot upshoot - upshot upshot upside upsid - upspring upspr upstairs upstair - upstart upstart upturned upturn - upward upward upwards upward - urchin urchin urchinfield urchinfield - urchins urchin urg urg - urge urg urged urg - urgent urgent urges urg - urgest urgest urging urg - urinal urin urinals urin - urine urin urn urn - urns urn urs ur - ursa ursa ursley urslei - ursula ursula urswick urswick - us us usage usag - usance usanc usances usanc - use us used us - useful us useless useless - user user uses us - usest usest useth useth - usher usher ushered usher - ushering usher ushers usher - using us usual usual - usually usual usurer usur - usurers usur usuries usuri - usuring usur usurp usurp - usurpation usurp usurped usurp - usurper usurp usurpers usurp - usurping usurp usurpingly usurpingli - usurps usurp usury usuri - ut ut utensil utensil - utensils utensil utility util - utmost utmost utt utt - utter utter utterance utter - uttered utter uttereth uttereth - uttering utter utterly utterli - uttermost uttermost utters utter - uy uy v v - va va vacancy vacanc - vacant vacant vacation vacat - vade vade vagabond vagabond - vagabonds vagabond vagram vagram - vagrom vagrom vail vail - vailed vail vailing vail - vaillant vaillant vain vain - vainer vainer vainglory vainglori - vainly vainli vainness vain - vais vai valanc valanc - valance valanc vale vale - valence valenc valentine valentin - valentinus valentinu valentio valentio - valeria valeria valerius valeriu - vales vale valiant valiant - valiantly valiantli valiantness valiant - validity valid vallant vallant - valley vallei valleys vallei - vally valli valor valor - valorous valor valorously valor - valour valour valu valu - valuation valuat value valu - valued valu valueless valueless - values valu valuing valu - vane vane vanish vanish - vanished vanish vanishes vanish - vanishest vanishest vanishing vanish - vanities vaniti vanity vaniti - vanquish vanquish vanquished vanquish - vanquisher vanquish vanquishest vanquishest - vanquisheth vanquisheth vant vant - vantage vantag vantages vantag - vantbrace vantbrac vapians vapian - vapor vapor vaporous vapor - vapour vapour vapours vapour - vara vara variable variabl - variance varianc variation variat - variations variat varied vari - variest variest variety varieti - varld varld varlet varlet - varletry varletri varlets varlet - varletto varletto varnish varnish - varrius varriu varro varro - vary vari varying vari - vassal vassal vassalage vassalag - vassals vassal vast vast - vastidity vastid vasty vasti - vat vat vater vater - vaudemont vaudemont vaughan vaughan - vault vault vaultages vaultag - vaulted vault vaulting vault - vaults vault vaulty vaulti - vaumond vaumond vaunt vaunt - vaunted vaunt vaunter vaunter - vaunting vaunt vauntingly vauntingli - vaunts vaunt vauvado vauvado - vaux vaux vaward vaward - ve ve veal veal - vede vede vehemence vehem - vehemency vehem vehement vehement - vehor vehor veil veil - veiled veil veiling veil - vein vein veins vein - vell vell velure velur - velutus velutu velvet velvet - vendible vendibl venerable vener - venereal vener venetia venetia - venetian venetian venetians venetian - veneys venei venge veng - vengeance vengeanc vengeances vengeanc - vengeful veng veni veni - venial venial venice venic - venison venison venit venit - venom venom venomous venom - venomously venom vent vent - ventages ventag vented vent - ventidius ventidiu ventricle ventricl - vents vent ventur ventur - venture ventur ventured ventur - ventures ventur venturing ventur - venturous ventur venue venu - venus venu venuto venuto - ver ver verb verb - verba verba verbal verbal - verbatim verbatim verbosity verbos - verdict verdict verdun verdun - verdure verdur vere vere - verefore verefor verg verg - verge verg vergers verger - verges verg verier verier - veriest veriest verified verifi - verify verifi verily verili - veritable verit verite verit - verities veriti verity veriti - vermilion vermilion vermin vermin - vernon vernon verona verona - veronesa veronesa versal versal - verse vers verses vers - versing vers vert vert - very veri vesper vesper - vessel vessel vessels vessel - vestal vestal vestments vestment - vesture vestur vetch vetch - vetches vetch veux veux - vex vex vexation vexat - vexations vexat vexed vex - vexes vex vexest vexest - vexeth vexeth vexing vex - vi vi via via - vial vial vials vial - viand viand viands viand - vic vic vicar vicar - vice vice vicegerent viceger - vicentio vicentio viceroy viceroi - viceroys viceroi vices vice - vici vici vicious viciou - viciousness vicious vict vict - victims victim victor victor - victoress victoress victories victori - victorious victori victors victor - victory victori victual victual - victuall victual victuals victual - videlicet videlicet video video - vides vide videsne videsn - vidi vidi vie vie - vied vi vienna vienna - view view viewest viewest - vieweth vieweth viewing view - viewless viewless views view - vigil vigil vigilance vigil - vigilant vigil vigitant vigit - vigour vigour vii vii - viii viii vile vile - vilely vile vileness vile - viler viler vilest vilest - vill vill village villag - villager villag villagery villageri - villages villag villain villain - villainies villaini villainous villain - villainously villain villains villain - villainy villaini villanies villani - villanous villan villany villani - villiago villiago villian villian - villianda villianda villians villian - vinaigre vinaigr vincentio vincentio - vincere vincer vindicative vindic - vine vine vinegar vinegar - vines vine vineyard vineyard - vineyards vineyard vint vint - vintner vintner viol viol - viola viola violate violat - violated violat violates violat - violation violat violator violat - violence violenc violent violent - violenta violenta violenteth violenteth - violently violent violet violet - violets violet viper viper - viperous viper vipers viper - vir vir virgilia virgilia - virgin virgin virginal virgin - virginalling virginal virginity virgin - virginius virginiu virgins virgin - virgo virgo virtue virtu - virtues virtu virtuous virtuou - virtuously virtuous visag visag - visage visag visages visag - visard visard viscount viscount - visible visibl visibly visibl - vision vision visions vision - visit visit visitation visit - visitations visit visited visit - visiting visit visitings visit - visitor visitor visitors visitor - visits visit visor visor - vita vita vitae vita - vital vital vitement vitement - vitruvio vitruvio vitx vitx - viva viva vivant vivant - vive vive vixen vixen - viz viz vizaments vizament - vizard vizard vizarded vizard - vizards vizard vizor vizor - vlouting vlout vocation vocat - vocativo vocativo vocatur vocatur - voce voce voic voic - voice voic voices voic - void void voided void - voiding void voke voke - volable volabl volant volant - volivorco volivorco volley vollei - volquessen volquessen volsce volsc - volsces volsc volscian volscian - volscians volscian volt volt - voltemand voltemand volubility volubl - voluble volubl volume volum - volumes volum volumnia volumnia - volumnius volumniu voluntaries voluntari - voluntary voluntari voluptuously voluptu - voluptuousness voluptu vomissement vomiss - vomit vomit vomits vomit - vor vor vore vore - vortnight vortnight vot vot - votaries votari votarist votarist - votarists votarist votary votari - votre votr vouch vouch - voucher voucher vouchers voucher - vouches vouch vouching vouch - vouchsaf vouchsaf vouchsafe vouchsaf - vouchsafed vouchsaf vouchsafes vouchsaf - vouchsafing vouchsaf voudrais voudrai - vour vour vous vou - voutsafe voutsaf vow vow - vowed vow vowel vowel - vowels vowel vowing vow - vows vow vox vox - voyage voyag voyages voyag - vraiment vraiment vulcan vulcan - vulgar vulgar vulgarly vulgarli - vulgars vulgar vulgo vulgo - vulnerable vulner vulture vultur - vultures vultur vurther vurther - w w wad wad - waddled waddl wade wade - waded wade wafer wafer - waft waft waftage waftag - wafting waft wafts waft - wag wag wage wage - wager wager wagers wager - wages wage wagging wag - waggish waggish waggling waggl - waggon waggon waggoner waggon - wagon wagon wagoner wagon - wags wag wagtail wagtail - wail wail wailful wail - wailing wail wails wail - wain wain wainropes wainrop - wainscot wainscot waist waist - wait wait waited wait - waiter waiter waiteth waiteth - waiting wait waits wait - wak wak wake wake - waked wake wakefield wakefield - waken waken wakened waken - wakes wake wakest wakest - waking wake wales wale - walk walk walked walk - walking walk walks walk - wall wall walled wall - wallet wallet wallets wallet - wallon wallon walloon walloon - wallow wallow walls wall - walnut walnut walter walter - wan wan wand wand - wander wander wanderer wander - wanderers wander wandering wander - wanders wander wands wand - wane wane waned wane - wanes wane waning wane - wann wann want want - wanted want wanteth wanteth - wanting want wanton wanton - wantonly wantonli wantonness wanton - wantons wanton wants want - wappen wappen war war - warble warbl warbling warbl - ward ward warded ward - warden warden warder warder - warders warder wardrobe wardrob - wardrop wardrop wards ward - ware ware wares ware - warily warili warkworth warkworth - warlike warlik warm warm - warmed warm warmer warmer - warming warm warms warm - warmth warmth warn warn - warned warn warning warn - warnings warn warns warn - warp warp warped warp - warr warr warrant warrant - warranted warrant warranteth warranteth - warrantise warrantis warrantize warrant - warrants warrant warranty warranti - warren warren warrener warren - warring war warrior warrior - warriors warrior wars war - wart wart warwick warwick - warwickshire warwickshir wary wari - was wa wash wash - washed wash washer washer - washes wash washford washford - washing wash wasp wasp - waspish waspish wasps wasp - wassail wassail wassails wassail - wast wast waste wast - wasted wast wasteful wast - wasters waster wastes wast - wasting wast wat wat - watch watch watched watch - watchers watcher watches watch - watchful watch watching watch - watchings watch watchman watchman - watchmen watchmen watchword watchword - water water waterdrops waterdrop - watered water waterfly waterfli - waterford waterford watering water - waterish waterish waterpots waterpot - waterrugs waterrug waters water - waterton waterton watery wateri - wav wav wave wave - waved wave waver waver - waverer waver wavering waver - waves wave waving wave - waw waw wawl wawl - wax wax waxed wax - waxen waxen waxes wax - waxing wax way wai - waylaid waylaid waylay waylai - ways wai wayward wayward - waywarder wayward waywardness wayward - we we weak weak - weaken weaken weakens weaken - weaker weaker weakest weakest - weakling weakl weakly weakli - weakness weak weal weal - wealsmen wealsmen wealth wealth - wealthiest wealthiest wealthily wealthili - wealthy wealthi wealtlly wealtlli - wean wean weapon weapon - weapons weapon wear wear - wearer wearer wearers wearer - wearied weari wearies weari - weariest weariest wearily wearili - weariness weari wearing wear - wearisome wearisom wears wear - weary weari weasel weasel - weather weather weathercock weathercock - weathers weather weav weav - weave weav weaver weaver - weavers weaver weaves weav - weaving weav web web - wed wed wedded wed - wedding wed wedg wedg - wedged wedg wedges wedg - wedlock wedlock wednesday wednesdai - weed weed weeded weed - weeder weeder weeding weed - weeds weed weedy weedi - week week weeke week - weekly weekli weeks week - ween ween weening ween - weep weep weeper weeper - weeping weep weepingly weepingli - weepings weep weeps weep - weet weet weigh weigh - weighed weigh weighing weigh - weighs weigh weight weight - weightier weightier weightless weightless - weights weight weighty weighti - weird weird welcom welcom - welcome welcom welcomer welcom - welcomes welcom welcomest welcomest - welfare welfar welkin welkin - well well wells well - welsh welsh welshman welshman - welshmen welshmen welshwomen welshwomen - wench wench wenches wench - wenching wench wend wend - went went wept wept - weraday weradai were were - wert wert west west - western western westminster westminst - westmoreland westmoreland westward westward - wet wet wether wether - wetting wet wezand wezand - whale whale whales whale - wharf wharf wharfs wharf - what what whate whate - whatever whatev whatsoe whatso - whatsoever whatsoev whatsome whatsom - whe whe wheat wheat - wheaten wheaten wheel wheel - wheeling wheel wheels wheel - wheer wheer wheeson wheeson - wheezing wheez whelk whelk - whelks whelk whelm whelm - whelp whelp whelped whelp - whelps whelp when when - whenas whena whence whenc - whencesoever whencesoev whene whene - whenever whenev whensoever whensoev - where where whereabout whereabout - whereas wherea whereat whereat - whereby wherebi wherefore wherefor - wherein wherein whereinto whereinto - whereof whereof whereon whereon - whereout whereout whereso whereso - wheresoe whereso wheresoever wheresoev - wheresome wheresom whereto whereto - whereuntil whereuntil whereunto whereunto - whereupon whereupon wherever wherev - wherewith wherewith wherewithal wherewith - whet whet whether whether - whetstone whetston whetted whet - whew whew whey whei - which which whiff whiff - whiffler whiffler while while - whiles while whilst whilst - whin whin whine whine - whined whine whinid whinid - whining whine whip whip - whipp whipp whippers whipper - whipping whip whips whip - whipster whipster whipstock whipstock - whipt whipt whirl whirl - whirled whirl whirligig whirligig - whirling whirl whirlpool whirlpool - whirls whirl whirlwind whirlwind - whirlwinds whirlwind whisp whisp - whisper whisper whispering whisper - whisperings whisper whispers whisper - whist whist whistle whistl - whistles whistl whistling whistl - whit whit white white - whitehall whitehal whitely white - whiteness white whiter whiter - whites white whitest whitest - whither whither whiting white - whitmore whitmor whitsters whitster - whitsun whitsun whittle whittl - whizzing whizz who who - whoa whoa whoe whoe - whoever whoever whole whole - wholesom wholesom wholesome wholesom - wholly wholli whom whom - whoobub whoobub whoop whoop - whooping whoop whor whor - whore whore whoremaster whoremast - whoremasterly whoremasterli whoremonger whoremong - whores whore whoreson whoreson - whoresons whoreson whoring whore - whorish whorish whose whose - whoso whoso whosoe whoso - whosoever whosoev why why - wi wi wick wick - wicked wick wickednes wickedn - wickedness wicked wicket wicket - wicky wicki wid wid - wide wide widens widen - wider wider widow widow - widowed widow widower widow - widowhood widowhood widows widow - wield wield wife wife - wight wight wights wight - wild wild wildcats wildcat - wilder wilder wilderness wilder - wildest wildest wildfire wildfir - wildly wildli wildness wild - wilds wild wiles wile - wilful wil wilfull wilful - wilfully wilfulli wilfulnes wilfuln - wilfulness wil will will - willed will willers willer - willeth willeth william william - williams william willing will - willingly willingli willingness willing - willoughby willoughbi willow willow - wills will wilt wilt - wiltshire wiltshir wimpled wimpl - win win wince winc - winch winch winchester winchest - wincot wincot wind wind - winded wind windgalls windgal - winding wind windlasses windlass - windmill windmil window window - windows window windpipe windpip - winds wind windsor windsor - windy windi wine wine - wing wing winged wing - wingfield wingfield wingham wingham - wings wing wink wink - winking wink winks wink - winner winner winners winner - winning win winnow winnow - winnowed winnow winnows winnow - wins win winter winter - winterly winterli winters winter - wip wip wipe wipe - wiped wipe wipes wipe - wiping wipe wire wire - wires wire wiry wiri - wisdom wisdom wisdoms wisdom - wise wise wiselier wiseli - wisely wise wiser wiser - wisest wisest wish wish - wished wish wisher wisher - wishers wisher wishes wish - wishest wishest wisheth wisheth - wishful wish wishing wish - wishtly wishtli wisp wisp - wist wist wit wit - witb witb witch witch - witchcraft witchcraft witches witch - witching witch with with - withal withal withdraw withdraw - withdrawing withdraw withdrawn withdrawn - withdrew withdrew wither wither - withered wither withering wither - withers wither withheld withheld - withhold withhold withholds withhold - within within withold withold - without without withstand withstand - withstanding withstand withstood withstood - witless witless witness wit - witnesses wit witnesseth witnesseth - witnessing wit wits wit - witted wit wittenberg wittenberg - wittiest wittiest wittily wittili - witting wit wittingly wittingli - wittol wittol wittolly wittolli - witty witti wiv wiv - wive wive wived wive - wives wive wiving wive - wizard wizard wizards wizard - wo wo woe woe - woeful woeful woefull woeful - woefullest woefullest woes woe - woful woful wolf wolf - wolfish wolfish wolsey wolsei - wolves wolv wolvish wolvish - woman woman womanhood womanhood - womanish womanish womankind womankind - womanly womanli womb womb - wombs womb womby wombi - women women won won - woncot woncot wond wond - wonder wonder wondered wonder - wonderful wonder wonderfully wonderfulli - wondering wonder wonders wonder - wondrous wondrou wondrously wondrous - wont wont wonted wont - woo woo wood wood - woodbine woodbin woodcock woodcock - woodcocks woodcock wooden wooden - woodland woodland woodman woodman - woodmonger woodmong woods wood - woodstock woodstock woodville woodvil - wooed woo wooer wooer - wooers wooer wooes wooe - woof woof wooing woo - wooingly wooingli wool wool - woollen woollen woolly woolli - woolsack woolsack woolsey woolsei - woolward woolward woos woo - wor wor worcester worcest - word word words word - wore wore worins worin - work work workers worker - working work workings work - workman workman workmanly workmanli - workmanship workmanship workmen workmen - works work worky worki - world world worldlings worldl - worldly worldli worlds world - worm worm worms worm - wormwood wormwood wormy wormi - worn worn worried worri - worries worri worry worri - worrying worri worse wors - worser worser worship worship - worshipful worship worshipfully worshipfulli - worshipp worshipp worshipper worshipp - worshippers worshipp worshippest worshippest - worships worship worst worst - worsted worst wort wort - worth worth worthied worthi - worthier worthier worthies worthi - worthiest worthiest worthily worthili - worthiness worthi worthless worthless - worths worth worthy worthi - worts wort wot wot - wots wot wotting wot - wouid wouid would would - wouldest wouldest wouldst wouldst - wound wound wounded wound - wounding wound woundings wound - woundless woundless wounds wound - wouns woun woven woven - wow wow wrack wrack - wrackful wrack wrangle wrangl - wrangler wrangler wranglers wrangler - wrangling wrangl wrap wrap - wrapp wrapp wraps wrap - wrapt wrapt wrath wrath - wrathful wrath wrathfully wrathfulli - wraths wrath wreak wreak - wreakful wreak wreaks wreak - wreath wreath wreathed wreath - wreathen wreathen wreaths wreath - wreck wreck wrecked wreck - wrecks wreck wren wren - wrench wrench wrenching wrench - wrens wren wrest wrest - wrested wrest wresting wrest - wrestle wrestl wrestled wrestl - wrestler wrestler wrestling wrestl - wretch wretch wretchcd wretchcd - wretched wretch wretchedness wretched - wretches wretch wring wring - wringer wringer wringing wring - wrings wring wrinkle wrinkl - wrinkled wrinkl wrinkles wrinkl - wrist wrist wrists wrist - writ writ write write - writer writer writers writer - writes write writhled writhl - writing write writings write - writs writ written written - wrong wrong wronged wrong - wronger wronger wrongful wrong - wrongfully wrongfulli wronging wrong - wrongly wrongli wrongs wrong - wronk wronk wrote wrote - wroth wroth wrought wrought - wrung wrung wry wry - wrying wry wt wt - wul wul wye wye - x x xanthippe xanthipp - xi xi xii xii - xiii xiii xiv xiv - xv xv y y - yard yard yards yard - yare yare yarely yare - yarn yarn yaughan yaughan - yaw yaw yawn yawn - yawning yawn ycleped yclepe - ycliped yclipe ye ye - yea yea yead yead - year year yearly yearli - yearn yearn yearns yearn - years year yeas yea - yeast yeast yedward yedward - yell yell yellow yellow - yellowed yellow yellowing yellow - yellowness yellow yellows yellow - yells yell yelping yelp - yeoman yeoman yeomen yeomen - yerk yerk yes ye - yesterday yesterdai yesterdays yesterdai - yesternight yesternight yesty yesti - yet yet yew yew - yicld yicld yield yield - yielded yield yielder yielder - yielders yielder yielding yield - yields yield yok yok - yoke yoke yoked yoke - yokefellow yokefellow yokes yoke - yoketh yoketh yon yon - yond yond yonder yonder - yongrey yongrei yore yore - yorick yorick york york - yorkists yorkist yorks york - yorkshire yorkshir you you - young young younger younger - youngest youngest youngling youngl - younglings youngl youngly youngli - younker younker your your - yours your yourself yourself - yourselves yourselv youth youth - youthful youth youths youth - youtli youtli zanies zani - zany zani zeal zeal - zealous zealou zeals zeal - zed zed zenelophon zenelophon - zenith zenith zephyrs zephyr - zir zir zo zo - zodiac zodiac zodiacs zodiac - zone zone zounds zound - zwagger zwagger -} - - -set i 0 -foreach {in out} $test_vocab { - do_test "1.$i.($in -> $out)" { - lindex [sqlite3_fts5_tokenize db porter $in] 0 - } $out - incr i -} - - -finish_test - DELETED ext/fts5/test/fts5prefix.test Index: ext/fts5/test/fts5prefix.test ================================================================== --- ext/fts5/test/fts5prefix.test +++ /dev/null @@ -1,60 +0,0 @@ -# 2015 Jan 13 -# -# 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 fts5prefix - - -#------------------------------------------------------------------------- -# Check that prefix indexes really do index n-character prefixes, not -# n-byte prefixes. Use the ascii tokenizer so as not to be confused by -# diacritic removal. -# -do_execsql_test 1.0 { - CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = ascii, prefix = 2) -} - -do_test 1.2 { - foreach {rowid string} { - 1 "\xCA\xCB\xCC\xCD" - 2 "\u1234\u5678\u4321\u8765" - } { - execsql { INSERT INTO t1(rowid, x) VALUES($rowid, $string) } - } -} {} - -do_execsql_test 1.1.2 { - INSERT INTO t1(t1) VALUES('integrity-check'); -} - -#db eval { select fts5_decode(id, block) AS d FROM t1_data; } { puts $d } - -foreach o {1 2} { - if {$o==2} breakpoint - foreach {tn q res} { - 1 "SELECT rowid FROM t1 WHERE t1 MATCH '\xCA\xCB*'" 1 - 2 "SELECT rowid FROM t1 WHERE t1 MATCH '\u1234\u5678*'" 2 - } { - do_execsql_test 1.$o.$tn $q $res - } - - execsql { - DELETE FROM t1_data WHERE - rowid>=fts5_rowid('start-of-index', 0) AND - rowidzKey)==pOld ); */ ppParent = amatchAvlFromPtr(pOld, ppHead); if( pOld->pBefore==0 && pOld->pAfter==0 ){ *ppParent = 0; pBalance = pOld->pUp; Index: ext/misc/fuzzer.c ================================================================== --- ext/misc/fuzzer.c +++ ext/misc/fuzzer.c @@ -340,11 +340,12 @@ pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo ); if( pRule==0 ){ rc = SQLITE_NOMEM; }else{ memset(pRule, 0, sizeof(*pRule)); - pRule->zFrom = &pRule->zTo[nTo+1]; + pRule->zFrom = pRule->zTo; + pRule->zFrom += nTo + 1; pRule->nFrom = nFrom; memcpy(pRule->zFrom, zFrom, nFrom+1); memcpy(pRule->zTo, zTo, nTo+1); pRule->nTo = nTo; pRule->rCost = nCost; Index: ext/misc/spellfix.c ================================================================== --- ext/misc/spellfix.c +++ ext/misc/spellfix.c @@ -354,11 +354,11 @@ ** of zB that was deemed to match zA. */ static int editdist1(const char *zA, const char *zB, int *pnMatch){ int nA, nB; /* Number of characters in zA[] and zB[] */ int xA, xB; /* Loop counters for zA[] and zB[] */ - char cA, cB; /* Current character of zA and zB */ + char cA = 0, cB; /* Current character of zA and zB */ char cAprev, cBprev; /* Previous character of zA and zB */ char cAnext, cBnext; /* Next character in zA and zB */ int d; /* North-west cost value */ int dc = 0; /* North-west character value */ int res; /* Final result */ Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -367,17 +367,16 @@ */ static int readInt16(u8 *p){ return (p[0]<<8) + p[1]; } static void readCoord(u8 *p, RtreeCoord *pCoord){ - u32 i = ( + pCoord->u = ( (((u32)p[0]) << 24) + (((u32)p[1]) << 16) + (((u32)p[2]) << 8) + (((u32)p[3]) << 0) ); - *(u32 *)pCoord = i; } static i64 readInt64(u8 *p){ return ( (((i64)p[0]) << 56) + (((i64)p[1]) << 48) + @@ -402,11 +401,11 @@ } static int writeCoord(u8 *p, RtreeCoord *pCoord){ u32 i; assert( sizeof(RtreeCoord)==4 ); assert( sizeof(u32)==4 ); - i = *(u32 *)pCoord; + i = pCoord->u; p[0] = (i>>24)&0xFF; p[1] = (i>>16)&0xFF; p[2] = (i>> 8)&0xFF; p[3] = (i>> 0)&0xFF; return 4; @@ -733,18 +732,17 @@ RtreeNode *pNode, /* The node containing the cell to be read */ int iCell, /* Index of the cell within the node */ RtreeCell *pCell /* OUT: Write the cell contents here */ ){ u8 *pData; - u8 *pEnd; RtreeCoord *pCoord; + int ii; pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); - pEnd = pData + pRtree->nDim*8; pCoord = pCell->aCoord; - for(; pDatanDim*2; ii++){ + readCoord(&pData[ii*4], &pCoord[ii]); } } /* Forward declaration for the function that does the work of @@ -1180,11 +1178,11 @@ } i = pCur->nPoint++; pNew = pCur->aPoint + i; pNew->rScore = rScore; pNew->iLevel = iLevel; - assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH ); + assert( iLevel<=RTREE_MAX_DEPTH ); while( i>0 ){ RtreeSearchPoint *pParent; j = (i-1)/2; pParent = pCur->aPoint + j; if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break; @@ -2803,10 +2801,12 @@ RtreeCell cell; /* New cell to insert if nData>1 */ int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ rtreeReference(pRtree); assert(nData>=1); + + cell.iRowid = 0; /* Used only to suppress a compiler warning */ /* Constraint handling. A write operation on an r-tree table may return ** SQLITE_CONSTRAINT for two reasons: ** ** 1. A duplicate rowid value, or Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -45,11 +45,10 @@ # This is how we compile # TCCX = $(TCC) $(OPTS) -I. -I$(TOP)/src -I$(TOP) TCCX += -I$(TOP)/ext/rtree -I$(TOP)/ext/icu -I$(TOP)/ext/fts3 TCCX += -I$(TOP)/ext/async -I$(TOP)/ext/userauth -TCCX += -I$(TOP)/ext/fts5 # Object files for the SQLite library. # LIBOBJ+= vdbe.o parse.o \ alter.o analyze.o attach.o auth.o \ @@ -70,22 +69,10 @@ table.o threads.o tokenize.o trigger.o \ update.o userauth.o util.o vacuum.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o utf.o vtab.o -LIBOBJ += fts5.o -LIBOBJ += fts5_aux.o -LIBOBJ += fts5_buffer.o -LIBOBJ += fts5_config.o -LIBOBJ += fts5_expr.o -LIBOBJ += fts5_hash.o -LIBOBJ += fts5_index.o -LIBOBJ += fts5_storage.o -LIBOBJ += fts5_tokenize.o -LIBOBJ += fts5_unicode2.o -LIBOBJ += fts5parse.o - # All of the source code files. # SRC = \ @@ -123,10 +110,11 @@ $(TOP)/src/mem1.c \ $(TOP)/src/mem2.c \ $(TOP)/src/mem3.c \ $(TOP)/src/mem5.c \ $(TOP)/src/memjournal.c \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.c \ $(TOP)/src/mutex.h \ $(TOP)/src/mutex_noop.c \ $(TOP)/src/mutex_unix.c \ $(TOP)/src/mutex_w32.c \ @@ -228,24 +216,10 @@ $(TOP)/ext/rtree/rtree.h \ $(TOP)/ext/rtree/rtree.c SRC += \ $(TOP)/ext/userauth/userauth.c \ $(TOP)/ext/userauth/sqlite3userauth.h -SRC += \ - $(TOP)/ext/fts5/fts5.h \ - $(TOP)/ext/fts5/fts5Int.h \ - $(TOP)/ext/fts5/fts5_aux.c \ - $(TOP)/ext/fts5/fts5_buffer.c \ - $(TOP)/ext/fts5/fts5.c \ - $(TOP)/ext/fts5/fts5_config.c \ - $(TOP)/ext/fts5/fts5_expr.c \ - $(TOP)/ext/fts5/fts5_hash.c \ - $(TOP)/ext/fts5/fts5_index.c \ - fts5parse.c \ - $(TOP)/ext/fts5/fts5_storage.c \ - $(TOP)/ext/fts5/fts5_tokenize.c - # Generated source code files # SRC += \ keywordhash.h \ @@ -316,12 +290,11 @@ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c \ - $(TOP)/ext/misc/vfslog.c \ - $(TOP)/ext/fts5/fts5_tcl.c + $(TOP)/ext/misc/vfslog.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c @@ -371,10 +344,11 @@ $(TOP)/src/btree.h \ $(TOP)/src/btreeInt.h \ $(TOP)/src/hash.h \ $(TOP)/src/hwtime.h \ keywordhash.h \ + $(TOP)/src/msvc.h \ $(TOP)/src/mutex.h \ opcodes.h \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/os_setup.h \ @@ -408,13 +382,10 @@ EXTHDR += \ $(TOP)/ext/rtree/rtree.h EXTHDR += \ $(TOP)/ext/icu/sqliteicu.h EXTHDR += \ - $(TOP)/ext/fts5/fts5Int.h \ - $(TOP)/ext/fts5/fts5.h -EXTHDR += \ $(TOP)/ext/userauth/sqlite3userauth.h # This is the default Makefile target. The objects listed here # are what get build when you type just "make" with no arguments. # @@ -590,52 +561,12 @@ $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_unicode2.c fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c -fts5.o: $(TOP)/ext/fts5/fts5.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5.c - rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c - -# FTS5 things -# -fts5_aux.o: $(TOP)/ext/fts5/fts5_aux.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_aux.c - -fts5_buffer.o: $(TOP)/ext/fts5/fts5_buffer.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_buffer.c - -fts5_config.o: $(TOP)/ext/fts5/fts5_config.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_config.c - -fts5_expr.o: $(TOP)/ext/fts5/fts5_expr.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_expr.c - -fts5_hash.o: $(TOP)/ext/fts5/fts5_hash.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_hash.c - -fts5_index.o: $(TOP)/ext/fts5/fts5_index.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_index.c - -fts5_storage.o: $(TOP)/ext/fts5/fts5_storage.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_storage.c - -fts5_tokenize.o: $(TOP)/ext/fts5/fts5_tokenize.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_tokenize.c - -fts5_unicode2.o: $(TOP)/ext/fts5/fts5_unicode2.c $(HDR) $(EXTHDR) - $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts5/fts5_unicode2.c - -fts5parse.c: $(TOP)/ext/fts5/fts5parse.y lemon - cp $(TOP)/ext/fts5/fts5parse.y . - rm -f fts5parse.h - ./lemon $(OPTS) fts5parse.y - mv fts5parse.c fts5parse.c.orig - cat fts5parse.c.orig | sed 's/yy/fts5yy/g' | sed 's/YY/fts5YY/g' > fts5parse.c - userauth.o: $(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/userauth/userauth.c @@ -748,13 +679,10 @@ $(TOP)/test/wordcount.c sqlite3.c speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o $(TCC) -I. -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB) -loadfts: $(TOP)/tool/loadfts.c libsqlite3.a - $(TCC) $(TOP)/tool/loadfts.c libsqlite3.a -o loadfts $(THREADLIB) - # This target will fail if the SQLite amalgamation contains any exported # symbols that do not begin with "sqlite3_". It is run as part of the # releasetest.tcl script. # checksymbols: sqlite3.o Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -446,11 +446,11 @@ p->iGet = -1; p->mxSample = mxSample; p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); p->current.anLt = &p->current.anEq[nColUp]; - p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565; + p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); /* Set up the Stat4Accum.a[] and aBest[] arrays */ p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; p->aBest = &p->a[mxSample]; pSpace = (u8*)(&p->a[mxSample+nCol]); Index: src/ctime.c ================================================================== --- src/ctime.c +++ src/ctime.c @@ -31,355 +31,355 @@ /* These macros are provided to "stringify" the value of the define ** for those options in which the value is meaningful. */ #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) -#ifdef SQLITE_32BIT_ROWID +#if SQLITE_32BIT_ROWID "32BIT_ROWID", #endif -#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +#if SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif -#ifdef SQLITE_CASE_SENSITIVE_LIKE +#if SQLITE_CASE_SENSITIVE_LIKE "CASE_SENSITIVE_LIKE", #endif -#ifdef SQLITE_CHECK_PAGES +#if SQLITE_CHECK_PAGES "CHECK_PAGES", #endif -#ifdef SQLITE_COVERAGE_TEST +#if SQLITE_COVERAGE_TEST "COVERAGE_TEST", #endif -#ifdef SQLITE_DEBUG +#if SQLITE_DEBUG "DEBUG", #endif -#ifdef SQLITE_DEFAULT_LOCKING_MODE +#if SQLITE_DEFAULT_LOCKING_MODE "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), #endif #if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc) "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), #endif -#ifdef SQLITE_DISABLE_DIRSYNC +#if SQLITE_DISABLE_DIRSYNC "DISABLE_DIRSYNC", #endif -#ifdef SQLITE_DISABLE_LFS +#if SQLITE_DISABLE_LFS "DISABLE_LFS", #endif -#ifdef SQLITE_ENABLE_API_ARMOR +#if SQLITE_ENABLE_API_ARMOR "ENABLE_API_ARMOR", #endif -#ifdef SQLITE_ENABLE_ATOMIC_WRITE +#if SQLITE_ENABLE_ATOMIC_WRITE "ENABLE_ATOMIC_WRITE", #endif -#ifdef SQLITE_ENABLE_CEROD +#if SQLITE_ENABLE_CEROD "ENABLE_CEROD", #endif -#ifdef SQLITE_ENABLE_COLUMN_METADATA +#if SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", #endif -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT +#if SQLITE_ENABLE_EXPENSIVE_ASSERT "ENABLE_EXPENSIVE_ASSERT", #endif -#ifdef SQLITE_ENABLE_FTS1 +#if SQLITE_ENABLE_FTS1 "ENABLE_FTS1", #endif -#ifdef SQLITE_ENABLE_FTS2 +#if SQLITE_ENABLE_FTS2 "ENABLE_FTS2", #endif -#ifdef SQLITE_ENABLE_FTS3 +#if SQLITE_ENABLE_FTS3 "ENABLE_FTS3", #endif -#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS +#if SQLITE_ENABLE_FTS3_PARENTHESIS "ENABLE_FTS3_PARENTHESIS", #endif -#ifdef SQLITE_ENABLE_FTS4 +#if SQLITE_ENABLE_FTS4 "ENABLE_FTS4", #endif -#ifdef SQLITE_ENABLE_ICU +#if SQLITE_ENABLE_ICU "ENABLE_ICU", #endif -#ifdef SQLITE_ENABLE_IOTRACE +#if SQLITE_ENABLE_IOTRACE "ENABLE_IOTRACE", #endif -#ifdef SQLITE_ENABLE_LOAD_EXTENSION +#if SQLITE_ENABLE_LOAD_EXTENSION "ENABLE_LOAD_EXTENSION", #endif -#ifdef SQLITE_ENABLE_LOCKING_STYLE +#if SQLITE_ENABLE_LOCKING_STYLE "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), #endif -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT +#if SQLITE_ENABLE_MEMORY_MANAGEMENT "ENABLE_MEMORY_MANAGEMENT", #endif -#ifdef SQLITE_ENABLE_MEMSYS3 +#if SQLITE_ENABLE_MEMSYS3 "ENABLE_MEMSYS3", #endif -#ifdef SQLITE_ENABLE_MEMSYS5 +#if SQLITE_ENABLE_MEMSYS5 "ENABLE_MEMSYS5", #endif -#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK +#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif -#ifdef SQLITE_ENABLE_RTREE +#if SQLITE_ENABLE_RTREE "ENABLE_RTREE", #endif #if defined(SQLITE_ENABLE_STAT4) "ENABLE_STAT4", #elif defined(SQLITE_ENABLE_STAT3) "ENABLE_STAT3", #endif -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY +#if SQLITE_ENABLE_UNLOCK_NOTIFY "ENABLE_UNLOCK_NOTIFY", #endif -#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT +#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT "ENABLE_UPDATE_DELETE_LIMIT", #endif -#ifdef SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC "HAS_CODEC", #endif -#ifdef SQLITE_HAVE_ISNAN +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN "HAVE_ISNAN", #endif -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX +#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX "HOMEGROWN_RECURSIVE_MUTEX", #endif -#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS +#if SQLITE_IGNORE_AFP_LOCK_ERRORS "IGNORE_AFP_LOCK_ERRORS", #endif -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS +#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS "IGNORE_FLOCK_LOCK_ERRORS", #endif #ifdef SQLITE_INT64_TYPE "INT64_TYPE", #endif -#ifdef SQLITE_LOCK_TRACE +#if SQLITE_LOCK_TRACE "LOCK_TRACE", #endif #if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc) "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), #endif #ifdef SQLITE_MAX_SCHEMA_RETRY "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), #endif -#ifdef SQLITE_MEMDEBUG +#if SQLITE_MEMDEBUG "MEMDEBUG", #endif -#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT +#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT "MIXED_ENDIAN_64BIT_FLOAT", #endif -#ifdef SQLITE_NO_SYNC +#if SQLITE_NO_SYNC "NO_SYNC", #endif -#ifdef SQLITE_OMIT_ALTERTABLE +#if SQLITE_OMIT_ALTERTABLE "OMIT_ALTERTABLE", #endif -#ifdef SQLITE_OMIT_ANALYZE +#if SQLITE_OMIT_ANALYZE "OMIT_ANALYZE", #endif -#ifdef SQLITE_OMIT_ATTACH +#if SQLITE_OMIT_ATTACH "OMIT_ATTACH", #endif -#ifdef SQLITE_OMIT_AUTHORIZATION +#if SQLITE_OMIT_AUTHORIZATION "OMIT_AUTHORIZATION", #endif -#ifdef SQLITE_OMIT_AUTOINCREMENT +#if SQLITE_OMIT_AUTOINCREMENT "OMIT_AUTOINCREMENT", #endif -#ifdef SQLITE_OMIT_AUTOINIT +#if SQLITE_OMIT_AUTOINIT "OMIT_AUTOINIT", #endif -#ifdef SQLITE_OMIT_AUTOMATIC_INDEX +#if SQLITE_OMIT_AUTOMATIC_INDEX "OMIT_AUTOMATIC_INDEX", #endif -#ifdef SQLITE_OMIT_AUTORESET +#if SQLITE_OMIT_AUTORESET "OMIT_AUTORESET", #endif -#ifdef SQLITE_OMIT_AUTOVACUUM +#if SQLITE_OMIT_AUTOVACUUM "OMIT_AUTOVACUUM", #endif -#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION +#if SQLITE_OMIT_BETWEEN_OPTIMIZATION "OMIT_BETWEEN_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_BLOB_LITERAL +#if SQLITE_OMIT_BLOB_LITERAL "OMIT_BLOB_LITERAL", #endif -#ifdef SQLITE_OMIT_BTREECOUNT +#if SQLITE_OMIT_BTREECOUNT "OMIT_BTREECOUNT", #endif -#ifdef SQLITE_OMIT_BUILTIN_TEST +#if SQLITE_OMIT_BUILTIN_TEST "OMIT_BUILTIN_TEST", #endif -#ifdef SQLITE_OMIT_CAST +#if SQLITE_OMIT_CAST "OMIT_CAST", #endif -#ifdef SQLITE_OMIT_CHECK +#if SQLITE_OMIT_CHECK "OMIT_CHECK", #endif -#ifdef SQLITE_OMIT_COMPLETE +#if SQLITE_OMIT_COMPLETE "OMIT_COMPLETE", #endif -#ifdef SQLITE_OMIT_COMPOUND_SELECT +#if SQLITE_OMIT_COMPOUND_SELECT "OMIT_COMPOUND_SELECT", #endif -#ifdef SQLITE_OMIT_CTE +#if SQLITE_OMIT_CTE "OMIT_CTE", #endif -#ifdef SQLITE_OMIT_DATETIME_FUNCS +#if SQLITE_OMIT_DATETIME_FUNCS "OMIT_DATETIME_FUNCS", #endif -#ifdef SQLITE_OMIT_DECLTYPE +#if SQLITE_OMIT_DECLTYPE "OMIT_DECLTYPE", #endif -#ifdef SQLITE_OMIT_DEPRECATED +#if SQLITE_OMIT_DEPRECATED "OMIT_DEPRECATED", #endif -#ifdef SQLITE_OMIT_DISKIO +#if SQLITE_OMIT_DISKIO "OMIT_DISKIO", #endif -#ifdef SQLITE_OMIT_EXPLAIN +#if SQLITE_OMIT_EXPLAIN "OMIT_EXPLAIN", #endif -#ifdef SQLITE_OMIT_FLAG_PRAGMAS +#if SQLITE_OMIT_FLAG_PRAGMAS "OMIT_FLAG_PRAGMAS", #endif -#ifdef SQLITE_OMIT_FLOATING_POINT +#if SQLITE_OMIT_FLOATING_POINT "OMIT_FLOATING_POINT", #endif -#ifdef SQLITE_OMIT_FOREIGN_KEY +#if SQLITE_OMIT_FOREIGN_KEY "OMIT_FOREIGN_KEY", #endif -#ifdef SQLITE_OMIT_GET_TABLE +#if SQLITE_OMIT_GET_TABLE "OMIT_GET_TABLE", #endif -#ifdef SQLITE_OMIT_INCRBLOB +#if SQLITE_OMIT_INCRBLOB "OMIT_INCRBLOB", #endif -#ifdef SQLITE_OMIT_INTEGRITY_CHECK +#if SQLITE_OMIT_INTEGRITY_CHECK "OMIT_INTEGRITY_CHECK", #endif -#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION +#if SQLITE_OMIT_LIKE_OPTIMIZATION "OMIT_LIKE_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_LOAD_EXTENSION +#if SQLITE_OMIT_LOAD_EXTENSION "OMIT_LOAD_EXTENSION", #endif -#ifdef SQLITE_OMIT_LOCALTIME +#if SQLITE_OMIT_LOCALTIME "OMIT_LOCALTIME", #endif -#ifdef SQLITE_OMIT_LOOKASIDE +#if SQLITE_OMIT_LOOKASIDE "OMIT_LOOKASIDE", #endif -#ifdef SQLITE_OMIT_MEMORYDB +#if SQLITE_OMIT_MEMORYDB "OMIT_MEMORYDB", #endif -#ifdef SQLITE_OMIT_OR_OPTIMIZATION +#if SQLITE_OMIT_OR_OPTIMIZATION "OMIT_OR_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_PAGER_PRAGMAS +#if SQLITE_OMIT_PAGER_PRAGMAS "OMIT_PAGER_PRAGMAS", #endif -#ifdef SQLITE_OMIT_PRAGMA +#if SQLITE_OMIT_PRAGMA "OMIT_PRAGMA", #endif -#ifdef SQLITE_OMIT_PROGRESS_CALLBACK +#if SQLITE_OMIT_PROGRESS_CALLBACK "OMIT_PROGRESS_CALLBACK", #endif -#ifdef SQLITE_OMIT_QUICKBALANCE +#if SQLITE_OMIT_QUICKBALANCE "OMIT_QUICKBALANCE", #endif -#ifdef SQLITE_OMIT_REINDEX +#if SQLITE_OMIT_REINDEX "OMIT_REINDEX", #endif -#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS +#if SQLITE_OMIT_SCHEMA_PRAGMAS "OMIT_SCHEMA_PRAGMAS", #endif -#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS +#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS "OMIT_SCHEMA_VERSION_PRAGMAS", #endif -#ifdef SQLITE_OMIT_SHARED_CACHE +#if SQLITE_OMIT_SHARED_CACHE "OMIT_SHARED_CACHE", #endif -#ifdef SQLITE_OMIT_SUBQUERY +#if SQLITE_OMIT_SUBQUERY "OMIT_SUBQUERY", #endif -#ifdef SQLITE_OMIT_TCL_VARIABLE +#if SQLITE_OMIT_TCL_VARIABLE "OMIT_TCL_VARIABLE", #endif -#ifdef SQLITE_OMIT_TEMPDB +#if SQLITE_OMIT_TEMPDB "OMIT_TEMPDB", #endif -#ifdef SQLITE_OMIT_TRACE +#if SQLITE_OMIT_TRACE "OMIT_TRACE", #endif -#ifdef SQLITE_OMIT_TRIGGER +#if SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif -#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION +#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_UTF16 +#if SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif -#ifdef SQLITE_OMIT_VACUUM +#if SQLITE_OMIT_VACUUM "OMIT_VACUUM", #endif -#ifdef SQLITE_OMIT_VIEW +#if SQLITE_OMIT_VIEW "OMIT_VIEW", #endif -#ifdef SQLITE_OMIT_VIRTUALTABLE +#if SQLITE_OMIT_VIRTUALTABLE "OMIT_VIRTUALTABLE", #endif -#ifdef SQLITE_OMIT_WAL +#if SQLITE_OMIT_WAL "OMIT_WAL", #endif -#ifdef SQLITE_OMIT_WSD +#if SQLITE_OMIT_WSD "OMIT_WSD", #endif -#ifdef SQLITE_OMIT_XFER_OPT +#if SQLITE_OMIT_XFER_OPT "OMIT_XFER_OPT", #endif -#ifdef SQLITE_PERFORMANCE_TRACE +#if SQLITE_PERFORMANCE_TRACE "PERFORMANCE_TRACE", #endif -#ifdef SQLITE_PROXY_DEBUG +#if SQLITE_PROXY_DEBUG "PROXY_DEBUG", #endif -#ifdef SQLITE_RTREE_INT_ONLY +#if SQLITE_RTREE_INT_ONLY "RTREE_INT_ONLY", #endif -#ifdef SQLITE_SECURE_DELETE +#if SQLITE_SECURE_DELETE "SECURE_DELETE", #endif -#ifdef SQLITE_SMALL_STACK +#if SQLITE_SMALL_STACK "SMALL_STACK", #endif -#ifdef SQLITE_SOUNDEX +#if SQLITE_SOUNDEX "SOUNDEX", #endif -#ifdef SQLITE_SYSTEM_MALLOC +#if SQLITE_SYSTEM_MALLOC "SYSTEM_MALLOC", #endif -#ifdef SQLITE_TCL +#if SQLITE_TCL "TCL", #endif #if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc) "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), #endif -#ifdef SQLITE_TEST +#if SQLITE_TEST "TEST", #endif #if defined(SQLITE_THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), #endif -#ifdef SQLITE_USE_ALLOCA +#if SQLITE_USE_ALLOCA "USE_ALLOCA", #endif -#ifdef SQLITE_USER_AUTHENTICATION +#if SQLITE_USER_AUTHENTICATION "USER_AUTHENTICATION", #endif -#ifdef SQLITE_WIN32_MALLOC +#if SQLITE_WIN32_MALLOC "WIN32_MALLOC", #endif -#ifdef SQLITE_ZERO_MALLOC +#if SQLITE_ZERO_MALLOC "ZERO_MALLOC" #endif }; /* @@ -390,11 +390,11 @@ ** is not required for a match. */ int sqlite3_compileoption_used(const char *zOptName){ int i, n; -#ifdef SQLITE_ENABLE_API_ARMOR +#if SQLITE_ENABLE_API_ARMOR if( zOptName==0 ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif Index: src/date.c ================================================================== --- src/date.c +++ src/date.c @@ -410,12 +410,13 @@ ** ** If the user has not indicated to use localtime_r() or localtime_s() ** already, check for an MSVC build environment that provides ** localtime_s(). */ -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ - defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \ + && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#undef HAVE_LOCALTIME_S #define HAVE_LOCALTIME_S 1 #endif #ifndef SQLITE_OMIT_LOCALTIME /* @@ -431,12 +432,11 @@ ** library function localtime_r() is used to assist in the calculation of ** local time. */ static int osLocaltime(time_t *t, struct tm *pTm){ int rc; -#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ - && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S struct tm *pX; #if SQLITE_THREADSAFE>0 sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); #endif sqlite3_mutex_enter(mutex); @@ -449,11 +449,11 @@ rc = pX==0; #else #ifndef SQLITE_OMIT_BUILTIN_TEST if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; #endif -#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R +#if HAVE_LOCALTIME_R rc = localtime_r(t, pTm)==0; #else rc = localtime_s(pTm, t); #endif /* HAVE_LOCALTIME_R */ #endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ @@ -893,12 +893,14 @@ DateTime x; u64 n; size_t i,j; char *z; sqlite3 *db; - const char *zFmt = (const char*)sqlite3_value_text(argv[0]); + const char *zFmt; char zBuf[100]; + if( argc==0 ) return; + zFmt = (const char*)sqlite3_value_text(argv[0]); if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; db = sqlite3_context_db_handle(context); for(i=0, n=1; zFmt[i]; i++, n++){ if( zFmt[i]=='%' ){ switch( zFmt[i+1] ){ @@ -1088,11 +1090,11 @@ UNUSED_PARAMETER(argv); iT = sqlite3StmtCurrentTime(context); if( iT<=0 ) return; t = iT/1000 - 10000*(sqlite3_int64)21086676; -#ifdef HAVE_GMTIME_R +#if HAVE_GMTIME_R pTm = gmtime_r(&t, &sNow); #else sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); pTm = gmtime(&t); if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -224,12 +224,12 @@ const char *zDb; /* Name of database holding pTab */ int i; /* Loop counter */ WhereInfo *pWInfo; /* Information about the WHERE clause */ Index *pIdx; /* For looping over indices of the table */ int iTabCur; /* Cursor number for the table */ - int iDataCur; /* VDBE cursor for the canonical data source */ - int iIdxCur; /* Cursor number of the first index */ + int iDataCur = 0; /* VDBE cursor for the canonical data source */ + int iIdxCur = 0; /* Cursor number of the first index */ int nIdx; /* Number of indices */ sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -513,11 +513,11 @@ Expr *pLeft, /* Left operand */ Expr *pRight, /* Right operand */ const Token *pToken /* Argument token */ ){ Expr *p; - if( op==TK_AND && pLeft && pRight ){ + if( op==TK_AND && pLeft && pRight && pParse->nErr==0 ){ /* Take advantage of short-circuit false optimization for AND */ p = sqlite3ExprAnd(pParse->db, pLeft, pRight); }else{ p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); @@ -4067,14 +4067,15 @@ ** NEVER() will need to be removed. */ if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){ int i; struct SrcCount *p = pWalker->u.pSrcCount; SrcList *pSrc = p->pSrc; - for(i=0; inSrc; i++){ + int nSrc = pSrc ? pSrc->nSrc : 0; + for(i=0; iiTable==pSrc->a[i].iCursor ) break; } - if( inSrc ){ + if( inThis++; }else{ p->nOther++; } } Index: src/global.c ================================================================== --- src/global.c +++ src/global.c @@ -150,10 +150,17 @@ */ #ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN # define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 #endif +/* The minimum PMA size is set to this value multiplied by the database +** page size in bytes. +*/ +#ifndef SQLITE_SORTER_PMASZ +# define SQLITE_SORTER_PMASZ 250 +#endif + /* ** The following singleton contains the global configuration for ** the SQLite library. */ SQLITE_WSD struct Sqlite3Config sqlite3Config = { @@ -180,10 +187,11 @@ (void*)0, /* pPage */ 0, /* szPage */ 0, /* nPage */ 0, /* mxParserStack */ 0, /* sharedCacheEnabled */ + SQLITE_SORTER_PMASZ, /* szPma */ /* All the rest should always be initialized to zero */ 0, /* isInit */ 0, /* inProgress */ 0, /* isMutexInit */ 0, /* isMallocInit */ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -17,13 +17,10 @@ #include "sqliteInt.h" #ifdef SQLITE_ENABLE_FTS3 # include "fts3.h" #endif -#ifdef SQLITE_ENABLE_FTS5 -int sqlite3Fts5Init(sqlite3*); -#endif #ifdef SQLITE_ENABLE_RTREE # include "rtree.h" #endif #ifdef SQLITE_ENABLE_ICU # include "sqliteicu.h" @@ -594,10 +591,15 @@ ** heap. */ sqlite3GlobalConfig.nHeap = va_arg(ap, int); break; } #endif + + case SQLITE_CONFIG_PMASZ: { + sqlite3GlobalConfig.szPma = va_arg(ap, unsigned int); + break; + } default: { rc = SQLITE_ERROR; break; } @@ -1346,11 +1348,11 @@ */ static int sqliteDefaultBusyCallback( void *ptr, /* Database connection */ int count /* Number of times table has been busy */ ){ -#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP) +#if SQLITE_OS_WIN || HAVE_USLEEP static const u8 delays[] = { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; # define NDELAY ArraySize(delays) @@ -2822,16 +2824,10 @@ if( !db->mallocFailed && rc==SQLITE_OK ){ rc = sqlite3Fts3Init(db); } #endif -#ifdef SQLITE_ENABLE_FTS5 - if( !db->mallocFailed && rc==SQLITE_OK ){ - rc = sqlite3Fts5Init(db); - } -#endif - #ifdef SQLITE_ENABLE_ICU if( !db->mallocFailed && rc==SQLITE_OK ){ rc = sqlite3IcuInit(db); } #endif @@ -3142,11 +3138,11 @@ ){ int rc; char *zErrMsg = 0; Table *pTab = 0; Column *pCol = 0; - int iCol; + int iCol = 0; char const *zDataType = 0; char const *zCollSeq = 0; int notnull = 0; int primarykey = 0; Index: src/mem1.c ================================================================== --- src/mem1.c +++ src/mem1.c @@ -77,13 +77,13 @@ /* ** The malloc.h header file is needed for malloc_usable_size() function ** on some systems (e.g. Linux). */ -#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE) -# define SQLITE_USE_MALLOC_H -# define SQLITE_USE_MALLOC_USABLE_SIZE +#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE +# define SQLITE_USE_MALLOC_H 1 +# define SQLITE_USE_MALLOC_USABLE_SIZE 1 /* ** The MSVCRT has malloc_usable_size(), but it is called _msize(). The ** use of _msize() is automatic, but can be disabled by compiling with ** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires ** the malloc.h header file. ADDED src/msvc.h Index: src/msvc.h ================================================================== --- /dev/null +++ src/msvc.h @@ -0,0 +1,35 @@ +/* +** 2015 January 12 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to MSVC. +*/ +#ifndef _MSVC_H_ +#define _MSVC_H_ + +#if defined(_MSC_VER) +#pragma warning(disable : 4054) +#pragma warning(disable : 4055) +#pragma warning(disable : 4100) +#pragma warning(disable : 4127) +#pragma warning(disable : 4152) +#pragma warning(disable : 4189) +#pragma warning(disable : 4206) +#pragma warning(disable : 4210) +#pragma warning(disable : 4232) +#pragma warning(disable : 4244) +#pragma warning(disable : 4305) +#pragma warning(disable : 4306) +#pragma warning(disable : 4702) +#pragma warning(disable : 4706) +#endif /* defined(_MSC_VER) */ + +#endif /* _MSVC_H_ */ Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -3384,13 +3384,13 @@ /* ** We do not trust systems to provide a working fdatasync(). Some do. ** Others do no. To be safe, we will stick with the (slightly slower) ** fsync(). If you know that your system does support fdatasync() correctly, -** then simply compile with -Dfdatasync=fdatasync +** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC */ -#if !defined(fdatasync) +#if !defined(fdatasync) && !HAVE_FDATASYNC # define fdatasync fsync #endif /* ** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not @@ -3715,22 +3715,23 @@ ** at offset (nSize-1), to set the size of the file correctly. ** This is a similar technique to that used by glibc on systems ** that do not have a real fallocate() call. */ int nBlk = buf.st_blksize; /* File-system block size */ + int nWrite = 0; /* Number of bytes written by seekAndWrite */ i64 iWrite; /* Next offset to write to */ iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; assert( iWrite>=buf.st_size ); assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) ); assert( ((iWrite+1)%nBlk)==0 ); for(/*no-op*/; iWritepWith = W; if( p->pPrior ){ + u16 allValues = SF_Values; pNext = 0; for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ pLoop->pNext = pNext; pLoop->selFlags |= SF_Compound; + allValues &= pLoop->selFlags; } - mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; - if( mxSelect && cnt>mxSelect ){ + if( allValues ){ + p->selFlags |= SF_AllValues; + }else if( + (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 + && cnt>mxSelect + ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); } } }else{ sqlite3WithDelete(pParse->db, W); Index: src/pcache.c ================================================================== --- src/pcache.c +++ src/pcache.c @@ -29,22 +29,10 @@ void *pStress; /* Argument to xStress */ sqlite3_pcache *pCache; /* Pluggable cache module */ PgHdr *pPage1; /* Reference to page 1 */ }; -/* -** Some of the assert() macros in this code are too expensive to run -** even during normal debugging. Use them only rarely on long-running -** tests. Enable the expensive asserts using the -** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. -*/ -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT -# define expensive_assert(X) assert(X) -#else -# define expensive_assert(X) -#endif - /********************************** Linked List Management ********************/ /* Allowed values for second argument to pcacheManageDirtyList() */ #define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ #define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -725,11 +725,11 @@ char *zRight = 0; /* Nul-terminated UTF-8 string , or NULL */ const char *zDb = 0; /* The database name */ Token *pId; /* Pointer to token */ char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ int iDb; /* Database index for */ - int lwr, upr, mid; /* Binary search bounds */ + int lwr, upr, mid = 0; /* Binary search bounds */ int rc; /* return value form SQLITE_FCNTL_PRAGMA */ sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* The specific database being pragmaed */ Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ Index: src/printf.c ================================================================== --- src/printf.c +++ src/printf.c @@ -11,21 +11,10 @@ ** routines format strings much like the printf() from the standard C ** library, though the implementation here has enhancements to support ** SQLlite. */ #include "sqliteInt.h" - -/* -** If the strchrnul() library function is available, then set -** HAVE_STRCHRNUL. If that routine is not available, this module -** will supply its own. The built-in version is slower than -** the glibc version so the glibc version is definitely preferred. -*/ -#if !defined(HAVE_STRCHRNUL) -# define HAVE_STRCHRNUL 0 -#endif - /* ** Conversion types fall into various categories as defined by the ** following enumeration. */ Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -56,24 +56,29 @@ u8 sortFlags; /* Zero or more SORTFLAG_* bits */ }; #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ /* -** Delete all the content of a Select structure but do not deallocate -** the select structure itself. +** Delete all the content of a Select structure. Deallocate the structure +** itself only if bFree is true. */ -static void clearSelect(sqlite3 *db, Select *p){ - sqlite3ExprListDelete(db, p->pEList); - sqlite3SrcListDelete(db, p->pSrc); - sqlite3ExprDelete(db, p->pWhere); - sqlite3ExprListDelete(db, p->pGroupBy); - sqlite3ExprDelete(db, p->pHaving); - sqlite3ExprListDelete(db, p->pOrderBy); - sqlite3SelectDelete(db, p->pPrior); - sqlite3ExprDelete(db, p->pLimit); - sqlite3ExprDelete(db, p->pOffset); - sqlite3WithDelete(db, p->pWith); +static void clearSelect(sqlite3 *db, Select *p, int bFree){ + while( p ){ + Select *pPrior = p->pPrior; + sqlite3ExprListDelete(db, p->pEList); + sqlite3SrcListDelete(db, p->pSrc); + sqlite3ExprDelete(db, p->pWhere); + sqlite3ExprListDelete(db, p->pGroupBy); + sqlite3ExprDelete(db, p->pHaving); + sqlite3ExprListDelete(db, p->pOrderBy); + sqlite3ExprDelete(db, p->pLimit); + sqlite3ExprDelete(db, p->pOffset); + sqlite3WithDelete(db, p->pWith); + if( bFree ) sqlite3DbFree(db, p); + p = pPrior; + bFree = 1; + } } /* ** Initialize a SelectDest structure. */ @@ -128,12 +133,11 @@ pNew->pOffset = pOffset; assert( pOffset==0 || pLimit!=0 ); pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; if( db->mallocFailed ) { - clearSelect(db, pNew); - if( pNew!=&standin ) sqlite3DbFree(db, pNew); + clearSelect(db, pNew, pNew!=&standin); pNew = 0; }else{ assert( pNew->pSrc!=0 || pParse->nErr>0 ); } assert( pNew!=&standin ); @@ -154,14 +158,11 @@ /* ** Delete the given Select structure and all of its substructures. */ void sqlite3SelectDelete(sqlite3 *db, Select *p){ - if( p ){ - clearSelect(db, p); - sqlite3DbFree(db, p); - } + clearSelect(db, p, 1); } /* ** Return a pointer to the right-most SELECT statement in a compound. */ @@ -2073,10 +2074,70 @@ Parse *pParse, /* Parsing context */ Select *p, /* The right-most of SELECTs to be coded */ SelectDest *pDest /* What to do with query results */ ); +/* +** Error message for when two or more terms of a compound select have different +** size result sets. +*/ +static void selectWrongNumTermsError(Parse *pParse, Select *p){ + if( p->selFlags & SF_Values ){ + sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); + }else{ + sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" + " do not have the same number of result columns", selectOpName(p->op)); + } +} + +/* +** Handle the special case of a compound-select that originates from a +** VALUES clause. By handling this as a special case, we avoid deep +** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT +** on a VALUES clause. +** +** Because the Select object originates from a VALUES clause: +** (1) It has no LIMIT or OFFSET +** (2) All terms are UNION ALL +** (3) There is no ORDER BY clause +*/ +static int multiSelectValues( + Parse *pParse, /* Parsing context */ + Select *p, /* The right-most of SELECTs to be coded */ + SelectDest *pDest /* What to do with query results */ +){ + Select *pPrior; + int nExpr = p->pEList->nExpr; + int nRow = 1; + int rc = 0; + assert( p->pNext==0 ); + assert( p->selFlags & SF_AllValues ); + do{ + assert( p->selFlags & SF_Values ); + assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); + assert( p->pLimit==0 ); + assert( p->pOffset==0 ); + if( p->pEList->nExpr!=nExpr ){ + selectWrongNumTermsError(pParse, p); + return 1; + } + if( p->pPrior==0 ) break; + assert( p->pPrior->pNext==p ); + p = p->pPrior; + nRow++; + }while(1); + while( p ){ + pPrior = p->pPrior; + p->pPrior = 0; + rc = sqlite3Select(pParse, p, pDest); + p->pPrior = pPrior; + if( rc ) break; + p->nSelectRow = nRow; + p = p->pNext; + } + return rc; +} /* ** This routine is called to process a compound query form from ** two or more separate queries using UNION, UNION ALL, EXCEPT, or ** INTERSECT @@ -2153,22 +2214,24 @@ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } + + /* Special handling for a compound-select that originates as a VALUES clause. + */ + if( p->selFlags & SF_AllValues ){ + rc = multiSelectValues(pParse, p, &dest); + goto multi_select_end; + } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ - if( p->selFlags & SF_Values ){ - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); - }else{ - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" - " do not have the same number of result columns", selectOpName(p->op)); - } + selectWrongNumTermsError(pParse, p); rc = 1; goto multi_select_end; } #ifndef SQLITE_OMIT_CTE @@ -4050,11 +4113,13 @@ if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ return WRC_Prune; } pTabList = p->pSrc; pEList = p->pEList; - sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); + if( pWalker->xSelectCallback2==selectPopWith ){ + sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); + } /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. */ sqlite3SrcListAssignCursors(pParse, pTabList); @@ -4341,11 +4406,13 @@ if( pParse->hasCompound ){ w.xSelectCallback = convertCompoundSelectToSubquery; sqlite3WalkSelect(&w, pSelect); } w.xSelectCallback = selectExpander; - w.xSelectCallback2 = selectPopWith; + if( (pSelect->selFlags & SF_AllValues)==0 ){ + w.xSelectCallback2 = selectPopWith; + } sqlite3WalkSelect(&w, pSelect); } #ifndef SQLITE_OMIT_SUBQUERY Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -15,10 +15,17 @@ #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) /* This needs to come before any includes for MSVC compiler */ #define _CRT_SECURE_NO_WARNINGS #endif +/* +** If requested, include the SQLite compiler options file for MSVC. +*/ +#if defined(INCLUDE_MSVC_H) +#include "msvc.h" +#endif + /* ** Enable large-file support for fopen() and friends on unix. */ #ifndef SQLITE_DISABLE_LFS # define _LARGE_FILE 1 @@ -46,21 +53,20 @@ # endif # include # include #endif -#if defined(HAVE_READLINE) && HAVE_READLINE!=0 +#if HAVE_READLINE # include # include -#else -# undef HAVE_READLINE #endif -#if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE) +#if HAVE_EDITLINE +# undef HAVE_READLINE # define HAVE_READLINE 1 # include #endif -#if !defined(HAVE_READLINE) +#if !HAVE_READLINE # define add_history(X) # define read_history(X) # define write_history(X) # define stifle_history(X) #endif @@ -423,11 +429,11 @@ char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; -#if defined(HAVE_READLINE) +#if HAVE_READLINE free(zPrior); zResult = readline(zPrompt); if( zResult && *zResult ) add_history(zResult); #else printf("%s", zPrompt); @@ -469,15 +475,15 @@ int mode; /* An output mode setting */ int writableSchema; /* True if PRAGMA writable_schema=ON */ int showHeader; /* True to show column names in List or Column mode */ unsigned shellFlgs; /* Various flags */ char *zDestTable; /* Name of destination table when MODE_Insert */ - char separator[20]; /* Separator character for MODE_List */ - char newline[20]; /* Record separator in MODE_Csv */ + char colSeparator[20]; /* Column separator character for several modes */ + char rowSeparator[20]; /* Row separator character for MODE_Ascii */ int colWidth[100]; /* Requested width of each column when in column mode*/ int actualWidth[100]; /* Actual width of each column */ - char nullvalue[20]; /* The text to print when a NULL comes back from + char nullValue[20]; /* The text to print when a NULL comes back from ** the database */ SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ char *zFreeOnClose; /* Filename to free when closing */ @@ -506,10 +512,11 @@ #define MODE_Html 4 /* Generate an XHTML table */ #define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Csv 7 /* Quote strings, numbers are plain */ #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ +#define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ static const char *modeDescr[] = { "line", "column", "list", @@ -517,12 +524,26 @@ "html", "insert", "tcl", "csv", "explain", + "ascii", }; +/* +** These are the column/row/line separators used by the various +** import/export modes. +*/ +#define SEP_Column "|" +#define SEP_Row "\n" +#define SEP_Tab "\t" +#define SEP_Space " " +#define SEP_Comma "," +#define SEP_CrLf "\r\n" +#define SEP_Unit "\x1F" +#define SEP_Record "\x1E" + /* ** Number of elements in an array */ #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) @@ -675,26 +696,26 @@ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; /* -** Output a single term of CSV. Actually, p->separator is used for -** the separator, which may or may not be a comma. p->nullvalue is +** Output a single term of CSV. Actually, p->colSeparator is used for +** the separator, which may or may not be a comma. p->nullValue is ** the null value. Strings are quoted if necessary. The separator ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ - fprintf(out,"%s",p->nullvalue); + fprintf(out,"%s",p->nullValue); }else{ int i; - int nSep = strlen30(p->separator); + int nSep = strlen30(p->colSeparator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] - || (z[i]==p->separator[0] && - (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ + || (z[i]==p->colSeparator[0] && + (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ i = 0; break; } } if( i==0 ){ @@ -707,11 +728,11 @@ }else{ fprintf(out, "%s", z); } } if( bSep ){ - fprintf(p->out, "%s", p->separator); + fprintf(p->out, "%s", p->colSeparator); } } #ifdef SIGINT /* @@ -745,14 +766,14 @@ if( azArg==0 ) break; for(i=0; iw ) w = len; } - if( p->cnt++>0 ) fprintf(p->out,"\n"); + if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); for(i=0; iout,"%*s = %s\n", w, azCol[i], - azArg[i] ? azArg[i] : p->nullvalue); + fprintf(p->out,"%*s = %s%s", w, azCol[i], + azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } case MODE_Explain: case MODE_Column: { @@ -765,21 +786,23 @@ w = 0; } if( w==0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; - n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); + n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); if( wactualWidth) ){ p->actualWidth[i] = w; } if( p->showHeader ){ if( w<0 ){ - fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); + fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], + i==nArg-1 ? p->rowSeparator : " "); }else{ - fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); + fprintf(p->out,"%-*.*s%s",w,w,azCol[i], + i==nArg-1 ? p->rowSeparator : " "); } } } if( p->showHeader ){ for(i=0; iout,"%-*.*s%s",w,w,"-----------------------------------" "----------------------------------------------------------", - i==nArg-1 ? "\n": " "); + i==nArg-1 ? p->rowSeparator : " "); } } } if( azArg==0 ) break; for(i=0; iiIndent++; } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + azArg[i] ? azArg[i] : p->nullValue, + i==nArg-1 ? p->rowSeparator : " "); }else{ fprintf(p->out,"%-*.*s%s",w,w, - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + azArg[i] ? azArg[i] : p->nullValue, + i==nArg-1 ? p->rowSeparator : " "); } } break; } case MODE_Semi: case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); + fprintf(p->out,"%s%s",azCol[i], + i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; inullvalue; + if( z==0 ) z = p->nullValue; fprintf(p->out, "%s", z); if( iout, "%s", p->separator); + fprintf(p->out, "%s", p->colSeparator); }else if( p->mode==MODE_Semi ){ - fprintf(p->out, ";\n"); + fprintf(p->out, ";%s", p->rowSeparator); }else{ - fprintf(p->out, "\n"); + fprintf(p->out, "%s", p->rowSeparator); } } break; } case MODE_Html: { @@ -857,30 +883,30 @@ } if( azArg==0 ) break; fprintf(p->out,""); for(i=0; iout,""); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); + output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); fprintf(p->out,"\n"); } fprintf(p->out,"\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, "%s", p->separator); + if(iout, "%s", p->colSeparator); } - fprintf(p->out,"\n"); + fprintf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullvalue); - if(iout, "%s", p->separator); + output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); + if(iout, "%s", p->colSeparator); } - fprintf(p->out,"\n"); + fprintf(p->out, "%s", p->rowSeparator); break; } case MODE_Csv: { #if defined(WIN32) || defined(_WIN32) fflush(p->out); @@ -888,17 +914,17 @@ #endif if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s",p->newline); + fprintf(p->out, "%s", p->rowSeparator); } if( nArg>0 ){ for(i=0; iout,"%s",p->newline); + fprintf(p->out, "%s", p->rowSeparator); } #if defined(WIN32) || defined(_WIN32) fflush(p->out); _setmode(_fileno(p->out), _O_TEXT); #endif @@ -930,10 +956,26 @@ output_quoted_string(p->out, azArg[i]); } } fprintf(p->out,");\n"); break; + } + case MODE_Ascii: { + if( p->cnt++==0 && p->showHeader ){ + for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); + fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); + } + fprintf(p->out, "%s", p->rowSeparator); + } + if( azArg==0 ) break; + for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); + fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + } + fprintf(p->out, "%s", p->rowSeparator); + break; } } return 0; } @@ -1696,16 +1738,17 @@ #ifndef SQLITE_OMIT_LOAD_EXTENSION ".load FILE ?ENTRY? Load an extension library\n" #endif ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" + " ascii Columns/rows delimited by 0x1F and 0x1E\n" " csv Comma-separated values\n" " column Left-aligned columns. (See .width)\n" " html HTML code\n" " insert SQL insert statements for TABLE\n" " line One value per line\n" - " list Values delimited by .separator string\n" + " list Values delimited by .separator strings\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" ".nullvalue STRING Use STRING in place of NULL values\n" ".once FILENAME Output for the next SQL command only to FILENAME\n" ".open ?FILENAME? Close existing database and reopen FILENAME\n" @@ -1718,12 +1761,12 @@ ".save FILE Write in-memory database into FILE\n" ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" ".schema ?TABLE? Show the CREATE statements\n" " If TABLE specified, only show tables matching\n" " LIKE pattern TABLE.\n" - ".separator STRING ?NL? Change separator used by output mode and .import\n" - " NL is the end-of-line mark for CSV\n" + ".separator COL ?ROW? Change the column separator and optionally the row\n" + " separator for both the output mode and .import\n" ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats on|off Turn stats on or off\n" ".system CMD ARGS... Run CMD ARGS... in a system shell\n" ".tables ?TABLE? List names of tables\n" @@ -2000,26 +2043,27 @@ static int nCall = 0; nCall++; } /* -** An object used to read a CSV file +** An object used to read a CSV and other files for import. */ -typedef struct CSVReader CSVReader; -struct CSVReader { +typedef struct ImportCtx ImportCtx; +struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ char *z; /* Accumulated text for a field */ int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ int cTerm; /* Character that terminated the most recent field */ - int cSeparator; /* The separator character. (Usually ",") */ + int cColSep; /* The column separator character. (Usually ",") */ + int cRowSep; /* The row separator character. (Usually "\n") */ }; /* Append a single byte to z[] */ -static void csv_append_char(CSVReader *p, int c){ +static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; p->z = sqlite3_realloc(p->z, p->nAlloc); if( p->z==0 ){ fprintf(stderr, "out of memory\n"); @@ -2033,41 +2077,44 @@ ** with the option of having a separator other than ",". ** ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc(). -** + Use p->cSep as the separator. The default is ",". +** + Use p->cSep as the column separator. The default is ",". +** + Use p->rSep as the row separator. The default is "\n". ** + Keep track of the line number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. ** + Report syntax errors on stderr */ -static char *csv_read_one_field(CSVReader *p){ - int c, pc, ppc; - int cSep = p->cSeparator; +static char *csv_read_one_field(ImportCtx *p){ + int c; + int cSep = p->cColSep; + int rSep = p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; } if( c=='"' ){ + int pc, ppc; int startLine = p->nLine; int cQuote = c; pc = ppc = 0; while( 1 ){ c = fgetc(p->in); - if( c=='\n' ) p->nLine++; + if( c==rSep ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ pc = 0; continue; } } if( (c==cSep && pc==cQuote) - || (c=='\n' && pc==cQuote) - || (c=='\n' && pc=='\r' && ppc==cQuote) + || (c==rSep && pc==cQuote) + || (c==rSep && pc=='\r' && ppc==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); p->cTerm = c; break; @@ -2077,31 +2124,65 @@ p->zFile, p->nLine, cQuote); } if( c==EOF ){ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); - p->cTerm = EOF; + p->cTerm = c; break; } - csv_append_char(p, c); + import_append_char(p, c); ppc = pc; pc = c; } }else{ - while( c!=EOF && c!=cSep && c!='\n' ){ - csv_append_char(p, c); + while( c!=EOF && c!=cSep && c!=rSep ){ + import_append_char(p, c); c = fgetc(p->in); } - if( c=='\n' ){ + if( c==rSep ){ p->nLine++; if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; } p->cTerm = c; } if( p->z ) p->z[p->n] = 0; return p->z; } + +/* Read a single field of ASCII delimited text. +** +** + Input comes from p->in. +** + Store results in p->z of length p->n. Space to hold p->z comes +** from sqlite3_malloc(). +** + Use p->cSep as the column separator. The default is "\x1F". +** + Use p->rSep as the row separator. The default is "\x1E". +** + Keep track of the row number in p->nLine. +** + Store the character that terminates the field in p->cTerm. Store +** EOF on end-of-file. +** + Report syntax errors on stderr +*/ +static char *ascii_read_one_field(ImportCtx *p){ + int c; + int cSep = p->cColSep; + int rSep = p->cRowSep; + p->n = 0; + c = fgetc(p->in); + if( c==EOF || seenInterrupt ){ + p->cTerm = EOF; + return 0; + } + while( c!=EOF && c!=cSep && c!=rSep ){ + import_append_char(p, c); + c = fgetc(p->in); + } + if( c==rSep ){ + p->nLine++; + } + p->cTerm = c; + if( p->z ) p->z[p->n] = 0; + return p->z; +} /* ** Try to transfer data for table zTable. If an error is seen while ** moving forward, try to go backwards. The backwards movement won't ** work for WITHOUT ROWID tables. @@ -2653,100 +2734,125 @@ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ - int nSep; /* Number of bytes in p->separator[] */ + int nSep; /* Number of bytes in p->colSeparator[] */ char *zSql; /* An SQL statement */ - CSVReader sCsv; /* Reader context */ + ImportCtx sCtx; /* Reader context */ + char *(*xRead)(ImportCtx*); /* Procedure to read one value */ int (*xCloser)(FILE*); /* Procedure to close th3 connection */ if( nArg!=3 ){ fprintf(stderr, "Usage: .import FILE TABLE\n"); goto meta_command_exit; } zFile = azArg[1]; zTable = azArg[2]; seenInterrupt = 0; - memset(&sCsv, 0, sizeof(sCsv)); + memset(&sCtx, 0, sizeof(sCtx)); open_db(p, 0); - nSep = strlen30(p->separator); + nSep = strlen30(p->colSeparator); + if( nSep==0 ){ + fprintf(stderr, "Error: non-null column separator required for import\n"); + return 1; + } + if( nSep>1 ){ + fprintf(stderr, "Error: multi-character column separators not allowed" + " for import\n"); + return 1; + } + nSep = strlen30(p->rowSeparator); if( nSep==0 ){ - fprintf(stderr, "Error: non-null separator required for import\n"); + fprintf(stderr, "Error: non-null row separator required for import\n"); return 1; + } + if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ + /* When importing CSV (only), if the row separator is set to the + ** default output row separator, change it to the default input + ** row separator. This avoids having to maintain different input + ** and output row separators. */ + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); + nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ - fprintf(stderr, "Error: multi-character separators not allowed" + fprintf(stderr, "Error: multi-character row separators not allowed" " for import\n"); return 1; } - sCsv.zFile = zFile; - sCsv.nLine = 1; - if( sCsv.zFile[0]=='|' ){ - sCsv.in = popen(sCsv.zFile+1, "r"); - sCsv.zFile = ""; + sCtx.zFile = zFile; + sCtx.nLine = 1; + if( sCtx.zFile[0]=='|' ){ + sCtx.in = popen(sCtx.zFile+1, "r"); + sCtx.zFile = ""; xCloser = pclose; }else{ - sCsv.in = fopen(sCsv.zFile, "rb"); + sCtx.in = fopen(sCtx.zFile, "rb"); xCloser = fclose; } - if( sCsv.in==0 ){ + if( p->mode==MODE_Ascii ){ + xRead = ascii_read_one_field; + }else{ + xRead = csv_read_one_field; + } + if( sCtx.in==0 ){ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); return 1; } - sCsv.cSeparator = p->separator[0]; + sCtx.cColSep = p->colSeparator[0]; + sCtx.cRowSep = p->rowSeparator[0]; zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } nByte = strlen30(zSql); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */ + import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); char cSep = '('; - while( csv_read_one_field(&sCsv) ){ - zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); + while( xRead(&sCtx) ){ + zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); cSep = ','; - if( sCsv.cTerm!=sCsv.cSeparator ) break; + if( sCtx.cTerm!=sCtx.cColSep ) break; } if( cSep=='(' ){ sqlite3_free(zCreate); - sqlite3_free(sCsv.z); - xCloser(sCsv.in); - fprintf(stderr,"%s: empty file\n", sCsv.zFile); + sqlite3_free(sCtx.z); + xCloser(sCtx.in); + fprintf(stderr,"%s: empty file\n", sCtx.zFile); return 1; } zCreate = sqlite3_mprintf("%z\n)", zCreate); rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); sqlite3_free(zCreate); if( rc ){ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, sqlite3_errmsg(db)); - sqlite3_free(sCsv.z); - xCloser(sCsv.in); + sqlite3_free(sCtx.z); + xCloser(sCtx.in); return 1; } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); } sqlite3_free(zSql); if( rc ){ if (pStmt) sqlite3_finalize(pStmt); fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } nCol = sqlite3_column_count(pStmt); sqlite3_finalize(pStmt); pStmt = 0; if( nCol==0 ) return 0; /* no columns, no error */ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); if( zSql==0 ){ fprintf(stderr, "Error: out of memory\n"); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); j = strlen30(zSql); for(i=1; idb, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc ){ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); if (pStmt) sqlite3_finalize(pStmt); - xCloser(sCsv.in); + xCloser(sCtx.in); return 1; } needCommit = sqlite3_get_autocommit(db); if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); do{ - int startLine = sCsv.nLine; + int startLine = sCtx.nLine; for(i=0; imode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); - if( i=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ - fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, + fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(db)); } } - }while( sCsv.cTerm!=EOF ); + }while( sCtx.cTerm!=EOF ); - xCloser(sCsv.in); - sqlite3_free(sCsv.z); + xCloser(sCtx.in); + sqlite3_free(sCtx.z); sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); }else if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){ @@ -2915,32 +3031,36 @@ p->mode = MODE_List; }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ p->mode = MODE_Html; }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; - sqlite3_snprintf(sizeof(p->separator), p->separator, " "); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; - sqlite3_snprintf(sizeof(p->separator), p->separator, ","); - sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ p->mode = MODE_List; - sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, nArg>=3 ? azArg[2] : "table"); + }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ + p->mode = MODE_Ascii; + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); }else { fprintf(stderr,"Error: mode should be one of: " - "column csv html insert line list tabs tcl\n"); + "ascii column csv html insert line list tabs tcl\n"); rc = 1; } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ - sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, - "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); + sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, + "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ fprintf(stderr, "Usage: .nullvalue STRING\n"); rc = 1; } }else @@ -3221,18 +3341,20 @@ }else #endif if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ - fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); + fprintf(stderr, "Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ - sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, + "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); } if( nArg>=3 ){ - sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, + "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); } }else if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) @@ -3259,27 +3381,28 @@ if( nArg!=1 ){ fprintf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } - fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); - fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off"); + fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); + fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); - fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); - fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); - fprintf(p->out,"%9.9s: ", "nullvalue"); - output_c_string(p->out, p->nullvalue); + fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); + fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); + fprintf(p->out,"%12.12s: ", "nullvalue"); + output_c_string(p->out, p->nullValue); fprintf(p->out, "\n"); - fprintf(p->out,"%9.9s: %s\n","output", + fprintf(p->out,"%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); - fprintf(p->out,"%9.9s: ", "separator"); - output_c_string(p->out, p->separator); - fprintf(p->out," "); - output_c_string(p->out, p->newline); + fprintf(p->out,"%12.12s: ", "colseparator"); + output_c_string(p->out, p->colSeparator); + fprintf(p->out, "\n"); + fprintf(p->out,"%12.12s: ", "rowseparator"); + output_c_string(p->out, p->rowSeparator); fprintf(p->out, "\n"); - fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); - fprintf(p->out,"%9.9s: ","width"); + fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off"); + fprintf(p->out,"%12.12s: ","width"); for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { fprintf(p->out,"%d ",p->colWidth[i]); } fprintf(p->out,"\n"); }else @@ -3936,10 +4059,11 @@ /* ** Show available command line options */ static const char zOptions[] = + " -ascii set output mode to 'ascii'\n" " -bail stop after hitting an error\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" @@ -3957,15 +4081,15 @@ " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif - " -newline SEP set newline character(s) for CSV\n" + " -newline SEP set output row separator. Default: '\\n'\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" - " -separator SEP set output field separator. Default: '|'\n" + " -separator SEP set output column separator. Default: '|'\n" " -stats print memory stats before each finalize\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" @@ -3988,12 +4112,12 @@ ** Initialize the state information in data */ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; - memcpy(data->separator,"|", 2); - memcpy(data->newline,"\r\n", 3); + memcpy(data->colSeparator,SEP_Column, 2); + memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); @@ -4228,19 +4352,25 @@ data.mode = MODE_Line; }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; }else if( strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; - memcpy(data.separator,",",2); + memcpy(data.colSeparator,",",2); + }else if( strcmp(z,"-ascii")==0 ){ + data.mode = MODE_Ascii; + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, + SEP_Unit); + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, + SEP_Record); }else if( strcmp(z,"-separator")==0 ){ - sqlite3_snprintf(sizeof(data.separator), data.separator, + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-newline")==0 ){ - sqlite3_snprintf(sizeof(data.newline), data.newline, + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ - sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, + sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; @@ -4356,11 +4486,11 @@ nHistory = strlen30(zHome) + 20; if( (zHistory = malloc(nHistory))!=0 ){ sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); } } -#if defined(HAVE_READLINE) +#if HAVE_READLINE if( zHistory ) read_history(zHistory); #endif rc = process_input(&data, 0); if( zHistory ){ stifle_history(100); Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -1743,10 +1743,21 @@ **
^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which ** is a pointer to an integer and writes into that integer the number of extra ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. ** The amount of extra space required can change depending on the compiler, ** target platform, and SQLite version. +** +** [[SQLITE_CONFIG_PMASZ]] +**
SQLITE_CONFIG_PMASZ +**
^The SQLITE_CONFIG_PMASZ option takes a single parameter which +** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded +** sorter to that integer. The default minimum PMA Size is set by the +** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched +** to help with sort operations when multithreaded sorting +** is enabled (using the [PRAGMA threads] command) and the amount of content +** to be sorted exceeds the page size times the minimum of the +** [PRAGMA cache_size] setting and this value. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ @@ -1769,10 +1780,11 @@ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ +#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that @@ -7472,10 +7484,14 @@ ** ** The following constants can be used for the T parameter to the ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a ** different metric for sqlite3_stmt_scanstatus() to return. ** +** When the value returned to V is a string, space to hold that string is +** managed by the prepared statement S and will be automatically freed when +** S is finalized. +** **
** [[SQLITE_SCANSTAT_NLOOP]]
SQLITE_SCANSTAT_NLOOP
**
^The [sqlite3_int64] variable pointed to by the T parameter will be ** set to the total number of times that the X-th loop has run.
** @@ -7517,11 +7533,18 @@ #define SQLITE_SCANSTAT_SELECTID 5 /* ** CAPI3REF: Prepared Statement Scan Status ** -** Return status data for a single loop within query pStmt. +** This interface returns information about the predicted and measured +** performance for pStmt. Advanced applications can use this +** interface to compare the predicted and the measured performance and +** issue warnings and/or rerun [ANALYZE] if discrepancies are found. +** +** Since this interface is expected to be rarely used, it is only +** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] +** compile-time option. ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior ** of this interface is undefined. ** ^The requested measurement is written into a variable pointed to by @@ -7535,13 +7558,10 @@ ** ^Statistics might not be available for all loops in all statements. ^In cases ** where there exist loops with no available statistics, this function behaves ** as if the loop did not exist - it returns non-zero and leave the variable ** that pOut points to unchanged. ** -** This API is only available if the library is built with pre-processor -** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. -** ** See also: [sqlite3_stmt_scanstatus_reset()] */ SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ int idx, /* Index of loop to report on */ Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -13,10 +13,18 @@ ** */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ +/* +** Include the header file used to customize the compiler options for MSVC. +** This should be done first so that it can successfully prevent spurious +** compiler warnings due to subsequent content in this file and other files +** that are included by this file. +*/ +#include "msvc.h" + /* ** These #defines should enable >2GB file support on POSIX if the ** underlying operating system supports it. If the OS lacks ** large file support, or if the OS is windows, these should be no-ops. ** @@ -2347,11 +2355,11 @@ #define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ #define SF_Compound 0x0040 /* Part of a compound query */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ - /* 0x0100 NOT USED */ +#define SF_AllValues 0x0100 /* All terms of compound are VALUES */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ @@ -2837,10 +2845,11 @@ void *pPage; /* Page cache memory */ int szPage; /* Size of each page in pPage[] */ int nPage; /* Number of pages in pPage[] */ int mxParserStack; /* maximum depth of the parser stack */ int sharedCacheEnabled; /* true if shared-cache mode enabled */ + u32 szPma; /* Maximum Sorter PMA size */ /* The above might be initialized to non-zero. The following need to always ** initially be zero, however. */ int isInit; /* True after initialization has finished */ int inProgress; /* True while initialization in progress */ int isMutexInit; /* True after mutexes are initialized */ @@ -2974,11 +2983,11 @@ ** FTS4 is really an extension for FTS3. It is enabled using the ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. */ #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) -# define SQLITE_ENABLE_FTS3 +# define SQLITE_ENABLE_FTS3 1 #endif /* ** The ctype.h header is needed for non-ASCII systems. It is also ** needed by FTS3 when FTS3 is included in the amalgamation. Index: src/tclsqlite.c ================================================================== --- src/tclsqlite.c +++ src/tclsqlite.c @@ -23,10 +23,18 @@ ** ** -DSQLITE_TEST When used in conjuction with -DTCLSH=1, add ** hundreds of new commands used for testing ** SQLite. This option implies -DSQLITE_TCLMD5. */ + +/* +** If requested, include the SQLite compiler options file for MSVC. +*/ +#if defined(INCLUDE_MSVC_H) +#include "msvc.h" +#endif + #include "tcl.h" #include /* ** Some additional include files are needed if this file is not @@ -1083,14 +1091,14 @@ char const *zIn, /* SQL to compile */ char const **pzOut, /* OUT: Pointer to next SQL statement */ SqlPreparedStmt **ppPreStmt /* OUT: Object used to cache statement */ ){ const char *zSql = zIn; /* Pointer to first SQL statement in zIn */ - sqlite3_stmt *pStmt; /* Prepared statement object */ + sqlite3_stmt *pStmt = 0; /* Prepared statement object */ SqlPreparedStmt *pPreStmt; /* Pointer to cached statement */ int nSql; /* Length of zSql in bytes */ - int nVar; /* Number of variables in statement */ + int nVar = 0; /* Number of variables in statement */ int iParm = 0; /* Next free entry in apParm */ char c; int i; Tcl_Interp *interp = pDb->interp; @@ -3100,11 +3108,11 @@ ** for additional information. ** ** The EXTERN macros are required by TCL in order to work on windows. */ EXTERN int Sqlite3_Init(Tcl_Interp *interp){ - int rc = Tcl_InitStubs(interp, "8.4", 0)==0 ? TCL_ERROR : TCL_OK; + int rc = Tcl_InitStubs(interp, "8.4", 0) ? TCL_OK : TCL_ERROR; if( rc==TCL_OK ){ Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0); #ifndef SQLITE_3_SUFFIX_ONLY /* The "sqlite" alias is undocumented. It is here only to support ** legacy scripts. All new scripts should use only the "sqlite3" @@ -3746,11 +3754,10 @@ extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); - extern int Fts5tcl_Init(Tcl_Interp *); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); #endif @@ -3790,11 +3797,10 @@ Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); - Fts5tcl_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); #endif @@ -3812,10 +3818,15 @@ Sqlitetestsse_Init(interp); #endif } #endif } + +/* Needed for the setrlimit() system call on unix */ +#if defined(unix) +#include +#endif #define TCLSH_MAIN main /* Needed to fake out mktclapp */ int TCLSH_MAIN(int argc, char **argv){ Tcl_Interp *interp; @@ -3825,10 +3836,21 @@ "attach debugger to process %d and press any key to continue.\n", GETPID()); fgetc(stdin); } #endif + + /* Since the primary use case for this binary is testing of SQLite, + ** be sure to generate core files if we crash */ +#if defined(SQLITE_TEST) && defined(unix) + { struct rlimit x; + getrlimit(RLIMIT_CORE, &x); + x.rlim_cur = x.rlim_max; + setrlimit(RLIMIT_CORE, &x); + } +#endif /* SQLITE_TEST && unix */ + /* Call sqlite3_shutdown() once before doing anything else. This is to ** test that sqlite3_shutdown() can be safely called by a process before ** sqlite3_initialize() is. */ sqlite3_shutdown(); Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -259,10 +259,12 @@ /* ** Usage: clang_sanitize_address ** ** Returns true if the program was compiled using clang with the ** -fsanitize=address switch on the command line. False otherwise. +** +** Also return true if the OMIT_MISUSE environment variable exists. */ static int clang_sanitize_address( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ @@ -272,10 +274,11 @@ #if defined(__has_feature) # if __has_feature(address_sanitizer) res = 1; # endif #endif + if( res==0 && getenv("OMIT_MISUSE")!=0 ) res = 1; Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); return TCL_OK; } /* @@ -562,11 +565,11 @@ ){ sqlite3 *db; Tcl_DString str; int rc; char *zErr = 0; - int nRow, nCol; + int nRow = 0, nCol = 0; char **aResult; int i; char zBuf[30]; char *zSql; int resCount = -1; @@ -2115,11 +2118,11 @@ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int iValue; - int i, op, resetFlag; + int i, op = 0, resetFlag; const char *zOpName; sqlite3_stmt *pStmt; static const struct { const char *zName; @@ -3093,11 +3096,11 @@ int objc, Tcl_Obj *CONST objv[] ){ sqlite3_stmt *pStmt; int idx; - double value; + double value = 0; int rc; const char *zVal; int i; static const struct { const char *zName; /* Name of the special floating point value */ @@ -5453,11 +5456,11 @@ /* Out of range test cases */ { "SQLITE_LIMIT_TOOSMALL", -1, }, { "SQLITE_LIMIT_TOOBIG", SQLITE_LIMIT_WORKER_THREADS+1 }, }; - int i, id; + int i, id = 0; int val; const char *zId; if( objc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", @@ -6602,10 +6605,69 @@ Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* SQLITE_USER_AUTHENTICATION */ +/* +** tclcmd: bad_behavior TYPE +** +** Do some things that should trigger a valgrind or -fsanitize=undefined +** warning. This is used to verify that errors and warnings output by those +** tools are detected by the test scripts. +** +** TYPE BEHAVIOR +** 1 Overflow a signed integer +** 2 Jump based on an uninitialized variable +** 3 Read after free +** 4 Panic +*/ +static int test_bad_behavior( + ClientData clientData, /* Pointer to an integer containing zero */ + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int objc, /* Number of arguments */ + Tcl_Obj *CONST objv[] /* Command arguments */ +){ + int iType; + int xyz; + int i = *(int*)clientData; + int j; + int w[10]; + int *a; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "TYPE"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &iType) ) return TCL_ERROR; + switch( iType ){ + case 1: { + xyz = 0x7fffff00 - i; + xyz += 0x100; + Tcl_SetObjResult(interp, Tcl_NewIntObj(xyz)); + break; + } + case 2: { + w[1] = 5; + if( w[i]>0 ) w[1]++; + Tcl_SetObjResult(interp, Tcl_NewIntObj(w[1])); + break; + } + case 3: { + a = malloc( sizeof(int)*10 ); + for(j=0; j<10; j++) a[j] = j; + free(a); + Tcl_SetObjResult(interp, Tcl_NewIntObj(a[i])); + break; + } + case 4: { + Tcl_Panic("Deliberate panic"); + break; + } + } + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; @@ -6618,10 +6680,11 @@ extern int sqlite3_hostid_num; #endif extern int sqlite3_max_blobsize; extern int sqlite3BtreeSharedCacheReport(void*, Tcl_Interp*,int,Tcl_Obj*CONST*); + static int iZero = 0; static struct { char *zName; Tcl_CmdProc *xProc; } aCmd[] = { { "db_enter", (Tcl_CmdProc*)db_enter }, @@ -6670,10 +6733,11 @@ static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { + { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, { "sqlite3_bind_int64", test_bind_int64, 0 }, { "sqlite3_bind_double", test_bind_double, 0 }, Index: src/test2.c ================================================================== --- src/test2.c +++ src/test2.c @@ -308,11 +308,11 @@ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Pager *pPager; char zBuf[100]; - DbPage *pPage; + DbPage *pPage = 0; int pgno; int rc; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID PGNO\"", 0); Index: src/test8.c ================================================================== --- src/test8.c +++ src/test8.c @@ -646,16 +646,16 @@ ** in echoBestIndex(), idxNum is set to the corresponding hash value. ** In echoFilter(), code assert()s that the supplied idxNum value is ** indeed the hash of the supplied idxStr. */ static int hashString(const char *zString){ - int val = 0; + u32 val = 0; int ii; for(ii=0; zString[ii]; ii++){ val = (val << 3) + (int)zString[ii]; } - return val; + return (int)(val&0x7fffffff); } /* ** Echo virtual table module xFilter method. */ @@ -775,15 +775,15 @@ const char *zSep = "WHERE"; echo_vtab *pVtab = (echo_vtab *)tab; sqlite3_stmt *pStmt = 0; Tcl_Interp *interp = pVtab->interp; - int nRow; + int nRow = 0; int useIdx = 0; int rc = SQLITE_OK; int useCost = 0; - double cost; + double cost = 0; int isIgnoreUsable = 0; if( Tcl_GetVar(interp, "echo_module_ignore_usable", TCL_GLOBAL_ONLY) ){ isIgnoreUsable = 1; } @@ -925,11 +925,11 @@ ){ echo_vtab *pVtab = (echo_vtab *)tab; sqlite3 *db = pVtab->db; int rc = SQLITE_OK; - sqlite3_stmt *pStmt; + sqlite3_stmt *pStmt = 0; char *z = 0; /* SQL statement to execute */ int bindArgZero = 0; /* True to bind apData[0] to sql var no. nData */ int bindArgOne = 0; /* True to bind apData[1] to sql var no. 1 */ int i; /* Counter variable used by for loops */ Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -39,11 +39,11 @@ ** This routine sets entries in the global ::sqlite_options() array variable ** according to the compile-time configuration of the database. Test ** procedures use this to determine when tests should be omitted. */ static void set_options(Tcl_Interp *interp){ -#ifdef HAVE_MALLOC_USABLE_SIZE +#if HAVE_MALLOC_USABLE_SIZE Tcl_SetVar2(interp, "sqlite_options", "malloc_usable_size", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "malloc_usable_size", "0", TCL_GLOBAL_ONLY); @@ -338,16 +338,10 @@ Tcl_SetVar2(interp, "sqlite_options", "fts3", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_ENABLE_FTS5 - Tcl_SetVar2(interp, "sqlite_options", "fts5", "1", TCL_GLOBAL_ONLY); -#else - Tcl_SetVar2(interp, "sqlite_options", "fts5", "0", TCL_GLOBAL_ONLY); -#endif - #if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_DISABLE_FTS3_UNICODE) Tcl_SetVar2(interp, "sqlite_options", "fts3_unicode", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "fts3_unicode", "0", TCL_GLOBAL_ONLY); #endif Index: src/test_journal.c ================================================================== --- src/test_journal.c +++ src/test_journal.c @@ -407,11 +407,13 @@ for(ii=0; rc==SQLITE_OK && ii<(int)pMain->nPage; ii++){ i64 iOff = (i64)(pMain->nPagesize) * (i64)ii; if( iOff==PENDING_BYTE ) continue; rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff); pMain->aCksum[ii] = genCksum(aData, pMain->nPagesize); - if( ii+1==pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ) rc = SQLITE_OK; + if( ii+1==(int)pMain->nPage && rc==SQLITE_IOERR_SHORT_READ ){ + rc = SQLITE_OK; + } } } start_ioerr_simulation(iSave, iSave2); } @@ -548,11 +550,12 @@ ** it needs to fill in the non-locking-region part of the original ** pending-byte page. */ }else{ u32 pgno = (u32)(iOfst/p->nPagesize + 1); - assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); + assert( (iAmt==1||iAmt==(int)p->nPagesize) && + ((iOfst+iAmt)%p->nPagesize)==0 ); assert( pgno<=p->nPage || p->nSync>0 ); assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); } } Index: src/test_malloc.c ================================================================== --- src/test_malloc.c +++ src/test_malloc.c @@ -1258,10 +1258,38 @@ Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } +/* +** Usage: sqlite3_config_pmasz INTEGER +** +** Set the minimum PMA size. +*/ +static int test_config_pmasz( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int rc; + int iPmaSz; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "BOOL"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &iPmaSz) ){ + return TCL_ERROR; + } + + rc = sqlite3_config(SQLITE_CONFIG_PMASZ, iPmaSz); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); + + return TCL_OK; +} + /* ** Usage: sqlite3_dump_memsys3 FILENAME ** sqlite3_dump_memsys5 FILENAME ** @@ -1308,11 +1336,11 @@ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc, iValue, mxValue; - int i, op, resetFlag; + int i, op = 0, resetFlag; const char *zOpName; static const struct { const char *zName; int op; } aOp[] = { @@ -1365,11 +1393,11 @@ Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc, iValue, mxValue; - int i, op, resetFlag; + int i, op = 0, resetFlag; const char *zOpName; sqlite3 *db; extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); static const struct { const char *zName; @@ -1512,10 +1540,11 @@ { "sqlite3_config_memstatus", test_config_memstatus ,0 }, { "sqlite3_config_lookaside", test_config_lookaside ,0 }, { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_config_uri", test_config_uri ,0 }, { "sqlite3_config_cis", test_config_cis ,0 }, + { "sqlite3_config_pmasz", test_config_pmasz ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, { "sqlite3_install_memsys3", test_install_memsys3 ,0 }, { "sqlite3_memdebug_vfs_oom_test", test_vfs_oom_test ,0 }, Index: src/test_multiplex.c ================================================================== --- src/test_multiplex.c +++ src/test_multiplex.c @@ -404,11 +404,11 @@ int argc, sqlite3_value **argv ){ int rc = SQLITE_OK; sqlite3 *db = sqlite3_context_db_handle(context); - int op; + int op = 0; int iVal; if( !db || argc!=2 ){ rc = SQLITE_ERROR; }else{ @@ -533,11 +533,11 @@ if( rc==SQLITE_OK ){ const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0; /* assign pointers to extra space allocated */ memset(pGroup, 0, sz); pMultiplexOpen->pGroup = pGroup; - pGroup->bEnabled = -1; + pGroup->bEnabled = (unsigned char)-1; pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate", (flags & SQLITE_OPEN_MAIN_DB)==0); pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize", SQLITE_MULTIPLEX_CHUNK_SIZE); pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff; Index: src/test_quota.c ================================================================== --- src/test_quota.c +++ src/test_quota.c @@ -887,11 +887,11 @@ /* ** Bring the named file under quota management. Or if it is already under ** management, update its size. */ int sqlite3_quota_file(const char *zFilename){ - char *zFull; + char *zFull = 0; sqlite3_file *fd; int rc; int outFlags = 0; sqlite3_int64 iSize; int nAlloc = gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+2; Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -419,11 +419,11 @@ int rc = SQLITE_OK; TestvfsFd *pFd = tvfsGetFd(pFile); Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_SYNC_MASK ){ - char *zFlags; + char *zFlags = 0; switch( flags ){ case SQLITE_SYNC_NORMAL: zFlags = "normal"; break; @@ -1223,11 +1223,11 @@ ** Where IFAIL is an integer and PERSIST is boolean. */ case CMD_CANTOPENERR: case CMD_IOERR: case CMD_FULLERR: { - TestFaultInject *pTest; + TestFaultInject *pTest = 0; int iRet; switch( aSubcmd[i].eCmd ){ case CMD_IOERR: pTest = &p->ioerr_err; break; case CMD_FULLERR: pTest = &p->full_err; break; Index: src/threads.c ================================================================== --- src/threads.c +++ src/threads.c @@ -24,10 +24,13 @@ ** This interface exists so that applications that want to take advantage ** of multiple cores can do so, while also allowing applications to stay ** single-threaded if desired. */ #include "sqliteInt.h" +#if SQLITE_OS_WIN +# include "os_win.h" +#endif #if SQLITE_MAX_WORKER_THREADS>0 /********************************* Unix Pthreads ****************************/ #if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -15,11 +15,11 @@ ** strings, and stuff like that. ** */ #include "sqliteInt.h" #include -#ifdef SQLITE_HAVE_ISNAN +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN # include #endif /* ** Routine needed to support the testcase() macro. @@ -56,11 +56,11 @@ ** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. ** Otherwise, we have our own implementation that works on most systems. */ int sqlite3IsNaN(double x){ int rc; /* The value return */ -#if !defined(SQLITE_HAVE_ISNAN) +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN /* ** Systems that support the isnan() library function should probably ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have ** found that many systems do not have a working isnan() function so ** this implementation is provided as an alternative. @@ -86,13 +86,13 @@ # error SQLite will not work correctly with the -ffast-math option of GCC. #endif volatile double y = x; volatile double z = y; rc = (y!=z); -#else /* if defined(SQLITE_HAVE_ISNAN) */ +#else /* if HAVE_ISNAN */ rc = isnan(x); -#endif /* SQLITE_HAVE_ISNAN */ +#endif /* HAVE_ISNAN */ testcase( rc ); return rc; } #endif /* SQLITE_OMIT_FLOATING_POINT */ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -3821,12 +3821,12 @@ pIdxKey->default_rc = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ - for(ii=0; iinField; ii++){ + if( pIdxKey->aMem[ii].flags & MEM_Null ){ pc = pOp->p2 - 1; VdbeBranchTaken(1,2); break; } } } Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -150,11 +150,11 @@ /* ** Hard-coded maximum amount of data to accumulate in memory before flushing ** to a level 0 PMA. The purpose of this limit is to prevent various integer ** overflows. 512MiB. */ -#define SQLITE_MAX_MXPMASIZE (1<<29) +#define SQLITE_MAX_PMASZ (1<<29) /* ** Private objects used by the sorter */ typedef struct MergeEngine MergeEngine; /* Merge PMAs together */ @@ -446,15 +446,10 @@ ** ** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } */ #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) -/* The minimum PMA size is set to this value multiplied by the database -** page size in bytes. */ -#ifndef SQLITE_SORTER_PMASZ -# define SQLITE_SORTER_PMASZ 10 -#endif /* Maximum number of PMAs that a single MergeEngine can merge */ #define SORTER_MAX_MERGE_COUNT 16 static int vdbeIncrSwap(IncrMerger*); @@ -849,14 +844,15 @@ SortSubtask *pTask = &pSorter->aTask[i]; pTask->pSorter = pSorter; } if( !sqlite3TempInMemory(db) ){ - pSorter->mnPmaSize = SQLITE_SORTER_PMASZ * pgsz; + u32 szPma = sqlite3GlobalConfig.szPma; + pSorter->mnPmaSize = szPma * pgsz; mxCache = db->aDb[0].pSchema->cache_size; - if( mxCachemxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE); + if( mxCache<(int)szPma ) mxCache = (int)szPma; + pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ); /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary ** large heap allocations. */ @@ -2416,10 +2412,11 @@ rc = vdbePmaReaderNext(pSorter->pReader); *pbEof = (pSorter->pReader->pFd==0); }else #endif /*if( !pSorter->bUseThreads )*/ { + assert( pSorter->pMerger!=0 ); assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); rc = vdbeMergeEngineStep(pSorter->pMerger, pbEof); } }else{ SorterRecord *pFree = pSorter->list.pList; Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -801,25 +801,24 @@ ** The array is cleared after invoking the callbacks. */ static void callFinaliser(sqlite3 *db, int offset){ int i; if( db->aVTrans ){ - VTable **aVTrans = db->aVTrans; - db->aVTrans = 0; for(i=0; inVTrans; i++){ - VTable *pVTab = aVTrans[i]; + VTable *pVTab = db->aVTrans[i]; sqlite3_vtab *p = pVTab->pVtab; if( p ){ int (*x)(sqlite3_vtab *); x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); if( x ) x(p); } pVTab->iSavepoint = 0; sqlite3VtabUnlock(pVTab); } - sqlite3DbFree(db, aVTrans); + sqlite3DbFree(db, db->aVTrans); db->nVTrans = 0; + db->aVTrans = 0; } } /* ** Invoke the xSync method of all virtual tables in the sqlite3.aVTrans Index: test/e_walauto.test ================================================================== --- test/e_walauto.test +++ test/e_walauto.test @@ -16,16 +16,16 @@ set testprefix e_walauto proc read_nbackfill {} { seek $::shmfd 96 - binary scan [read $::shmfd 4] i nBackfill + binary scan [read $::shmfd 4] n nBackfill set nBackfill } proc read_mxframe {} { seek $::shmfd 16 - binary scan [read $::shmfd 4] i mxFrame + binary scan [read $::shmfd 4] n mxFrame set mxFrame } # Assuming that the main db for database handle # Index: test/fuzz2.test ================================================================== --- test/fuzz2.test +++ test/fuzz2.test @@ -10,11 +10,10 @@ #*********************************************************************** # This file implements regression tests for SQLite library. # # This file checks error recovery from malformed SQL strings. # -# $Id: fuzz2.test,v 1.3 2007/05/15 16:51:37 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -102,7 +101,29 @@ fuzzcatch {REPLACE INTO AAAAAA SELECT DISTINCT "AAAAAA" . * WHERE AAAAAA ( AAAAAA ( ) ) GROUP BY AAAAAA . AAAAAA . "AAAAAA" IN "AAAAAA" | AAAAAA ( ALL , ) ORDER BY #238, #92 DESC LIMIT 0 OFFSET - RAISE ( IGNORE ) NOT NULL > RAISE ( IGNORE ) IS NULL} } {1} do_test fuzz2-5.5 { fuzzcatch {SELECT ALL * GROUP BY EXISTS ( SELECT "AAAAAA" . * , AAAAAA ( * ) AS AAAAAA FROM "AAAAAA" . "AAAAAA" AS "AAAAAA" USING ( AAAAAA , "AAAAAA" , "AAAAAA" ) WHERE AAAAAA ( DISTINCT ) - RAISE ( FAIL , "AAAAAA" ) HAVING "AAAAAA" . "AAAAAA" . AAAAAA ORDER BY #182 , #55 ) BETWEEN EXISTS ( SELECT ALL * FROM ( ( } } {1} + +# Test cases discovered by Michal Zalewski on 2015-01-03 and reported on the +# sqlite-users mailing list. All of these cases cause segfaults in +# SQLite 3.8.7.4 and earlier. +# +do_test fuzz2-6.1 { + catchsql {SELECT n()AND+#0;} +} {1 {near "#0": syntax error}} +do_test fuzz2-6.2 { + catchsql {SELECT strftime()} +} {0 {{}}} +do_test fuzz2-6.3 { + catchsql {DETACH(SELECT group_concat(q));} +} {1 {no such column: q}} +do_test fuzz2-6.4a { + db eval {DROP TABLE IF EXISTS t0; CREATE TABLE t0(t);} + catchsql {INSERT INTO t0 SELECT strftime();} +} {0 {}} +do_test fuzz2-6.4b { + db eval {SELECT quote(t) FROM t0} +} {NULL} + finish_test Index: test/main.test ================================================================== --- test/main.test +++ test/main.test @@ -512,7 +512,27 @@ set rc [catch {sqlite3 db test.db -vfs async} msg] list $rc $msg } {1 {no such vfs: async}} } } + +# Print the version number so that it can be picked up by releasetest.tcl. +# +puts [db one {SELECT 'VERSION: ' || + sqlite_version() || ' ' || + sqlite_source_id();}] + +# Do deliberate failures if the TEST_FAILURE environment variable is set. +# This is done to verify that failure notifications are detected by the +# releasetest.tcl script, or possibly by other scripts involved in automatic +# testing. +# +if {[info exists ::env(TEST_FAILURE)]} { + set res 123 + if {$::env(TEST_FAILURE)==0} {set res 234} + do_test main-99.1 { + bad_behavior $::env(TEST_FAILURE) + set x 123 + } $res +} finish_test Index: test/malloc_common.tcl ================================================================== --- test/malloc_common.tcl +++ test/malloc_common.tcl @@ -127,12 +127,10 @@ set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" set DEFAULT(-install) "" set DEFAULT(-uninstall) "" - set DEFAULT(-start) 1 - set DEFAULT(-end) 0 fix_testname name array set O [array get DEFAULT] array set O $args @@ -146,12 +144,11 @@ if {[llength $flist]==0} { error "unknown fault: $f" } set faultlist [concat $faultlist $flist] } set testspec [list -prep $O(-prep) -body $O(-body) \ - -test $O(-test) -install $O(-install) -uninstall $O(-uninstall) \ - -start $O(-start) -end $O(-end) + -test $O(-test) -install $O(-install) -uninstall $O(-uninstall) ] foreach f [lsort -unique $faultlist] { eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec } } @@ -319,12 +316,10 @@ # # -body Script to execute (with fault injection). # # -test Script to execute after -body. # -# -start Index of first fault to inject (default 1) -# proc do_one_faultsim_test {testname args} { set DEFAULT(-injectstart) "expr" set DEFAULT(-injectstop) "expr 0" set DEFAULT(-injecterrlist) [list] @@ -333,12 +328,10 @@ set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" set DEFAULT(-install) "" set DEFAULT(-uninstall) "" - set DEFAULT(-start) 1 - set DEFAULT(-end) 0 array set O [array get DEFAULT] array set O $args foreach o [array names O] { if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" } @@ -351,14 +344,11 @@ eval $O(-injectinstall) eval $O(-install) set stop 0 - for {set iFail $O(-start)} \ - {!$stop && ($O(-end)==0 || $iFail<=$O(-end))} \ - {incr iFail} \ - { + for {set iFail 1} {!$stop} {incr iFail} { # Evaluate the -prep script. # eval $O(-prep) Index: test/oserror.test ================================================================== --- test/oserror.test +++ test/oserror.test @@ -49,23 +49,24 @@ # The xOpen() method of the unix VFS calls getcwd() as well as open(). # Although this does not appear to be documented in the man page, on OSX # a call to getcwd() may fail if there are no free file descriptors. So # an error may be reported for either open() or getcwd() here. # -puts "Possible valgrind error about invalid file descriptor follows:" -do_test 1.1.1 { - set ::log [list] - list [catch { - for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } - } msg] $msg -} {1 {unable to open database file}} -do_test 1.1.2 { - catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } -} {1} -do_re_test 1.1.3 { - lindex $::log 0 -} {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } +if {![clang_sanitize_address]} { + do_test 1.1.1 { + set ::log [list] + list [catch { + for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } + } msg] $msg + } {1 {unable to open database file}} + do_test 1.1.2 { + catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } + } {1} + do_re_test 1.1.3 { + lindex $::log 0 + } {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } +} # Test a failure in open() due to the path being a directory. # do_test 1.2.1 { Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -116,10 +116,23 @@ bigsort.test }] if {[info exists ::env(QUICKTEST_INCLUDE)]} { set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)] } +if {[info exists ::env(QUICKTEST_OMIT)]} { + foreach x [split $::env(QUICKTEST_OMIT) ,] { + regsub -all \\y$x\\y $allquicktests {} allquicktests + } +} + +# If the TEST_FAILURE environment variable is set, it means that we what to +# deliberately provoke test failures in order to test the test infrastructure. +# Only the main.test module is needed for this. +# +if {[info exists ::env(TEST_FAILURE)]} { + set allquicktests main.test +} ############################################################################# # Start of tests # @@ -150,11 +163,13 @@ test_suite "valgrind" -prefix "" -description { Run the "veryquick" test suite with a couple of multi-process tests (that fail under valgrind) omitted. } -files [ - test_set $allquicktests -exclude *malloc* *ioerr* *fault* wal.test atof1.test + test_set $allquicktests -exclude *malloc* *ioerr* *fault* wal.test \ + shell*.test crash8.test atof1.test selectG.test \ + tkt-fc62af4523.test ] -initialize { set ::G(valgrind) 1 } -shutdown { unset -nocomplain ::G(valgrind) } @@ -221,14 +236,10 @@ fts4check.test fts4unicode.test fts4noti.test fts3varint.test fts4growth.test fts4growth2.test } -test_suite "fts5" -prefix "" -description { - All FTS5 tests. -} -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test] - test_suite "nofaultsim" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ Index: test/releasetest.tcl ================================================================== --- test/releasetest.tcl +++ test/releasetest.tcl @@ -11,10 +11,11 @@ --srcdir TOP-OF-SQLITE-TREE (see below) --platform PLATFORM (see below) --config CONFIGNAME (Run only CONFIGNAME) --quick (Run "veryquick.test" only) + --veryquick (Run "make smoketest" only) --buildonly (Just build testfixture - do not run) --dryrun (Print what would have happened) --info (Show diagnostic info) The default value for --srcdir is the parent of the directory holding @@ -26,18 +27,39 @@ Every test begins with a fresh run of the configure script at the top of the SQLite source tree. } -array set ::Configs { +# Omit comments (text between # and \n) in a long multi-line string. +# +proc strip_comments {in} { + regsub -all {#[^\n]*\n} $in {} out + return $out +} + +array set ::Configs [strip_comments { "Default" { -O2 + --disable-amalgamation --disable-shared + } + "Sanitize" { + CC=clang -fsanitize=undefined + -DSQLITE_ENABLE_STAT4 } - "Ftrapv" { - -O2 -ftrapv - -DSQLITE_MAX_ATTACHED=55 - -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + "Have-Not" { + # The "Have-Not" configuration sets all possible -UHAVE_feature options + # in order to verify that the code works even on platforms that lack + # these support services. + -DHAVE_FDATASYNC=0 + -DHAVE_GMTIME_R=0 + -DHAVE_ISNAN=0 + -DHAVE_LOCALTIME_R=0 + -DHAVE_LOCALTIME_S=0 + -DHAVE_MALLOC_USABLE_SIZE=0 + -DHAVE_STRCHRNUL=0 + -DHAVE_USLEEP=0 + -DHAVE_UTIME=0 } "Unlock-Notify" { -O2 -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_THREADSAFE @@ -50,10 +72,11 @@ } "Update-Delete-Limit" { -O2 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_ENABLE_STMT_SCANSTATUS } "Check-Symbols" { -DSQLITE_MEMDEBUG=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_ENABLE_FTS3=1 @@ -65,12 +88,15 @@ -DSQLITE_SECURE_DELETE=1 -DSQLITE_SOUNDEX=1 -DSQLITE_ENABLE_ATOMIC_WRITE=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_STMT_SCANSTATUS } "Debug-One" { + --disable-shared -O2 -DSQLITE_DEBUG=1 -DSQLITE_MEMDEBUG=1 -DSQLITE_MUTEX_NOOP=1 -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 @@ -77,10 +103,12 @@ -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_ENABLE_MEMSYS3=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_MAX_ATTACHED=125 } "Device-One" { -O2 -DSQLITE_DEBUG=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 @@ -117,10 +145,11 @@ "Locking-Style" { -O2 -DSQLITE_ENABLE_LOCKING_STYLE=1 } "OS-X" { + -O1 # Avoid a compiler bug in gcc 4.2.1 build 5658 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_THREADSAFE=2 -DSQLITE_OS_UNIX=1 -DSQLITE_ENABLE_LOCKING_STYLE=1 @@ -146,52 +175,84 @@ -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_PARENTHESIS -DSQLITE_DISABLE_FTS4_DEFERRED -DSQLITE_ENABLE_RTREE } - "No-lookaside" { -DSQLITE_TEST_REALLOC_STRESS=1 -DSQLITE_OMIT_LOOKASIDE=1 -DHAVE_USLEEP=1 } -} + "Valgrind" { + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_FTS4 + -DSQLITE_ENABLE_RTREE + } + + # The next group of configurations are used only by the + # Failure-Detection platform. They are all the same, but we need + # different names for them all so that they results appear in separate + # subdirectories. + # + Fail0 {-O0} + Fail2 {-O0} + Fail3 {-O0} + Fail4 {-O0} +}] -array set ::Platforms { +array set ::Platforms [strip_comments { Linux-x86_64 { "Check-Symbols" checksymbols "Debug-One" "mptest test" + "Have-Not" test "Secure-Delete" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test "Extra-Robustness" test "Device-Two" test - "Ftrapv" test "No-lookaside" test "Devkit" test + "Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test} + "Valgrind" valgrindtest "Default" "threadtest fulltest" "Device-One" fulltest } Linux-i686 { "Devkit" test + "Have-Not" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Device-One" test "Device-Two" test "Default" "threadtest fulltest" } Darwin-i386 { "Locking-Style" "mptest test" + "Have-Not" test "OS-X" "threadtest fulltest" } Darwin-x86_64 { "Locking-Style" "mptest test" + "Have-Not" test "OS-X" "threadtest fulltest" } "Windows NT-intel" { "Default" "mptest fulltestonly" + "Have-Not" test + } + + # The Failure-Detection platform runs various tests that deliberately + # fail. This is used as a test of this script to verify that this script + # correctly identifies failures. + # + Failure-Detection { + Fail0 "TEST_FAILURE=0 test" + Sanitize "TEST_FAILURE=1 test" + Fail2 "TEST_FAILURE=2 valgrindtest" + Fail3 "TEST_FAILURE=3 valgrindtest" + Fail4 "TEST_FAILURE=4 test" } -} +}] # End of configuration section. ######################################################################### ######################################################################### @@ -226,15 +287,41 @@ if {$nerr>0} { set rc 1 set errmsg $line } } + if {[regexp {runtime error: +(.*)} $line all msg]} { + incr ::NERRCASE + if {$rc==0} { + set rc 1 + set errmsg $msg + } + } + if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} { + incr ::NERRCASE + if {$rc==0} { + set rc 1 + set errmsg $all + } + } + if {[regexp {^VERSION: 3\.\d+.\d+} $line]} { + set v [string range $line 9 end] + if {$::SQLITE_VERSION eq ""} { + set ::SQLITE_VERSION $v + } elseif {$::SQLITE_VERSION ne $v} { + set rc 1 + set errmsg "version conflict: {$::SQLITE_VERSION} vs. {$v}" + } + } } close $fd if {!$seen} { set rc 1 set errmsg "Test did not complete" + if {[file readable core]} { + append errmsg " - core file exists" + } } } proc run_test_suite {name testtarget config} { # Tcl variable $opts is used to build up the value used to set the @@ -242,13 +329,21 @@ # CFLAGS. The makefile will pass OPTS to both gcc and lemon, but # CFLAGS is only passed to gcc. # set cflags "-g" set opts "" + set title ${name}($testtarget) + set configOpts "" + + regsub -all {#[^\n]*\n} $config \n config foreach arg $config { - if {[string match -D* $arg]} { + if {[regexp {^-[UD]} $arg]} { lappend opts $arg + } elseif {[regexp {^[A-Z]+=} $arg]} { + lappend testtarget $arg + } elseif {[regexp {^--(enable|disable)-} $arg]} { + lappend configOpts $arg } else { lappend cflags $arg } } @@ -264,32 +359,33 @@ append opts " -DSQLITE_OS_WIN=1" } else { append opts " -DSQLITE_OS_UNIX=1" } - dryrun file mkdir $dir - if {!$::DRYRUN} { - set title ${name}($testtarget) + if {!$::TRACE} { set n [string length $title] puts -nonewline "${title}[string repeat . [expr {63-$n}]]" flush stdout } + set rc 0 set tm1 [clock seconds] set origdir [pwd] - dryrun cd $dir + trace_cmd file mkdir $dir + trace_cmd cd $dir set errmsg {} - set rc [catch [configureCommand]] + catch {file delete core} + set rc [catch [configureCommand $configOpts]] if {!$rc} { set rc [catch [makeCommand $testtarget $cflags $opts]] count_tests_and_errors test.log rc errmsg } + trace_cmd cd $origdir set tm2 [clock seconds] - dryrun cd $origdir - if {!$::DRYRUN} { - set hours [expr {($tm2-$tm2)/3600}] + if {!$::TRACE} { + set hours [expr {($tm2-$tm1)/3600}] set minutes [expr {(($tm2-$tm1)/60)%60}] set seconds [expr {($tm2-$tm1)%60}] set tm [format (%02d:%02d:%02d) $hours $minutes $seconds] if {$rc} { puts " FAIL $tm" @@ -302,37 +398,40 @@ } # The following procedure returns the "configure" command to be exectued for # the current platform, which may be Windows (via MinGW, etc). # -proc configureCommand {} { - set result [list dryrun exec] +proc configureCommand {opts} { + set result [list trace_cmd exec] if {$::tcl_platform(platform)=="windows"} { lappend result sh } - lappend result $::SRCDIR/configure -enable-load-extension >& test.log + lappend result $::SRCDIR/configure --enable-load-extension + foreach x $opts {lappend result $x} + lappend result >& test.log } # The following procedure returns the "make" command to be executed for the # specified targets, compiler flags, and options. # proc makeCommand { targets cflags opts } { - set result [list dryrun exec make clean] + set result [list trace_cmd exec make clean] foreach target $targets { lappend result $target } lappend result CFLAGS=$cflags OPTS=$opts >>& test.log } -# The following procedure either prints its arguments (if ::DRYRUN is true) -# or executes the command of its arguments in the calling context -# (if ::DRYRUN is false). -# -proc dryrun {args} { - if {$::DRYRUN} { - puts $args - } else { +# The following procedure prints its arguments if ::TRACE is true. +# And it executes the command of its arguments in the calling context +# if ::DRYRUN is false. +# +proc trace_cmd {args} { + if {$::TRACE} { + puts $args + } + if {!$::DRYRUN} { uplevel 1 $args } } @@ -345,17 +444,18 @@ set ::SRCDIR [file normalize [file dirname [file dirname $::argv0]]] set ::QUICK 0 set ::BUILDONLY 0 set ::DRYRUN 0 set ::EXEC exec + set ::TRACE 0 set config {} set platform $::tcl_platform(os)-$::tcl_platform(machine) for {set i 0} {$i < [llength $argv]} {incr i} { set x [lindex $argv $i] if {[regexp {^--[a-z]} $x]} {set x [string range $x 1 end]} - switch -- $x { + switch -glob -- $x { -srcdir { incr i set ::SRCDIR [file normalize [lindex $argv $i]] } @@ -365,10 +465,13 @@ } -quick { set ::QUICK 1 } + -veryquick { + set ::QUICK 2 + } -config { incr i set config [lindex $argv $i] } @@ -378,19 +481,24 @@ } -dryrun { set ::DRYRUN 1 } + + -trace { + set ::TRACE 1 + } -info { puts "Command-line Options:" puts " --srcdir $::SRCDIR" puts " --platform [list $platform]" puts " --config [list $config]" if {$::QUICK} {puts " --quick"} if {$::BUILDONLY} {puts " --buildonly"} if {$::DRYRUN} {puts " --dryrun"} + if {$::TRACE} {puts " --trace"} puts "\nAvailable --platform options:" foreach y [lsort [array names ::Platforms]] { puts " [list $y]" } puts "\nAvailable --config options:" @@ -397,10 +505,18 @@ foreach y [lsort [array names ::Configs]] { puts " [list $y]" } exit } + -g - + -D* - + -O* - + -enable-* - + -disable-* - + *=* { + lappend ::EXTRACONFIG [lindex $argv $i] + } default { puts stderr "" puts stderr [string trim $::USAGE_MESSAGE] exit -1 @@ -429,39 +545,50 @@ puts "Running the following test configurations for $platform:" puts " [string trim $::CONFIGLIST]" puts -nonewline "Flags:" if {$::DRYRUN} {puts -nonewline " --dryrun"} if {$::BUILDONLY} {puts -nonewline " --buildonly"} - if {$::QUICK} {puts -nonewline " --quick"} + switch -- $::QUICK { + 1 {puts -nonewline " --quick"} + 2 {puts -nonewline " --veryquick"} + } puts "" } # Main routine. # proc main {argv} { # Process any command line options. + set ::EXTRACONFIG {} process_options $argv puts [string repeat * 79] set ::NERR 0 set ::NTEST 0 set ::NTESTCASE 0 set ::NERRCASE 0 + set ::SQLITE_VERSION {} set STARTTIME [clock seconds] foreach {zConfig target} $::CONFIGLIST { - if {$::QUICK} {set target test} - if {$::BUILDONLY} {set target testfixture} - set config_options $::Configs($zConfig) + if {$target ne "checksymbols"} { + switch -- $::QUICK { + 1 {set target test} + 2 {set target smoketest} + } + if {$::BUILDONLY} {set target testfixture} + } + set config_options [concat $::Configs($zConfig) $::EXTRACONFIG] incr NTEST run_test_suite $zConfig $target $config_options # If the configuration included the SQLITE_DEBUG option, then remove # it and run veryquick.test. If it did not include the SQLITE_DEBUG option # add it and run veryquick.test. - if {$target!="checksymbols" && !$::BUILDONLY} { + if {$target!="checksymbols" && $target!="valgrindtest" + && !$::BUILDONLY && $::QUICK<2} { set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] set xtarget $target regsub -all {fulltest[a-z]*} $xtarget test xtarget if {$debug_idx < 0} { incr NTEST @@ -480,9 +607,12 @@ set hr [expr {$elapsetime/3600}] set min [expr {($elapsetime/60)%60}] set sec [expr {$elapsetime%60}] set etime [format (%02d:%02d:%02d) $hr $min $sec] puts [string repeat * 79] - puts "$::NERRCASE failures of $::NTESTCASE tests run in $etime" + puts "$::NERRCASE failures out of $::NTESTCASE tests in $etime" + if {$::SQLITE_VERSION ne ""} { + puts "SQLite $::SQLITE_VERSION" + } } main $argv ADDED test/selectG.test Index: test/selectG.test ================================================================== --- /dev/null +++ test/selectG.test @@ -0,0 +1,39 @@ +# 2015-01-05 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file verifies that INSERT operations with a very large number of +# VALUE terms works and does not hit the SQLITE_LIMIT_COMPOUND_SELECT limit. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix selectG + +# Do an INSERT with a VALUES clause that contains 100,000 entries. Verify +# that this insert happens quickly (in less than 10 seconds). Actually, the +# insert will normally happen in less than 0.5 seconds on a workstation, but +# we allow plenty of overhead for slower machines. The speed test checks +# for an O(N*N) inefficiency that was once in the code and that would make +# the insert run for over a minute. +# +do_test 100 { + set sql "CREATE TABLE t1(x);\nINSERT INTO t1(x) VALUES" + for {set i 1} {$i<100000} {incr i} { + append sql "($i)," + } + append sql "($i);" + set microsec [lindex [time {db eval $sql}] 0] + db eval { + SELECT count(x), sum(x), avg(x), $microsec<10000000 FROM t1; + } +} {100000 5000050000 50000.5 1} + +finish_test Index: test/shell1.test ================================================================== --- test/shell1.test +++ test/shell1.test @@ -205,14 +205,14 @@ do_test shell1-2.2.4 { catchcmd "test.db" ".explain \'OFF" } {0 {}} do_test shell1-2.2.5 { catchcmd "test.db" ".mode \"insert FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-2.2.6 { catchcmd "test.db" ".mode \'insert FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} # check multiple tokens, and quoted tokens do_test shell1-2.3.1 { catchcmd "test.db" ".explain 1" } {0 {}} @@ -236,11 +236,11 @@ } {0 {}} # check quoted args are unquoted do_test shell1-2.4.1 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode csv" } {0 {}} do_test shell1-2.4.2 { catchcmd "test.db" ".mode \"csv\"" @@ -419,24 +419,25 @@ # too many arguments catchcmd "test.db" ".indices FOO BAD" } {1 {Usage: .indices ?LIKE-PATTERN?}} # .mode MODE ?TABLE? Set output mode where MODE is one of: +# ascii Columns/rows delimited by 0x1F and 0x1E # csv Comma-separated values # column Left-aligned columns. (See .width) # html HTML
code # insert SQL insert statements for TABLE # line One value per line -# list Values delimited by .separator string +# list Values delimited by .separator strings # tabs Tab-separated values # tcl TCL list elements do_test shell1-3.13.1 { catchcmd "test.db" ".mode" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.2 { catchcmd "test.db" ".mode FOO" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.3 { catchcmd "test.db" ".mode csv" } {0 {}} do_test shell1-3.13.4 { catchcmd "test.db" ".mode column" @@ -465,14 +466,14 @@ } {0 {}} # don't allow partial mode type matches do_test shell1-3.13.12 { catchcmd "test.db" ".mode l" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.13 { catchcmd "test.db" ".mode li" -} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}} +} {1 {Error: mode should be one of: ascii column csv html insert line list tabs tcl}} do_test shell1-3.13.14 { catchcmd "test.db" ".mode lin" } {0 {}} # .nullvalue STRING Print STRING in place of NULL values @@ -584,24 +585,24 @@ } {0 {CREATE TABLE t1(x); CREATE VIEW v2 AS SELECT x+1 AS y FROM t1; CREATE VIEW v1 AS SELECT y+1 FROM v2;}} db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;} -# .separator STRING Change separator used by output mode and .import +# .separator STRING Change column separator used by output and .import do_test shell1-3.22.1 { catchcmd "test.db" ".separator" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} do_test shell1-3.22.2 { catchcmd "test.db" ".separator FOO" } {0 {}} do_test shell1-3.22.3 { catchcmd "test.db" ".separator ABC XYZ" } {0 {}} do_test shell1-3.22.4 { # too many arguments catchcmd "test.db" ".separator FOO BAD BAD2" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} # .show Show the current values for various settings do_test shell1-3.23.1 { set res [catchcmd "test.db" ".show"] list [regexp {echo:} $res] \ @@ -608,14 +609,15 @@ [regexp {explain:} $res] \ [regexp {headers:} $res] \ [regexp {mode:} $res] \ [regexp {nullvalue:} $res] \ [regexp {output:} $res] \ - [regexp {separator:} $res] \ + [regexp {colseparator:} $res] \ + [regexp {rowseparator:} $res] \ [regexp {stats:} $res] \ [regexp {width:} $res] -} {1 1 1 1 1 1 1 1 1} +} {1 1 1 1 1 1 1 1 1 1} do_test shell1-3.23.2 { # too many arguments catchcmd "test.db" ".show BAD" } {1 {Usage: .show}} Index: test/shell5.test ================================================================== --- test/shell5.test +++ test/shell5.test @@ -53,26 +53,32 @@ } {1 {Usage: .import FILE TABLE}} # .separator STRING Change separator used by output mode and .import do_test shell5-1.2.1 { catchcmd "test.db" ".separator" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} do_test shell5-1.2.2 { catchcmd "test.db" ".separator ONE" } {0 {}} do_test shell5-1.2.3 { catchcmd "test.db" ".separator ONE TWO" } {0 {}} do_test shell5-1.2.4 { # too many arguments catchcmd "test.db" ".separator ONE TWO THREE" -} {1 {Usage: .separator SEPARATOR ?NEWLINE?}} +} {1 {Usage: .separator COL ?ROW?}} + +# column separator should default to "|" +do_test shell5-1.3.1.1 { + set res [catchcmd "test.db" ".show"] + list [regexp {colseparator: \"\|\"} $res] +} {1} -# separator should default to "|" -do_test shell5-1.3.1 { +# row separator should default to "\n" +do_test shell5-1.3.1.2 { set res [catchcmd "test.db" ".show"] - list [regexp {separator: \"\|\"} $res] + list [regexp {rowseparator: \"\\n\"} $res] } {1} # set separator to different value. # check that .show reports new value do_test shell5-1.3.2 { @@ -370,7 +376,33 @@ .import shell5.csv t4 }] db eval { SELECT * FROM t4 } } {xy\" hello one 2 {} {}} +#---------------------------------------------------------------------------- +# Tests for the shell "ascii" import/export mode. +# +do_test shell5-3.1 { + set fd [open shell5.csv w] + fconfigure $fd -encoding binary -translation binary + puts -nonewline $fd "\"test 1\"\x1F,test 2\r\n\x1E" + puts -nonewline $fd "test 3\x1Ftest 4\n" + close $fd + catchcmd test.db { +.mode ascii +CREATE TABLE t5(a, b); +.import shell5.csv t5 + } + db eval { SELECT * FROM t5 } +} "\{\"test 1\"} \{,test 2\r\n\} \{test 3\} \{test 4\n\}" + +do_test shell5-3.2 { + set x [catchcmd test.db { +.mode ascii +SELECT * FROM t5; + }] + # Handle platform end-of-line differences + regsub -all {[\n\r]?\n} $x x + set x +} "0 \{\"test 1\"\x1F,test 2\x1Etest 3\x1Ftest 4\x1E\}" finish_test Index: test/sort.test ================================================================== --- test/sort.test +++ test/sort.test @@ -14,10 +14,15 @@ # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sort +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db # Create a bunch of data to sort against # do_test sort-1.0 { execsql { Index: test/sort2.test ================================================================== --- test/sort2.test +++ test/sort2.test @@ -15,10 +15,15 @@ # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sort2 +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db foreach {tn script} { 1 { } 2 { catch { db close } Index: test/sort4.test ================================================================== --- test/sort4.test +++ test/sort4.test @@ -15,10 +15,16 @@ # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sort4 +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db + # Configure the sorter to use 3 background threads. db eval {PRAGMA threads=3} # Minimum number of seconds to run for. If the value is 0, each test Index: test/sortfault.test ================================================================== --- test/sortfault.test +++ test/sortfault.test @@ -15,10 +15,16 @@ # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix sortfault +db close +sqlite3_shutdown +sqlite3_config_pmasz 10 +sqlite3_initialize +sqlite3 db test.db + do_execsql_test 1.0 { PRAGMA cache_size = 5; } Index: tool/lemon.c ================================================================== --- tool/lemon.c +++ tool/lemon.c @@ -1495,21 +1495,25 @@ static int noResort = 0; static struct s_options options[] = { {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, {OPT_FSTR, "D", (char*)handle_D_option, "Define an %ifdef macro."}, - {OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."}, + {OPT_FSTR, "f", 0, "Ignored. (Placeholder for -f compiler options.)"}, {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FSTR, "I", 0, "Ignored. (Placeholder for '-I' compiler options.)"}, {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file."}, {OPT_FLAG, "l", (char*)&nolinenosflag, "Do not print #line statements."}, + {OPT_FSTR, "O", 0, "Ignored. (Placeholder for '-O' compiler options.)"}, {OPT_FLAG, "p", (char*)&showPrecedenceConflict, "Show conflicts resolved by precedence rules"}, {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, {OPT_FLAG, "r", (char*)&noResort, "Do not sort or renumber states"}, {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."}, {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_FSTR, "T", (char*)handle_T_option, "Specify a template file."}, + {OPT_FSTR, "W", 0, "Ignored. (Placeholder for '-W' compiler options.)"}, {OPT_FLAG,0,0,0} }; int i; int exitcode; struct lemon lem; @@ -1810,10 +1814,12 @@ if( err ){ fprintf(err,"%sundefined option.\n",emsg); errline(i,1,err); } errcnt++; + }else if( op[j].arg==0 ){ + /* Ignore this option */ }else if( op[j].type==OPT_FLAG ){ *((int*)op[j].arg) = v; }else if( op[j].type==OPT_FFLAG ){ (*(void(*)(int))(op[j].arg))(v); }else if( op[j].type==OPT_FSTR ){ @@ -1999,21 +2005,21 @@ case OPT_FFLAG: fprintf(errstream," -%-*s %s\n",max,op[i].label,op[i].message); break; case OPT_INT: case OPT_FINT: - fprintf(errstream," %s=%*s %s\n",op[i].label, + fprintf(errstream," -%s%*s %s\n",op[i].label, (int)(max-lemonStrlen(op[i].label)-9),"",op[i].message); break; case OPT_DBL: case OPT_FDBL: - fprintf(errstream," %s=%*s %s\n",op[i].label, + fprintf(errstream," -%s%*s %s\n",op[i].label, (int)(max-lemonStrlen(op[i].label)-6),"",op[i].message); break; case OPT_STR: case OPT_FSTR: - fprintf(errstream," %s=%*s %s\n",op[i].label, + fprintf(errstream," -%s%*s %s\n",op[i].label, (int)(max-lemonStrlen(op[i].label)-8),"",op[i].message); break; } } } @@ -2434,11 +2440,11 @@ break; case WAITING_FOR_DECL_ARG: if( x[0]=='{' || x[0]=='\"' || isalnum(x[0]) ){ const char *zOld, *zNew; char *zBuf, *z; - int nOld, n, nLine, nNew, nBack; + int nOld, n, nLine = 0, nNew, nBack; int addLineMacro; char zLine[50]; zNew = x; if( zNew[0]=='"' || zNew[0]=='{' ) zNew++; nNew = lemonStrlen(zNew); @@ -2633,11 +2639,11 @@ void Parse(struct lemon *gp) { struct pstate ps; FILE *fp; char *filebuf; - int filesize; + unsigned int filesize; int lineno; int c; char *cp, *nextcp; int startline = 0; @@ -2767,11 +2773,11 @@ nextcp = cp; } c = *cp; *cp = 0; /* Null terminate the token */ parseonetoken(&ps); /* Parse the token */ - *cp = c; /* Restore the buffer */ + *cp = (char)c; /* Restore the buffer */ cp = nextcp; } free(filebuf); /* Release the buffer after parsing */ gp->rule = ps.firstrule; gp->errorcnt = ps.errorcnt; @@ -3390,11 +3396,11 @@ lemon_strcpy(&z[used], zInt); used += lemonStrlen(&z[used]); zText++; n--; }else{ - z[used++] = c; + z[used++] = (char)c; } } z[used] = 0; return z; } DELETED tool/loadfts.c Index: tool/loadfts.c ================================================================== --- tool/loadfts.c +++ /dev/null @@ -1,238 +0,0 @@ -/* -** 2013-06-10 -** -** 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. -** -************************************************************************* -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "sqlite3.h" - -/* -** Implementation of the "readtext(X)" SQL function. The entire content -** of the file named X is read and returned as a TEXT value. It is assumed -** the file contains UTF-8 text. NULL is returned if the file does not -** exist or is unreadable. -*/ -static void readfileFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zName; - FILE *in; - long nIn; - void *pBuf; - - zName = (const char*)sqlite3_value_text(argv[0]); - if( zName==0 ) return; - in = fopen(zName, "rb"); - if( in==0 ) return; - fseek(in, 0, SEEK_END); - nIn = ftell(in); - rewind(in); - pBuf = sqlite3_malloc( nIn ); - if( pBuf && 1==fread(pBuf, nIn, 1, in) ){ - sqlite3_result_text(context, pBuf, nIn, sqlite3_free); - }else{ - sqlite3_free(pBuf); - } - fclose(in); -} - -/* -** Print usage text for this program and exit. -*/ -static void showHelp(const char *zArgv0){ - printf("\n" -"Usage: %s SWITCHES... DB\n" -"\n" -" This program opens the database named on the command line and attempts to\n" -" create an FTS table named \"fts\" with a single column. If successful, it\n" -" recursively traverses the directory named by the -dir option and inserts\n" -" the contents of each file into the fts table. All files are assumed to\n" -" contain UTF-8 text.\n" -"\n" -"Switches are:\n" -" -fts [345] FTS version to use (default=5)\n" -" -idx [01] Create a mapping from filename to rowid (default=0)\n" -" -dir Root of directory tree to load data from (default=.)\n" -" -trans Number of inserts per transaction (default=1)\n" -, zArgv0 -); - exit(1); -} - -/* -** Exit with a message based on the argument and the current value of errno. -*/ -static void error_out(const char *zText){ - fprintf(stderr, "%s: %s\n", zText, strerror(errno)); - exit(-1); -} - -/* -** Exit with a message based on the first argument and the error message -** currently stored in database handle db. -*/ -static void sqlite_error_out(const char *zText, sqlite3 *db){ - fprintf(stderr, "%s: %s\n", zText, sqlite3_errmsg(db)); - exit(-1); -} - -/* -** Context object for visit_file(). -*/ -typedef struct VisitContext VisitContext; -struct VisitContext { - int nRowPerTrans; - sqlite3 *db; /* Database handle */ - sqlite3_stmt *pInsert; /* INSERT INTO fts VALUES(readtext(:1)) */ -}; - -/* -** Callback used with traverse(). The first argument points to an object -** of type VisitContext. This function inserts the contents of the text -** file zPath into the FTS table. -*/ -void visit_file(void *pCtx, const char *zPath){ - int rc; - VisitContext *p = (VisitContext*)pCtx; - /* printf("%s\n", zPath); */ - sqlite3_bind_text(p->pInsert, 1, zPath, -1, SQLITE_STATIC); - sqlite3_step(p->pInsert); - rc = sqlite3_reset(p->pInsert); - if( rc!=SQLITE_OK ){ - sqlite_error_out("insert", p->db); - }else if( p->nRowPerTrans>0 - && (sqlite3_last_insert_rowid(p->db) % p->nRowPerTrans)==0 - ){ - sqlite3_exec(p->db, "COMMIT ; BEGIN", 0, 0, 0); - } -} - -/* -** Recursively traverse directory zDir. For each file that is not a -** directory, invoke the supplied callback with its path. -*/ -static void traverse( - const char *zDir, /* Directory to traverse */ - void *pCtx, /* First argument passed to callback */ - void (*xCallback)(void*, const char *zPath) -){ - DIR *d; - struct dirent *e; - - d = opendir(zDir); - if( d==0 ) error_out("opendir()"); - - for(e=readdir(d); e; e=readdir(d)){ - if( strcmp(e->d_name, ".")==0 || strcmp(e->d_name, "..")==0 ) continue; - char *zPath = sqlite3_mprintf("%s/%s", zDir, e->d_name); - if (e->d_type & DT_DIR) { - traverse(zPath, pCtx, xCallback); - }else{ - xCallback(pCtx, zPath); - } - sqlite3_free(zPath); - } - - closedir(d); -} - -int main(int argc, char **argv){ - int iFts = 5; /* Value of -fts option */ - int bMap = 0; /* True to create mapping table */ - const char *zDir = "."; /* Directory to scan */ - int i; - int rc; - int nRowPerTrans = 0; - sqlite3 *db; - char *zSql; - VisitContext sCtx; - - int nCmd = 0; - char **aCmd = 0; - - if( argc % 2 ) showHelp(argv[0]); - - for(i=1; i<(argc-1); i+=2){ - char *zOpt = argv[i]; - char *zArg = argv[i+1]; - if( strcmp(zOpt, "-fts")==0 ){ - iFts = atoi(zArg); - if( iFts!=3 && iFts!=4 && iFts!= 5) showHelp(argv[0]); - } - else if( strcmp(zOpt, "-trans")==0 ){ - nRowPerTrans = atoi(zArg); - } - else if( strcmp(zOpt, "-idx")==0 ){ - bMap = atoi(zArg); - if( bMap!=0 && bMap!=1 ) showHelp(argv[0]); - } - else if( strcmp(zOpt, "-dir")==0 ){ - zDir = zArg; - } - else if( strcmp(zOpt, "-special")==0 ){ - nCmd++; - aCmd = sqlite3_realloc(aCmd, sizeof(char*) * nCmd); - aCmd[nCmd-1] = zArg; - } - else{ - showHelp(argv[0]); - } - } - - /* Open the database file */ - rc = sqlite3_open(argv[argc-1], &db); - if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_open()", db); - - rc = sqlite3_create_function(db, "readtext", 1, SQLITE_UTF8, 0, - readfileFunc, 0, 0); - if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_create_function()", db); - - /* Create the FTS table */ - zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE fts USING fts%d(content)", iFts); - rc = sqlite3_exec(db, zSql, 0, 0, 0); - if( rc!=SQLITE_OK ) sqlite_error_out("sqlite3_exec(1)", db); - sqlite3_free(zSql); - - for(i=0; i0 ) sqlite3_exec(db, "BEGIN", 0, 0, 0); - traverse(zDir, (void*)&sCtx, visit_file); - if( sCtx.nRowPerTrans>0 ) sqlite3_exec(db, "COMMIT", 0, 0, 0); - - /* Clean up and exit. */ - sqlite3_finalize(sCtx.pInsert); - sqlite3_close(db); - sqlite3_free(aCmd); - return 0; -} Index: tool/mksqlite3c-noext.tcl ================================================================== --- tool/mksqlite3c-noext.tcl +++ tool/mksqlite3c-noext.tcl @@ -94,10 +94,11 @@ btree.h btreeInt.h hash.h hwtime.h keywordhash.h + msvc.h mutex.h opcodes.h os_common.h os_setup.h os_win.h Index: tool/mksqlite3c.tcl ================================================================== --- tool/mksqlite3c.tcl +++ tool/mksqlite3c.tcl @@ -95,15 +95,14 @@ btreeInt.h fts3.h fts3Int.h fts3_hash.h fts3_tokenizer.h - fts5.h - fts5Int.h hash.h hwtime.h keywordhash.h + msvc.h mutex.h opcodes.h os_common.h os_setup.h os_win.h @@ -329,24 +328,13 @@ fts3_write.c fts3_snippet.c fts3_unicode.c fts3_unicode2.c - fts5_aux.c - fts5_buffer.c - fts5.c - fts5_config.c - fts5_expr.c - fts5_hash.c - fts5_index.c - fts5parse.c - fts5_storage.c - fts5_tokenize.c - rtree.c icu.c fts3_icu.c } { copy_file tsrc/$file } close $out Index: tool/mksqlite3internalh.tcl ================================================================== --- tool/mksqlite3internalh.tcl +++ tool/mksqlite3internalh.tcl @@ -56,10 +56,11 @@ btree.h btreeInt.h hash.h hwtime.h keywordhash.h + msvc.h opcodes.h os_common.h os_setup.h os_win.h os.h