From 67300dfce840a77fee7a3030eb7e4da4385700cc Mon Sep 17 00:00:00 2001 From: Nick Parker Date: Tue, 21 May 2013 15:38:11 -0500 Subject: [PATCH] Snapshot of upstream SQLite 3.7.17 --- Makefile.in | 33 +- Makefile.msc | 36 +- Makefile.vxworks | 2 + VERSION | 2 +- configure | 51 +- ext/fts1/ft_hash.h | 2 +- ext/fts1/fts1_hash.h | 2 +- ext/fts2/fts2.c | 4 +- ext/fts2/fts2_hash.h | 2 +- ext/fts2/fts2_tokenizer.c | 2 +- ext/fts2/fts2_tokenizer.h | 2 +- ext/fts3/fts3.c | 22 +- ext/fts3/fts3Int.h | 5 +- ext/fts3/fts3_aux.c | 29 +- ext/fts3/fts3_expr.c | 337 ++++- ext/fts3/fts3_hash.h | 2 +- ext/fts3/fts3_snippet.c | 4 +- ext/fts3/fts3_test.c | 2 +- ext/fts3/fts3_tokenizer.c | 2 +- ext/fts3/fts3_tokenizer.h | 2 +- ext/fts3/fts3_unicode.c | 2 +- ext/fts3/fts3_write.c | 28 +- ext/icu/README.txt | 2 +- ext/rtree/rtree.c | 12 +- ext/rtree/rtree1.test | 1 + ext/rtree/rtree5.test | 2 +- main.mk | 30 +- manifest | 440 +++--- manifest.uuid | 2 +- src/analyze.c | 4 +- src/attach.c | 4 +- src/backup.c | 38 +- src/bitvec.c | 2 +- src/btree.c | 467 +++--- src/btree.h | 2 + src/btreeInt.h | 1 + src/build.c | 71 +- src/ctime.c | 28 +- src/delete.c | 38 +- src/expr.c | 37 +- src/fkey.c | 32 +- src/func.c | 65 + src/global.c | 2 + src/hash.h | 2 +- src/insert.c | 16 +- src/journal.c | 8 + src/legacy.c | 12 +- src/loadext.c | 87 +- src/main.c | 138 +- src/memjournal.c | 4 +- src/os.c | 20 + src/os.h | 10 +- src/os_unix.c | 561 +++++-- src/os_win.c | 640 ++++++-- src/pager.c | 350 ++++- src/pager.h | 7 + src/parse.y | 39 +- src/pcache.h | 2 + src/pragma.c | 192 ++- src/prepare.c | 11 +- src/resolve.c | 146 +- src/select.c | 242 ++- src/shell.c | 154 +- src/sqlite.h.in | 131 +- src/sqlite3ext.h | 41 +- src/sqliteInt.h | 153 +- src/tclsqlite.c | 8 +- src/test1.c | 202 ++- src/test2.c | 54 +- src/test3.c | 40 +- src/test4.c | 31 +- src/test6.c | 2 +- src/test7.c | 31 +- src/test8.c | 28 +- src/test_async.c | 8 +- src/test_autoext.c | 2 +- src/test_backup.c | 10 +- src/test_config.c | 14 +- src/test_fuzzer.c | 1215 ---------------- src/test_intarray.c | 6 +- src/test_malloc.c | 22 +- src/test_multiplex.c | 10 +- src/test_mutex.c | 12 +- src/test_quota.c | 12 +- src/test_rtree.c | 8 +- src/test_spellfix.c | 2830 ------------------------------------ src/test_syscall.c | 43 +- src/test_thread.c | 16 +- src/test_vfs.c | 44 +- src/test_wholenumber.c | 311 ---- src/update.c | 6 +- src/util.c | 38 +- src/vacuum.c | 1 + src/vdbe.c | 31 +- src/vdbeInt.h | 22 +- src/vdbeapi.c | 12 +- src/vdbeaux.c | 44 +- src/vdbeblob.c | 2 +- src/vdbemem.c | 2 + src/vdbesort.c | 3 - src/vdbetrace.c | 40 +- src/wal.c | 70 +- src/wal.h | 5 +- src/walker.c | 17 +- src/where.c | 577 +++++--- test/8_3_names.test | 4 +- test/analyze7.test | 2 +- test/auth.test | 31 +- test/auth2.test | 12 +- test/autoindex1.test | 124 ++ test/backup_ioerr.test | 4 +- test/cache.test | 2 +- test/capi2.test | 14 +- test/check.test | 39 + test/collate3.test | 98 ++ test/collate4.test | 2 +- test/conflict.test | 4 + test/crash5.test | 2 +- test/dbstatus2.test | 1 + test/descidx3.test | 6 +- test/e_createtable.test | 2 +- test/e_fkey.test | 49 +- test/e_select.test | 2 +- test/e_uri.test | 2 +- test/enc2.test | 2 +- test/errmsg.test | 2 + test/exclusive2.test | 8 + test/filefmt.test | 35 + test/fkey2.test | 36 +- test/fkey4.test | 2 + test/fkey_malloc.test | 3 +- test/fts3ai.test | 5 + test/fts3aux1.test | 66 +- test/fts3near.test | 14 + test/fts4content.test | 101 ++ test/fts4unicode.test | 17 +- test/func.test | 29 +- test/fuzzer1.test | 7 +- test/fuzzerfault.test | 10 +- test/hook.test | 1 + test/incrblob.test | 8 +- test/incrvacuum_ioerr.test | 7 +- test/index5.test | 8 +- test/io.test | 77 +- test/limit.test | 147 ++ test/loadext.test | 16 +- test/malloc.test | 2 +- test/malloc3.test | 4 +- test/mallocG.test | 5 + test/malloc_common.tcl | 2 +- test/memdb.test | 2 +- test/minmax.test | 90 ++ test/misc7.test | 28 + test/notify2.test | 4 +- test/notnull.test | 27 +- test/pager1.test | 306 +++- test/pager2.test | 19 +- test/pagerfault.test | 299 ++++ test/pageropt.test | 7 +- test/permutations.test | 15 +- test/pragma.test | 68 +- test/selectA.test | 20 +- test/shell1.test | 10 +- test/speed1p.test | 3 +- test/spellfix.test | 64 +- test/subquery.test | 2 +- test/syscall.test | 2 +- test/sysfault.test | 32 +- test/temptable.test | 2 +- test/tester.tcl | 112 +- test/tkt-2d1a5c67d.test | 2 +- test/tkt2409.test | 4 +- test/tkt2822.test | 5 +- test/tkt3457.test | 14 + test/tkt3762.test | 4 +- test/trigger1.test | 1 + test/trigger3.test | 6 + test/triggerA.test | 7 + test/unique.test | 8 + test/view.test | 35 + test/vtab1.test | 46 +- test/wal.test | 13 +- test/wal5.test | 12 +- test/walfault.test | 40 +- test/where.test | 52 +- test/where2.test | 45 +- test/where8.test | 32 + test/where9.test | 39 +- test/win32lock.test | 1 + test/zerodamage.test | 2 +- tool/build-shell.sh | 3 +- tool/mksqlite3c.tcl | 3 +- tool/mksqlite3h.tcl | 9 +- tool/showdb.c | 107 +- tool/showwal.c | 293 +++- 195 files changed, 7166 insertions(+), 6391 deletions(-) delete mode 100644 src/test_fuzzer.c delete mode 100644 src/test_spellfix.c delete mode 100644 src/test_wholenumber.c diff --git a/Makefile.in b/Makefile.in index 8d9d3c9..052fd8f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -167,6 +167,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ expr.lo fault.lo fkey.lo \ fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \ fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \ + fts3_tokenize_vtab.lo \ fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ func.lo global.lo hash.lo \ icu.lo insert.lo journal.lo legacy.lo loadext.lo \ @@ -316,6 +317,7 @@ SRC += \ $(TOP)/ext/fts3/fts3_tokenizer.h \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_tokenizer1.c \ + $(TOP)/ext/fts3/fts3_tokenize_vtab.c \ $(TOP)/ext/fts3/fts3_unicode.c \ $(TOP)/ext/fts3/fts3_unicode2.c \ $(TOP)/ext/fts3/fts3_write.c @@ -357,8 +359,8 @@ TESTSRC = \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ + $(TOP)/src/test_fs.c \ $(TOP)/src/test_func.c \ - $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ @@ -379,11 +381,22 @@ TESTSRC = \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ - $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_test.c +# Statically linked extensions +# +TESTSRC += \ + $(TOP)/ext/misc/amatch.c \ + $(TOP)/ext/misc/closure.c \ + $(TOP)/ext/misc/fuzzer.c \ + $(TOP)/ext/misc/ieee754.c \ + $(TOP)/ext/misc/nextchar.c \ + $(TOP)/ext/misc/regexp.c \ + $(TOP)/ext/misc/spellfix.c \ + $(TOP)/ext/misc/wholenumber.c + # Source code to the library files needed by the test fixture # TESTSRC2 = \ @@ -398,6 +411,7 @@ TESTSRC2 = \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ $(TOP)/src/wal.c \ + $(TOP)/src/main.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ $(TOP)/src/os_unix.c \ @@ -500,6 +514,11 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h -o $@ $(TOP)/src/shell.c libsqlite3.la \ $(LIBREADLINE) $(TLIBS) -rpath "$(libdir)" +mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c + $(LTLINK) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ + $(TLIBS) -rpath "$(libdir)" + + # This target creates a directory named "tsrc" and fills it with # copies of all of the C source code and header files needed to # build on the target system. Some of the C source code and header @@ -517,6 +536,7 @@ sqlite3$(TEXE): $(TOP)/src/shell.c libsqlite3.la sqlite3.h sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl + cp tsrc/shell.c tsrc/sqlite3ext.h . tclsqlite3.c: sqlite3.c echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c @@ -850,6 +870,9 @@ fts3_tokenizer.lo: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR) fts3_tokenizer1.lo: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c +fts3_tokenize_vtab.lo: $(TOP)/ext/fts3/fts3_tokenize_vtab.c $(HDR) $(EXTHDR) + $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenize_vtab.c + fts3_unicode.lo: $(TOP)/ext/fts3/fts3_unicode.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_unicode.c @@ -876,7 +899,8 @@ TESTFIXTURE_FLAGS += -DBUILD_sqlite TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.la TESTFIXTURE_SRC1 = sqlite3.c -TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) +TESTFIXTURE_SRC = $(TESTSRC) $(TOP)/src/tclsqlite.c +TESTFIXTURE_SRC += $(TESTFIXTURE_SRC$(USE_AMALGAMATION)) testfixture$(TEXE): $(TESTFIXTURE_SRC) $(LTLINK) -DSQLITE_NO_SYNC=1 $(TEMP_STORE) $(TESTFIXTURE_FLAGS) \ @@ -942,8 +966,11 @@ clean: rm -f testfixture$(TEXE) test.db rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def rm -f sqlite3.c + rm -f sqlite3rc.h + rm -f shell.c sqlite3ext.h rm -f sqlite3_analyzer$(TEXE) sqlite3_analyzer.c rm -f sqlite-*-output.vsix + rm -f mptester mptester.exe distclean: clean rm -f config.log config.status libtool Makefile sqlite3.pc diff --git a/Makefile.msc b/Makefile.msc index d12e5d0..57d7065 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -478,7 +478,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ expr.lo fault.lo fkey.lo \ fts3.lo fts3_aux.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo \ fts3_porter.lo fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo \ - fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ + fts3_tokenize_vtab.lo fts3_unicode.lo fts3_unicode2.lo fts3_write.lo \ func.lo global.lo hash.lo \ icu.lo insert.lo journal.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ @@ -638,6 +638,7 @@ SRC = $(SRC) \ $(TOP)\ext\fts3\fts3_tokenizer.h \ $(TOP)\ext\fts3\fts3_tokenizer.c \ $(TOP)\ext\fts3\fts3_tokenizer1.c \ + $(TOP)\ext\fts3\fts3_tokenize_vtab.c \ $(TOP)\ext\fts3\fts3_unicode.c \ $(TOP)\ext\fts3\fts3_unicode2.c \ $(TOP)\ext\fts3\fts3_write.c @@ -678,8 +679,8 @@ TESTSRC = \ $(TOP)\src\test_config.c \ $(TOP)\src\test_demovfs.c \ $(TOP)\src\test_devsym.c \ + $(TOP)\src\test_fs.c \ $(TOP)\src\test_func.c \ - $(TOP)\src\test_fuzzer.c \ $(TOP)\src\test_hexio.c \ $(TOP)\src\test_init.c \ $(TOP)\src\test_intarray.c \ @@ -700,11 +701,23 @@ TESTSRC = \ $(TOP)\src\test_tclvar.c \ $(TOP)\src\test_thread.c \ $(TOP)\src\test_vfs.c \ - $(TOP)\src\test_wholenumber.c \ $(TOP)\src\test_wsd.c \ $(TOP)\ext\fts3\fts3_term.c \ $(TOP)\ext\fts3\fts3_test.c +# Statically linked extensions +# +TESTEXT = \ + $(TOP)\ext\misc\amatch.c \ + $(TOP)\ext\misc\closure.c \ + $(TOP)\ext\misc\fuzzer.c \ + $(TOP)\ext\misc\ieee754.c \ + $(TOP)\ext\misc\nextchar.c \ + $(TOP)\ext\misc\regexp.c \ + $(TOP)\ext\misc\spellfix.c \ + $(TOP)\ext\misc\wholenumber.c + + # Source code to the library files needed by the test fixture # TESTSRC2 = \ @@ -719,6 +732,7 @@ TESTSRC2 = \ $(TOP)\src\func.c \ $(TOP)\src\insert.c \ $(TOP)\src\wal.c \ + $(TOP)\src\main.c \ $(TOP)\src\mem5.c \ $(TOP)\src\os.c \ $(TOP)\src\os_unix.c \ @@ -746,6 +760,7 @@ TESTSRC2 = \ $(TOP)\ext\fts3\fts3_aux.c \ $(TOP)\ext\fts3\fts3_expr.c \ $(TOP)\ext\fts3\fts3_tokenizer.c \ + $(TOP)\ext\fts3\fts3_tokenize_vtab.c \ $(TOP)\ext\fts3\fts3_unicode.c \ $(TOP)\ext\fts3\fts3_unicode2.c \ $(TOP)\ext\fts3\fts3_write.c \ @@ -811,6 +826,10 @@ sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h $(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) + # This target creates a directory named "tsrc" and fills it with # copies of all of the C source code and header files needed to # build on the target system. Some of the C source code and header @@ -828,6 +847,8 @@ sqlite3.exe: $(TOP)\src\shell.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl $(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl + copy tsrc\shell.c . + copy tsrc\sqlite3ext.h . sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl $(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl @@ -1168,6 +1189,9 @@ fts3_tokenizer.lo: $(TOP)\ext\fts3\fts3_tokenizer.c $(HDR) $(EXTHDR) fts3_tokenizer1.lo: $(TOP)\ext\fts3\fts3_tokenizer1.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_tokenizer1.c +fts3_tokenize_vtab.lo: $(TOP)\ext\fts3\fts3_tokenize_vtab.c $(HDR) $(EXTHDR) + $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_tokenize_vtab.c + fts3_unicode.lo: $(TOP)\ext\fts3\fts3_unicode.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)\ext\fts3\fts3_unicode.c @@ -1191,8 +1215,8 @@ rtree.lo: $(TOP)\ext\rtree\rtree.c $(HDR) $(EXTHDR) TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1 TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE -TESTFIXTURE_SRC0 = $(TESTSRC2) libsqlite3.lib -TESTFIXTURE_SRC1 = sqlite3.c +TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) libsqlite3.lib +TESTFIXTURE_SRC1 = $(TESTEXT) sqlite3.c !IF $(USE_AMALGAMATION)==0 TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0) !ELSE @@ -1246,8 +1270,10 @@ clean: del /Q sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def del /Q sqlite3.c del /Q sqlite3rc.h + del /Q shell.c sqlite3ext.h del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp sqlite3_analyzer.c del /Q sqlite-*-output.vsix + del /Q mptester.exe # Dynamic link library section. # diff --git a/Makefile.vxworks b/Makefile.vxworks index d040288..c9058da 100644 --- a/Makefile.vxworks +++ b/Makefile.vxworks @@ -662,4 +662,6 @@ clean: rm -rf tsrc target_source rm -f testloadext.dll libtestloadext.so rm -f sqlite3.c fts?amal.c tclsqlite3.c + rm -f sqlite3rc.h + rm -f shell.c sqlite3ext.h rm -f $(SHPREFIX)sqlite3.$(SO) diff --git a/VERSION b/VERSION index 305df92..9a4c437 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.15.2 +3.7.17 diff --git a/configure b/configure index f4ce0c6..90452c9 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.7.15.2. +# Generated by GNU Autoconf 2.62 for sqlite 3.7.17. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. @@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.7.15.2' -PACKAGE_STRING='sqlite 3.7.15.2' +PACKAGE_VERSION='3.7.17' +PACKAGE_STRING='sqlite 3.7.17' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. @@ -1484,7 +1484,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.7.15.2 to adapt to many kinds of systems. +\`configure' configures sqlite 3.7.17 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1549,7 +1549,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.7.15.2:";; + short | recursive ) echo "Configuration of sqlite 3.7.17:";; esac cat <<\_ACEOF @@ -1665,7 +1665,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.7.15.2 +sqlite configure 3.7.17 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1679,7 +1679,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.7.15.2, which was +It was created by sqlite $as_me 3.7.17, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -3733,13 +3733,13 @@ if test "${lt_cv_nm_interface+set}" = set; then else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext - (eval echo "\"\$as_me:3737: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3736: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3740: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3739: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3743: output\"" >&5) + (eval echo "\"\$as_me:3742: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -4961,7 +4961,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4965 "configure"' > conftest.$ac_ext + echo '#line 4964 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6830,11 +6830,11 @@ else -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:6834: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6833: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6838: \$? = $ac_status" >&5 + echo "$as_me:6837: \$? = $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. @@ -7169,11 +7169,11 @@ else -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:7173: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7172: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7177: \$? = $ac_status" >&5 + echo "$as_me:7176: \$? = $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. @@ -7274,11 +7274,11 @@ else -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:7278: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7277: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7282: \$? = $ac_status" >&5 + echo "$as_me:7281: \$? = $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 @@ -7329,11 +7329,11 @@ else -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:7333: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7332: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7337: \$? = $ac_status" >&5 + echo "$as_me:7336: \$? = $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 @@ -10142,7 +10142,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10146 "configure" +#line 10145 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10238,7 +10238,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10242 "configure" +#line 10241 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12908,7 +12908,6 @@ $as_echo "file not found" >&6; } - fi fi if test "${use_tcl}" = "no" ; then @@ -14033,7 +14032,7 @@ exec 6>&1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.7.15.2, which was +This file was extended by sqlite $as_me 3.7.17, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14086,7 +14085,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.7.15.2 +sqlite config.status 3.7.17 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" @@ -14519,8 +14518,7 @@ $debug || if test -n "$CONFIG_FILES"; then -ac_cr=' -' +ac_cr=' ' ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' @@ -15755,3 +15753,4 @@ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:$LINENO: WARNING: Unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: Unrecognized options: $ac_unrecognized_opts" >&2;} fi + diff --git a/ext/fts1/ft_hash.h b/ext/fts1/ft_hash.h index 93b6dcf..95871a4 100644 --- a/ext/fts1/ft_hash.h +++ b/ext/fts1/ft_hash.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This is the header file for the generic hash-table implemenation +** This is the header file for the generic hash-table implementation ** used in SQLite. We've modified it slightly to serve as a standalone ** hash table implementation for the full-text indexing module. ** diff --git a/ext/fts1/fts1_hash.h b/ext/fts1/fts1_hash.h index c31c430..9001152 100644 --- a/ext/fts1/fts1_hash.h +++ b/ext/fts1/fts1_hash.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This is the header file for the generic hash-table implemenation +** This is the header file for the generic hash-table implementation ** used in SQLite. We've modified it slightly to serve as a standalone ** hash table implementation for the full-text indexing module. ** diff --git a/ext/fts2/fts2.c b/ext/fts2/fts2.c index 93e03cd..f008ce6 100644 --- a/ext/fts2/fts2.c +++ b/ext/fts2/fts2.c @@ -6779,7 +6779,7 @@ void sqlite3Fts2IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); int sqlite3Fts2InitHashTable(sqlite3 *, fts2Hash *, const char *); /* -** Initialise the fts2 extension. If this extension is built as part +** Initialize the fts2 extension. If this extension is built as part ** of the sqlite library, then this function is called directly by ** SQLite. If fts2 is built as a dynamically loadable extension, this ** function is called by the sqlite3_extension_init() entry point. @@ -6797,7 +6797,7 @@ int sqlite3Fts2Init(sqlite3 *db){ sqlite3Fts2IcuTokenizerModule(&pIcu); #endif - /* Allocate and initialise the hash-table used to store tokenizers. */ + /* Allocate and initialize the hash-table used to store tokenizers. */ pHash = sqlite3_malloc(sizeof(fts2Hash)); if( !pHash ){ rc = SQLITE_NOMEM; diff --git a/ext/fts2/fts2_hash.h b/ext/fts2/fts2_hash.h index 571aa2c..02936f1 100644 --- a/ext/fts2/fts2_hash.h +++ b/ext/fts2/fts2_hash.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This is the header file for the generic hash-table implemenation +** This is the header file for the generic hash-table implementation ** used in SQLite. We've modified it slightly to serve as a standalone ** hash table implementation for the full-text indexing module. ** diff --git a/ext/fts2/fts2_tokenizer.c b/ext/fts2/fts2_tokenizer.c index f8b0663..a93790c 100644 --- a/ext/fts2/fts2_tokenizer.c +++ b/ext/fts2/fts2_tokenizer.c @@ -319,7 +319,7 @@ static void intTestFunc( /* ** Set up SQL objects in database db used to access the contents of ** the hash table pointed to by argument pHash. The hash table must -** been initialised to use string keys, and to take a private copy +** been initialized to use string keys, and to take a private copy ** of the key when a value is inserted. i.e. by a call similar to: ** ** sqlite3Fts2HashInit(pHash, FTS2_HASH_STRING, 1); diff --git a/ext/fts2/fts2_tokenizer.h b/ext/fts2/fts2_tokenizer.h index 8c256b2..8db2048 100644 --- a/ext/fts2/fts2_tokenizer.h +++ b/ext/fts2/fts2_tokenizer.h @@ -70,7 +70,7 @@ struct sqlite3_tokenizer_module { ** This method should return either SQLITE_OK (0), or an SQLite error ** code. If SQLITE_OK is returned, then *ppTokenizer should be set ** to point at the newly created tokenizer structure. The generic - ** sqlite3_tokenizer.pModule variable should not be initialised by + ** sqlite3_tokenizer.pModule variable should not be initialized by ** this callback. The caller will do so. */ int (*xCreate)( diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c index 7a53ac3..c00a13f 100644 --- a/ext/fts3/fts3.c +++ b/ext/fts3/fts3.c @@ -1571,7 +1571,7 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ }else{ rc = sqlite3_reset(pCsr->pStmt); if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){ - /* If no row was found and no error has occured, then the %_content + /* If no row was found and no error has occurred, then the %_content ** table is missing a row that is present in the full-text index. ** The data structures are corrupt. */ rc = FTS_CORRUPT_VTAB; @@ -2811,7 +2811,7 @@ static void fts3SegReaderCursorFree(Fts3MultiSegReader *pSegcsr){ } /* -** This function retreives the doclist for the specified term (or term +** This function retrieves the doclist for the specified term (or term ** prefix) from the database. */ static int fts3TermSelect( @@ -2975,14 +2975,12 @@ static int fts3FilterMethod( pCsr->iLangid = 0; if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]); + assert( p->base.zErrMsg==0 ); rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid, - p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr + p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr, + &p->base.zErrMsg ); if( rc!=SQLITE_OK ){ - if( rc==SQLITE_ERROR ){ - static const char *zErr = "malformed MATCH expression: [%s]"; - p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery); - } return rc; } @@ -3562,7 +3560,7 @@ void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); #endif /* -** Initialise the fts3 extension. If this extension is built as part +** Initialize the fts3 extension. If this extension is built as part ** of the sqlite library, then this function is called directly by ** SQLite. If fts3 is built as a dynamically loadable extension, this ** function is called by the sqlite3_extension_init() entry point. @@ -3596,7 +3594,7 @@ int sqlite3Fts3Init(sqlite3 *db){ sqlite3Fts3SimpleTokenizerModule(&pSimple); sqlite3Fts3PorterTokenizerModule(&pPorter); - /* Allocate and initialise the hash-table used to store tokenizers. */ + /* Allocate and initialize the hash-table used to store tokenizers. */ pHash = sqlite3_malloc(sizeof(Fts3Hash)); if( !pHash ){ rc = SQLITE_NOMEM; @@ -3646,9 +3644,13 @@ int sqlite3Fts3Init(sqlite3 *db){ db, "fts4", &fts3Module, (void *)pHash, 0 ); } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3InitTok(db, (void *)pHash); + } return rc; } + /* An error has occurred. Delete the hash table and return the error code. */ assert( rc!=SQLITE_OK ); if( pHash ){ @@ -5195,7 +5197,7 @@ int sqlite3Fts3EvalPhraseStats( ** of the current row. ** ** More specifically, the returned buffer contains 1 varint for each -** occurence of the phrase in the column, stored using the normal (delta+2) +** occurrence of the phrase in the column, stored using the normal (delta+2) ** compression and is terminated by either an 0x01 or 0x00 byte. For example, ** if the requested column contains "a b X c d X X" and the position-list ** for 'X' is requested, the buffer returned may contain: diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h index 77ca470..b19064c 100644 --- a/ext/fts3/fts3Int.h +++ b/ext/fts3/fts3Int.h @@ -524,7 +524,7 @@ void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *); /* fts3_expr.c */ int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int, - char **, int, int, int, const char *, int, Fts3Expr ** + char **, int, int, int, const char *, int, Fts3Expr **, char ** ); void sqlite3Fts3ExprFree(Fts3Expr *); #ifdef SQLITE_TEST @@ -549,6 +549,9 @@ int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **); int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); +/* fts3_tokenize_vtab.c */ +int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *); + /* fts3_unicode2.c (functions generated by parsing unicode text files) */ #ifdef SQLITE_ENABLE_FTS4_UNICODE61 int sqlite3FtsUnicodeFold(int, int); diff --git a/ext/fts3/fts3_aux.c b/ext/fts3/fts3_aux.c index a2bff2e..9b582fc 100644 --- a/ext/fts3/fts3_aux.c +++ b/ext/fts3/fts3_aux.c @@ -70,17 +70,26 @@ static int fts3auxConnectMethod( UNUSED_PARAMETER(pUnused); - /* The user should specify a single argument - the name of an fts3 table. */ - if( argc!=4 ){ - *pzErr = sqlite3_mprintf( - "wrong number of arguments to fts4aux constructor" - ); - return SQLITE_ERROR; - } + /* The user should invoke this in one of two forms: + ** + ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table); + ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table-db, fts4-table); + */ + if( argc!=4 && argc!=5 ) goto bad_args; zDb = argv[1]; nDb = (int)strlen(zDb); - zFts3 = argv[3]; + if( argc==5 ){ + if( nDb==4 && 0==sqlite3_strnicmp("temp", zDb, 4) ){ + zDb = argv[3]; + nDb = (int)strlen(zDb); + zFts3 = argv[4]; + }else{ + goto bad_args; + } + }else{ + zFts3 = argv[3]; + } nFts3 = (int)strlen(zFts3); rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); @@ -103,6 +112,10 @@ static int fts3auxConnectMethod( *ppVtab = (sqlite3_vtab *)p; return SQLITE_OK; + + bad_args: + *pzErr = sqlite3_mprintf("invalid arguments to fts4aux constructor"); + return SQLITE_ERROR; } /* diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c index 7612789..c046d7d 100644 --- a/ext/fts3/fts3_expr.c +++ b/ext/fts3/fts3_expr.c @@ -106,7 +106,7 @@ struct ParseContext { ** This function is equivalent to the standard isspace() function. ** ** The standard isspace() can be awkward to use safely, because although it -** is defined to accept an argument of type int, its behaviour when passed +** is defined to accept an argument of type int, its behavior when passed ** an integer that falls outside of the range of the unsigned char type ** is undefined (and sometimes, "undefined" means segfault). This wrapper ** is defined to accept an argument of type char, and always returns 0 for @@ -640,8 +640,10 @@ static int fts3ExprParse( } pNot->eType = FTSQUERY_NOT; pNot->pRight = p; + p->pParent = pNot; if( pNotBranch ){ pNot->pLeft = pNotBranch; + pNotBranch->pParent = pNot; } pNotBranch = pNot; p = pPrev; @@ -729,6 +731,7 @@ static int fts3ExprParse( pIter = pIter->pLeft; } pIter->pLeft = pRet; + pRet->pParent = pIter; pRet = pNotBranch; } } @@ -745,6 +748,223 @@ exprparse_out: return rc; } +/* +** Return SQLITE_ERROR if the maximum depth of the expression tree passed +** as the only argument is more than nMaxDepth. +*/ +static int fts3ExprCheckDepth(Fts3Expr *p, int nMaxDepth){ + int rc = SQLITE_OK; + if( p ){ + if( nMaxDepth<0 ){ + rc = SQLITE_TOOBIG; + }else{ + rc = fts3ExprCheckDepth(p->pLeft, nMaxDepth-1); + if( rc==SQLITE_OK ){ + rc = fts3ExprCheckDepth(p->pRight, nMaxDepth-1); + } + } + } + return rc; +} + +/* +** This function attempts to transform the expression tree at (*pp) to +** an equivalent but more balanced form. The tree is modified in place. +** If successful, SQLITE_OK is returned and (*pp) set to point to the +** new root expression node. +** +** nMaxDepth is the maximum allowable depth of the balanced sub-tree. +** +** Otherwise, if an error occurs, an SQLite error code is returned and +** expression (*pp) freed. +*/ +static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){ + int rc = SQLITE_OK; /* Return code */ + Fts3Expr *pRoot = *pp; /* Initial root node */ + Fts3Expr *pFree = 0; /* List of free nodes. Linked by pParent. */ + int eType = pRoot->eType; /* Type of node in this tree */ + + if( nMaxDepth==0 ){ + rc = SQLITE_ERROR; + } + + if( rc==SQLITE_OK && (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){ + Fts3Expr **apLeaf; + apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth); + if( 0==apLeaf ){ + rc = SQLITE_NOMEM; + }else{ + memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth); + } + + if( rc==SQLITE_OK ){ + int i; + Fts3Expr *p; + + /* Set $p to point to the left-most leaf in the tree of eType nodes. */ + for(p=pRoot; p->eType==eType; p=p->pLeft){ + assert( p->pParent==0 || p->pParent->pLeft==p ); + assert( p->pLeft && p->pRight ); + } + + /* This loop runs once for each leaf in the tree of eType nodes. */ + while( 1 ){ + int iLvl; + Fts3Expr *pParent = p->pParent; /* Current parent of p */ + + assert( pParent==0 || pParent->pLeft==p ); + p->pParent = 0; + if( pParent ){ + pParent->pLeft = 0; + }else{ + pRoot = 0; + } + rc = fts3ExprBalance(&p, nMaxDepth-1); + if( rc!=SQLITE_OK ) break; + + for(iLvl=0; p && iLvlpLeft = apLeaf[iLvl]; + pFree->pRight = p; + pFree->pLeft->pParent = pFree; + pFree->pRight->pParent = pFree; + + p = pFree; + pFree = pFree->pParent; + p->pParent = 0; + apLeaf[iLvl] = 0; + } + } + if( p ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_TOOBIG; + break; + } + + /* If that was the last leaf node, break out of the loop */ + if( pParent==0 ) break; + + /* Set $p to point to the next leaf in the tree of eType nodes */ + for(p=pParent->pRight; p->eType==eType; p=p->pLeft); + + /* Remove pParent from the original tree. */ + assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent ); + pParent->pRight->pParent = pParent->pParent; + if( pParent->pParent ){ + pParent->pParent->pLeft = pParent->pRight; + }else{ + assert( pParent==pRoot ); + pRoot = pParent->pRight; + } + + /* Link pParent into the free node list. It will be used as an + ** internal node of the new tree. */ + pParent->pParent = pFree; + pFree = pParent; + } + + if( rc==SQLITE_OK ){ + p = 0; + for(i=0; ipParent = 0; + }else{ + assert( pFree!=0 ); + pFree->pRight = p; + pFree->pLeft = apLeaf[i]; + pFree->pLeft->pParent = pFree; + pFree->pRight->pParent = pFree; + + p = pFree; + pFree = pFree->pParent; + p->pParent = 0; + } + } + } + pRoot = p; + }else{ + /* An error occurred. Delete the contents of the apLeaf[] array + ** and pFree list. Everything else is cleaned up by the call to + ** sqlite3Fts3ExprFree(pRoot) below. */ + Fts3Expr *pDel; + for(i=0; ipParent; + sqlite3_free(pDel); + } + } + + assert( pFree==0 ); + sqlite3_free( apLeaf ); + } + } + + if( rc!=SQLITE_OK ){ + sqlite3Fts3ExprFree(pRoot); + pRoot = 0; + } + *pp = pRoot; + return rc; +} + +/* +** This function is similar to sqlite3Fts3ExprParse(), with the following +** differences: +** +** 1. It does not do expression rebalancing. +** 2. It does not check that the expression does not exceed the +** maximum allowable depth. +** 3. Even if it fails, *ppExpr may still be set to point to an +** expression tree. It should be deleted using sqlite3Fts3ExprFree() +** in this case. +*/ +static int fts3ExprParseUnbalanced( + sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ + int iLangid, /* Language id for tokenizer */ + char **azCol, /* Array of column names for fts3 table */ + int bFts4, /* True to allow FTS4-only syntax */ + int nCol, /* Number of entries in azCol[] */ + int iDefaultCol, /* Default column to query */ + const char *z, int n, /* Text of MATCH query */ + Fts3Expr **ppExpr /* OUT: Parsed query structure */ +){ + int nParsed; + int rc; + ParseContext sParse; + + memset(&sParse, 0, sizeof(ParseContext)); + sParse.pTokenizer = pTokenizer; + sParse.iLangid = iLangid; + sParse.azCol = (const char **)azCol; + sParse.nCol = nCol; + sParse.iDefaultCol = iDefaultCol; + sParse.bFts4 = bFts4; + if( z==0 ){ + *ppExpr = 0; + return SQLITE_OK; + } + if( n<0 ){ + n = (int)strlen(z); + } + rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed); + assert( rc==SQLITE_OK || *ppExpr==0 ); + + /* Check for mismatched parenthesis */ + if( rc==SQLITE_OK && sParse.nNest ){ + rc = SQLITE_ERROR; + } + + return rc; +} + /* ** Parameters z and n contain a pointer to and length of a buffer containing ** an fts3 query expression, respectively. This function attempts to parse the @@ -777,49 +997,74 @@ int sqlite3Fts3ExprParse( int nCol, /* Number of entries in azCol[] */ int iDefaultCol, /* Default column to query */ const char *z, int n, /* Text of MATCH query */ - Fts3Expr **ppExpr /* OUT: Parsed query structure */ + Fts3Expr **ppExpr, /* OUT: Parsed query structure */ + char **pzErr /* OUT: Error message (sqlite3_malloc) */ ){ - int nParsed; - int rc; - ParseContext sParse; - - memset(&sParse, 0, sizeof(ParseContext)); - sParse.pTokenizer = pTokenizer; - sParse.iLangid = iLangid; - sParse.azCol = (const char **)azCol; - sParse.nCol = nCol; - sParse.iDefaultCol = iDefaultCol; - sParse.bFts4 = bFts4; - if( z==0 ){ - *ppExpr = 0; - return SQLITE_OK; + static const int MAX_EXPR_DEPTH = 12; + int rc = fts3ExprParseUnbalanced( + pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr + ); + + /* Rebalance the expression. And check that its depth does not exceed + ** MAX_EXPR_DEPTH. */ + if( rc==SQLITE_OK && *ppExpr ){ + rc = fts3ExprBalance(ppExpr, MAX_EXPR_DEPTH); + if( rc==SQLITE_OK ){ + rc = fts3ExprCheckDepth(*ppExpr, MAX_EXPR_DEPTH); + } } - if( n<0 ){ - n = (int)strlen(z); - } - rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed); - /* Check for mismatched parenthesis */ - if( rc==SQLITE_OK && sParse.nNest ){ - rc = SQLITE_ERROR; + if( rc!=SQLITE_OK ){ sqlite3Fts3ExprFree(*ppExpr); *ppExpr = 0; + if( rc==SQLITE_TOOBIG ){ + *pzErr = sqlite3_mprintf( + "FTS expression tree is too large (maximum depth %d)", MAX_EXPR_DEPTH + ); + rc = SQLITE_ERROR; + }else if( rc==SQLITE_ERROR ){ + *pzErr = sqlite3_mprintf("malformed MATCH expression: [%s]", z); + } } return rc; } /* -** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse(). +** Free a single node of an expression tree. */ -void sqlite3Fts3ExprFree(Fts3Expr *p){ - if( p ){ - assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 ); - sqlite3Fts3ExprFree(p->pLeft); - sqlite3Fts3ExprFree(p->pRight); - sqlite3Fts3EvalPhraseCleanup(p->pPhrase); - sqlite3_free(p->aMI); - sqlite3_free(p); +static void fts3FreeExprNode(Fts3Expr *p){ + assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 ); + sqlite3Fts3EvalPhraseCleanup(p->pPhrase); + sqlite3_free(p->aMI); + sqlite3_free(p); +} + +/* +** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse(). +** +** This function would be simpler if it recursively called itself. But +** that would mean passing a sufficiently large expression to ExprParse() +** could cause a stack overflow. +*/ +void sqlite3Fts3ExprFree(Fts3Expr *pDel){ + Fts3Expr *p; + assert( pDel==0 || pDel->pParent==0 ); + for(p=pDel; p && (p->pLeft||p->pRight); p=(p->pLeft ? p->pLeft : p->pRight)){ + assert( p->pParent==0 || p==p->pParent->pRight || p==p->pParent->pLeft ); + } + while( p ){ + Fts3Expr *pParent = p->pParent; + fts3FreeExprNode(p); + if( pParent && p==pParent->pLeft && pParent->pRight ){ + p = pParent->pRight; + while( p && (p->pLeft || p->pRight) ){ + assert( p==p->pParent->pRight || p==p->pParent->pLeft ); + p = (p->pLeft ? p->pLeft : p->pRight); + } + }else{ + p = pParent; + } } } @@ -871,6 +1116,9 @@ static int queryTestTokenizer( ** the returned expression text and then freed using sqlite3_free(). */ static char *exprToString(Fts3Expr *pExpr, char *zBuf){ + if( pExpr==0 ){ + return sqlite3_mprintf(""); + } switch( pExpr->eType ){ case FTSQUERY_PHRASE: { Fts3Phrase *pPhrase = pExpr->pPhrase; @@ -978,10 +1226,21 @@ static void fts3ExprTest( azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]); } - rc = sqlite3Fts3ExprParse( - pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr - ); + if( sqlite3_user_data(context) ){ + char *zDummy = 0; + rc = sqlite3Fts3ExprParse( + pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr, &zDummy + ); + assert( rc==SQLITE_OK || pExpr==0 ); + sqlite3_free(zDummy); + }else{ + rc = fts3ExprParseUnbalanced( + pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr + ); + } + if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ + sqlite3Fts3ExprFree(pExpr); sqlite3_result_error(context, "Error parsing expression", -1); }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ sqlite3_result_error_nomem(context); @@ -1004,9 +1263,15 @@ exprtest_out: ** with database connection db. */ int sqlite3Fts3ExprInitTestInterface(sqlite3* db){ - return sqlite3_create_function( + int rc = sqlite3_create_function( db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0 ); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "fts3_exprtest_rebalance", + -1, SQLITE_UTF8, (void *)1, fts3ExprTest, 0, 0 + ); + } + return rc; } #endif diff --git a/ext/fts3/fts3_hash.h b/ext/fts3/fts3_hash.h index 399f515..dc3fcf8 100644 --- a/ext/fts3/fts3_hash.h +++ b/ext/fts3/fts3_hash.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This is the header file for the generic hash-table implemenation +** This is the header file for the generic hash-table implementation ** used in SQLite. We've modified it slightly to serve as a standalone ** hash table implementation for the full-text indexing module. ** diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c index 4bee014..d54a787 100644 --- a/ext/fts3/fts3_snippet.c +++ b/ext/fts3/fts3_snippet.c @@ -389,9 +389,9 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ ** is the snippet with the highest score, where scores are calculated ** by adding: ** -** (a) +1 point for each occurence of a matchable phrase in the snippet. +** (a) +1 point for each occurrence of a matchable phrase in the snippet. ** -** (b) +1000 points for the first occurence of each matchable phrase in +** (b) +1000 points for the first occurrence of each matchable phrase in ** the snippet for which the corresponding mCovered bit is not set. ** ** The selected snippet parameters are stored in structure *pFragment before diff --git a/ext/fts3/fts3_test.c b/ext/fts3/fts3_test.c index 4da0b8f..75ec6bd 100644 --- a/ext/fts3/fts3_test.c +++ b/ext/fts3/fts3_test.c @@ -267,7 +267,7 @@ static int fts3_near_match_cmd( ** ** Whether or not the arguments are present, this command returns a list of ** two integers - the initial chunksize and threshold when the command is -** invoked. This can be used to restore the default behaviour after running +** invoked. This can be used to restore the default behavior after running ** tests. For example: ** ** # Override incr-load settings for testing: diff --git a/ext/fts3/fts3_tokenizer.c b/ext/fts3/fts3_tokenizer.c index 8241be8..04f8446 100644 --- a/ext/fts3/fts3_tokenizer.c +++ b/ext/fts3/fts3_tokenizer.c @@ -428,7 +428,7 @@ static void intTestFunc( /* ** Set up SQL objects in database db used to access the contents of ** the hash table pointed to by argument pHash. The hash table must -** been initialised to use string keys, and to take a private copy +** been initialized to use string keys, and to take a private copy ** of the key when a value is inserted. i.e. by a call similar to: ** ** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); diff --git a/ext/fts3/fts3_tokenizer.h b/ext/fts3/fts3_tokenizer.h index c91c7ed..4a40b2b 100644 --- a/ext/fts3/fts3_tokenizer.h +++ b/ext/fts3/fts3_tokenizer.h @@ -70,7 +70,7 @@ struct sqlite3_tokenizer_module { ** This method should return either SQLITE_OK (0), or an SQLite error ** code. If SQLITE_OK is returned, then *ppTokenizer should be set ** to point at the newly created tokenizer structure. The generic - ** sqlite3_tokenizer.pModule variable should not be initialised by + ** sqlite3_tokenizer.pModule variable should not be initialized by ** this callback. The caller will do so. */ int (*xCreate)( diff --git a/ext/fts3/fts3_unicode.c b/ext/fts3/fts3_unicode.c index 79941ed..188358e 100644 --- a/ext/fts3/fts3_unicode.c +++ b/ext/fts3/fts3_unicode.c @@ -125,7 +125,7 @@ static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){ ** ** If a standalone diacritic mark (one that sqlite3FtsUnicodeIsdiacritic() ** identifies as a diacritic) occurs in the zIn/nIn string it is ignored. -** It is not possible to change the behaviour of the tokenizer with respect +** It is not possible to change the behavior of the tokenizer with respect ** to these codepoints. */ static int unicodeAddExceptions( diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c index bda7fbb..269d1dd 100644 --- a/ext/fts3/fts3_write.c +++ b/ext/fts3/fts3_write.c @@ -1482,6 +1482,7 @@ static int fts3SegReaderNextDocid( *pnOffsetList = (int)(p - pReader->pOffsetList - 1); } + /* List may have been edited in place by fts3EvalNearTrim() */ while( papSegment, nMerge, j, xCmp); + if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){ + rc = fts3MsrBufferData(pMsr, pList, nList+1); + if( rc!=SQLITE_OK ) return rc; + assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); + pList = pMsr->aBuffer; + } + if( pMsr->iColFilter>=0 ){ - fts3ColumnFilter(pMsr->iColFilter, &pList, &nList); + fts3ColumnFilter(pMsr->iColFilter, 1, &pList, &nList); } if( nList>0 ){ - if( fts3SegReaderIsPending(apSegment[0]) ){ - rc = fts3MsrBufferData(pMsr, pList, nList+1); - if( rc!=SQLITE_OK ) return rc; - *paPoslist = pMsr->aBuffer; - assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); - }else{ - *paPoslist = pList; - } + *paPoslist = pList; *piDocid = iDocid; *pnPoslist = nList; break; @@ -2856,7 +2864,7 @@ int sqlite3Fts3SegReaderStep( } if( isColFilter ){ - fts3ColumnFilter(pFilter->iCol, &pList, &nList); + fts3ColumnFilter(pFilter->iCol, 0, &pList, &nList); } if( !isIgnoreEmpty || nList>0 ){ diff --git a/ext/icu/README.txt b/ext/icu/README.txt index c5cadb5..d744f74 100644 --- a/ext/icu/README.txt +++ b/ext/icu/README.txt @@ -98,7 +98,7 @@ SQLite. Documentation follows. REGEXP This extension uses the ICU defaults for regular expression matching - behaviour. Specifically, this means that: + behavior. Specifically, this means that: * Matching is case-sensitive, * Regular expression comments are not allowed within patterns, and diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 731cb32..16a316f 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -2665,7 +2665,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ RtreeNode *pRoot; /* Root node of rtree structure */ - /* Obtain a reference to the root node to initialise Rtree.iDepth */ + /* Obtain a reference to the root node to initialize Rtree.iDepth */ rc = nodeAcquire(pRtree, 1, 0, &pRoot); /* Obtain a reference to the leaf node that contains the entry @@ -3049,7 +3049,8 @@ static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){ static int getNodeSize( sqlite3 *db, /* Database handle */ Rtree *pRtree, /* Rtree handle */ - int isCreate /* True for xCreate, false for xConnect */ + int isCreate, /* True for xCreate, false for xConnect */ + char **pzErr /* OUT: Error message, if any */ ){ int rc; char *zSql; @@ -3062,6 +3063,8 @@ static int getNodeSize( if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)iNodeSize ){ pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS; } + }else{ + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } }else{ zSql = sqlite3_mprintf( @@ -3069,6 +3072,9 @@ static int getNodeSize( pRtree->zDb, pRtree->zName ); rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); + } } sqlite3_free(zSql); @@ -3132,7 +3138,7 @@ static int rtreeInit( memcpy(pRtree->zName, argv[2], nName); /* Figure out the node size to use. */ - rc = getNodeSize(db, pRtree, isCreate); + rc = getNodeSize(db, pRtree, isCreate, pzErr); /* Create/Connect to the underlying relational database schema. If ** that is successful, call sqlite3_declare_vtab() to configure diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test index e3c7d68..275b132 100644 --- a/ext/rtree/rtree1.test +++ b/ext/rtree/rtree1.test @@ -17,6 +17,7 @@ if {![info exists testdir]} { } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl +set testprefix rtree1 # Test plan: # diff --git a/ext/rtree/rtree5.test b/ext/rtree/rtree5.test index 8990772..8ff90b0 100644 --- a/ext/rtree/rtree5.test +++ b/ext/rtree/rtree5.test @@ -61,7 +61,7 @@ do_test rtree5-1.9 { do_test rtree5-1.10 { execsql { SELECT (1<<31)-5, (1<<31)-1, -1*(1<<31), -1*(1<<31)+5 } } {2147483643 2147483647 -2147483648 -2147483643} -do_test rtree5-1.10 { +do_test rtree5-1.11 { execsql { INSERT INTO t1 VALUES(2, (1<<31)-5, (1<<31)-1, -1*(1<<31), -1*(1<<31)+5) } diff --git a/main.mk b/main.mk index cbae400..912ebb4 100644 --- a/main.mk +++ b/main.mk @@ -55,6 +55,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o \ + fts3_tokenize_vtab.o \ fts3_unicode.o fts3_unicode2.o \ fts3_write.o func.o global.o hash.o \ icu.o insert.o journal.o legacy.o loadext.o \ @@ -197,6 +198,7 @@ SRC += \ $(TOP)/ext/fts3/fts3_tokenizer.h \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_tokenizer1.c \ + $(TOP)/ext/fts3/fts3_tokenize_vtab.c \ $(TOP)/ext/fts3/fts3_unicode.c \ $(TOP)/ext/fts3/fts3_unicode2.c \ $(TOP)/ext/fts3/fts3_write.c @@ -240,8 +242,8 @@ TESTSRC = \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ + $(TOP)/src/test_fs.c \ $(TOP)/src/test_func.c \ - $(TOP)/src/test_fuzzer.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ @@ -263,9 +265,21 @@ TESTSRC = \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ - $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c +# Extensions to be statically loaded. +# +TESTSRC += \ + $(TOP)/ext/misc/amatch.c \ + $(TOP)/ext/misc/closure.c \ + $(TOP)/ext/misc/fuzzer.c \ + $(TOP)/ext/misc/ieee754.c \ + $(TOP)/ext/misc/nextchar.c \ + $(TOP)/ext/misc/regexp.c \ + $(TOP)/ext/misc/spellfix.c \ + $(TOP)/ext/misc/wholenumber.c + + #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c @@ -279,6 +293,7 @@ TESTSRC2 = \ $(TOP)/src/func.c \ $(TOP)/src/insert.c \ $(TOP)/src/wal.c \ + $(TOP)/src/main.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ $(TOP)/src/os_unix.c \ @@ -363,6 +378,10 @@ sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TOP)/src/shell.c \ libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB) +mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c + $(TCCX) -o $@ -I. $(TOP)/mptest/mptest.c sqlite3.c \ + $(TLIBS) $(THREADLIB) + sqlite3.o: sqlite3.c $(TCCX) -c sqlite3.c @@ -383,6 +402,7 @@ target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl tclsh $(TOP)/tool/mksqlite3c.tcl + cp tsrc/shell.c tsrc/sqlite3ext.h . echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c cat sqlite3.c >>tclsqlite3.c echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c @@ -509,6 +529,9 @@ fts3_tokenizer.o: $(TOP)/ext/fts3/fts3_tokenizer.c $(HDR) $(EXTHDR) fts3_tokenizer1.o: $(TOP)/ext/fts3/fts3_tokenizer1.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenizer1.c +fts3_tokenize_vtab.o: $(TOP)/ext/fts3/fts3_tokenize_vtab.c $(HDR) $(EXTHDR) + $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_tokenize_vtab.c + fts3_unicode.o: $(TOP)/ext/fts3/fts3_unicode.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_unicode.c @@ -619,5 +642,8 @@ clean: rm -f testfixture testfixture.exe rm -f threadtest3 threadtest3.exe rm -f sqlite3.c fts?amal.c tclsqlite3.c + rm -f sqlite3rc.h + rm -f shell.c sqlite3ext.h rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c rm -f sqlite-*-output.vsix + rm -f mptester mptester.exe diff --git a/manifest b/manifest index 853853f..5b3b906 100644 --- a/manifest +++ b/manifest @@ -1,12 +1,12 @@ -C Version\s3.7.15.2\srelease\scandidate -D 2013-01-09T11:53:05.549 +C Version\s3.7.17 +D 2013-05-20T00:56:22.515 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f -F Makefile.in 690d441a758cbffd13e814dc2724a721a6ebd400 +F Makefile.in f6b58b7bdf6535f0f0620c486dd59aa4662c0b4f F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 -F Makefile.msc 5a3b6f34d263b01f8b798c291fac1529fd650308 -F Makefile.vxworks b18ad88e9a8c6a001f5cf4a389116a4f1a7ab45f +F Makefile.msc 5dc042f51187414d5886ac6d8308630d484690c4 +F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 -F VERSION b5fc9136ddbcdd63702e99fea1b4e83960f52006 +F VERSION 05c7bd63b96f31cfdef5c766ed91307ac121f5aa F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 @@ -15,7 +15,7 @@ F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure a417189892a5a3c54a73ac96583e3671bddc8db7 x +F configure 8bb8bd13d3c918c4c1c73480930e81f955ac298a x F configure.ac 81c43d151d0b0e406be056394cc9ff4cb3fd0444 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 @@ -27,11 +27,11 @@ F ext/async/sqlite3async.c b5a3e30f538a9ffe81538b3063b4d5963f9bb422 F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b -F ext/fts1/ft_hash.h 1a35e654a235c2c662d3ca0dfc3138ad60b8b7d5 +F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea F ext/fts1/fts1.c 3e7b253e61aab0bb1fea808c7a0ce36c19432acc F ext/fts1/fts1.h 6060b8f62c1d925ea8356cb1a6598073eb9159a6 F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114 -F ext/fts1/fts1_hash.h 957d378355ed29f672cd5add012ce8b088a5e089 +F ext/fts1/fts1_hash.h e7f0d761353996a8175eda351104acfde23afcb0 F ext/fts1/fts1_porter.c b1c7304b8988ba3f764a147cdd32043b4913ea7b F ext/fts1/fts1_tokenizer.h fdea722c38a9f82ed921642981234f666e47919c F ext/fts1/fts1_tokenizer1.c fd00d1fe4dc30dfc5c64cba695ce34f4af20d2fa @@ -41,55 +41,65 @@ F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9 F ext/fts2/README.tokenizers 21e3684ea5a095b55d70f6878b4ce6af5932dfb7 F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts2/fts2.c 4ef7d7ecf590da0dd416ac54612c53a7d4175790 +F ext/fts2/fts2.c b48cc0bb657c0a421f4067b79aa0354bd16a046d F ext/fts2/fts2.h da5f76c65163301d1068a971fd32f4119e3c95fa F ext/fts2/fts2_hash.c 2689e42e1107ea67207f725cf69cf8972d00cf93 -F ext/fts2/fts2_hash.h 9a5b1be94664139f93217a0770d7144425cffb3a +F ext/fts2/fts2_hash.h 1824b99dfd8d0225facbdb26a2c87289b2e7dcf8 F ext/fts2/fts2_icu.c 51c5cd3c04954badd329fa738c95fcdb717b5188 F ext/fts2/fts2_porter.c 747056987951f743e955c8479f1df21a565720fe -F ext/fts2/fts2_tokenizer.c 26e993a00b2bd5b6e73c155597361710b12ffe25 -F ext/fts2/fts2_tokenizer.h a7e46462d935a314b2682287f12f27530a3ee08e +F ext/fts2/fts2_tokenizer.c a86d08c9634fabfa237c8f379008de2e11248d36 +F ext/fts2/fts2_tokenizer.h 27a1a99ca2d615cf7e142839b8d79e8751b4529e F ext/fts2/fts2_tokenizer1.c 0123d21078e053bd98fd6186c5c6dc6d67969f2e F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0 F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51 F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314 F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d -F ext/fts3/fts3.c a867cafae0235324df64c5775cbf352a3f712cb9 +F ext/fts3/fts3.c 4bc160e6ff9ab5456b600f389f8941485ea5082f F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe -F ext/fts3/fts3Int.h 1e58825246b56259267382d2f9902774c049460a -F ext/fts3/fts3_aux.c 5205182bd8f372782597888156404766edf5781e -F ext/fts3/fts3_expr.c ceefcaf91344abbf6ceb3cadf404eef5be6924e6 +F ext/fts3/fts3Int.h 0b167bed9e63151635620a4f639bc62ac6012cba +F ext/fts3/fts3_aux.c b02632f6dd0e375ce97870206d914ea6d8df5ccd +F ext/fts3/fts3_expr.c 193d6fc156d744ab548a2ed06c31869e54dac739 F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914 -F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec +F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5 F ext/fts3/fts3_porter.c a465b49fcb8249a755792f87516eff182efa42b3 -F ext/fts3/fts3_snippet.c f6ebb3536069ceaa27e9f178f4a59379db44ac10 +F ext/fts3/fts3_snippet.c 5fcfcafff46a2a3a63b8e59fcb51987d01c74695 F ext/fts3/fts3_term.c a521f75132f9a495bdca1bdd45949b3191c52763 -F ext/fts3/fts3_test.c 348f7d08cae05285794e23dc4fe8b8fdf66e264a -F ext/fts3/fts3_tokenizer.c 3664bb8836ab7633cf9da786c9d6fd366be5ae2c -F ext/fts3/fts3_tokenizer.h 66dec98e365854b6cd2d54f1a96bb6d428fc5a68 +F ext/fts3/fts3_test.c f9a1a1702db1bfad3e2d0064746eeb808f125489 +F ext/fts3/fts3_tokenize_vtab.c 011170fe9eba5ff062f1a31d3188e00267716706 +F ext/fts3/fts3_tokenizer.c bbdc731bc91338050675c6d1da9ab82147391e16 +F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3 F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004 -F ext/fts3/fts3_unicode.c 49e36e6ba59f79e6bd6a8bfe434570fe48d20559 +F ext/fts3/fts3_unicode.c 92391b4b4fb043564c6539ea9b8661e3bcba47b9 F ext/fts3/fts3_unicode2.c a863f05f758af36777dffc2facc898bc73fec896 -F ext/fts3/fts3_write.c c2166f7148a4ad8bcdad99a99d647b1091744e6b +F ext/fts3/fts3_write.c d92c6cbe07363791cfe8b62b4dee67e6f8afc9e2 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/tool/fts3view.c 6cfc5b67a5f0e09c0d698f9fd012c784bfaa9197 F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7 F ext/fts3/unicode/mkunicode.tcl 7a9bc018e2962abb79563c5a39fe581fcbf2f675 -F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 +F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 +F ext/misc/amatch.c eae8454cd9dcb287b2a3ec2e65a865a4ac5f0d06 +F ext/misc/closure.c 40788c54c59190a1f52f6492a260d8894a246fe9 +F ext/misc/fuzzer.c 51bd96960b6b077d41d6f3cedefbcb57f29efaa2 +F ext/misc/ieee754.c 2565ce373d842977efe0922dc50b8a41b3289556 +F ext/misc/nextchar.c 1131e2b36116ffc6fe6b2e3464bfdace27978b1e +F ext/misc/regexp.c c25c65fe775f5d9801fb8573e36ebe73f2c0c2e0 +F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a +F ext/misc/spellfix.c 6d7ce6105a4b7729f6c44ccdf1ab7e80d9707c02 +F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c 47064ee2995a396bfb626337d2b43f12cc0af687 +F ext/rtree/rtree.c 757abea591d4ff67c0ff4e8f9776aeda86b18c14 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e -F ext/rtree/rtree1.test e474a2b5eff231496dbd073fe67e5fbaf7f444c9 +F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290 F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 -F ext/rtree/rtree5.test 9a229678a00f40e6aedb40cb3a07ec5444af892c +F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e F ext/rtree/rtree6.test 3ff9113b4a872fa935309e3511cd9b7cdb4d2472 F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971 F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d @@ -103,155 +113,160 @@ F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 -F main.mk a0d170ae1a8a8683688e281194e09d47a68eaa3f +F magic.txt 3f820e18c43504b25da40ff4b4cdb66dc4c4907e +F main.mk a8ebdf910e2cc10db1f9f54ec316f637458e8001 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac F mkopcodec.awk f6fccee29e68493bfd90a2e0466ede5fa94dd2fc F mkopcodeh.awk 29b84656502eee5f444c3147f331ee686956ab0e F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 +F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 +F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 +F mptest/crash01.test cce8e306d8596d5a2e497e27112dae1f6e5e3538 +F mptest/crash02.subtest f4ef05adcd15d60e5d2bd654204f2c008b519df8 +F mptest/mptest.c 499a74af4be293b7c1c7c3d40f332b67227dd739 +F mptest/multiwrite01.test 499ad0310da8dff8e8f98d2e272fc2a8aa741b2e F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168 -F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410 -F src/attach.c ea5247f240e2c08afd608e9beb380814b86655e1 +F src/analyze.c d5f895810e8ff9737c9ec7b76abc3dcff5860335 +F src/attach.c 1816f5a9eea8d2010fc2b22b44f0f63eb3a62704 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 -F src/backup.c cab40f2c1fe79d6eb93d3b4086c78c41ad2fa5d0 -F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1 +F src/backup.c b266767351ae2d847716c56fcb2a1fea7c761c03 +F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c eccee944cb2221e919d7a855e5928d8643194b14 -F src/btree.h 3ad7964d6c5b1c7bff569aab6adfa075f8bf06cd -F src/btreeInt.h 4e5c2bd0f9b36b2a815a6d84f771a61a65830621 -F src/build.c f4f86c07002c6f3ee96c1e34e0e993a962ef2c73 +F src/btree.c fcfbe61a311e54224b23527bbf7586ce320e7b40 +F src/btree.h 6fa8a3ff2483d0bb64a9f0105a8cedeac9e00cca +F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2 +F src/build.c 92ef9483189389828966153c5950f2e5a49c1182 F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac -F src/ctime.c 72a70dcfda75d3a1f81041ce4573e7afddcd8e4e +F src/ctime.c 4262c227bc91cecc61ae37ed3a40f08069cfa267 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 -F src/delete.c 9b8d308979114991e5dc7cee958316e07186941d -F src/expr.c 0e41d66d868b37dbc0e041c342e0036fad27e705 +F src/delete.c aeabdabeeeaa0584127f291baa9617153d334778 +F src/expr.c e40d198a719aba1d2514f6e1541f9154f976ceb6 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c dcb7c37a4bf526ded7b24a01a60fe071bcd160a2 -F src/func.c 8147799b048065a1590805be464d05b4913e652c -F src/global.c e59ecd2c553ad0d4bfbc84ca71231336f8993a7a +F src/fkey.c e16942bd5c8a868ac53287886464a5ed0e72b179 +F src/func.c d3fdcff9274bc161152e67ed3f626841c247f4b9 +F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 -F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 +F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c dc197aa9293a26d300eb5378880e701f7b20fefa -F src/journal.c eb7b9f5e783266521bcd9b2b93d419a219411f71 -F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f +F src/insert.c f7cb141e8ce257cb6b15c497f09e4e23d6055599 +F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d +F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b -F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d -F src/main.c 448564ee3795b13c373bbc5c8590df8fbfe1d64c +F src/loadext.c c48f7f3f170e502fe0cc20748e03c6e0b5a016c2 +F src/main.c c6419ef57392b1aa0cf6ed9c80dfc06b153fe299 F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f -F src/memjournal.c 0ebce851677a7ac035ba1512a7e65851b34530c6 +F src/memjournal.c 41a598445c8f249bd9b26ecae700c60dcf34cbf3 F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc F src/mutex.h 5bc526e19dccc412b7ff04642f6fdad3fdfdabea F src/mutex_noop.c 7682796b7d8d39bf1c138248858efcd10c9e1553 F src/mutex_unix.c c3a4e00f96ba068a8dbef34084465979aaf369cc F src/mutex_w32.c 32a9b3841e2d757355f0012b860b1bc5e01eafa0 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 -F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c -F src/os.h 027491c77d2404c0a678bb3fb06286f331eb9b57 +F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be +F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 -F src/os_unix.c ad459bb62eb6f3f6aae26d97b1a28fbac7bf0260 -F src/os_win.c ce1f5db8a7bb4d6f2092b1b2cb9631bec54a6320 -F src/pager.c 4092c907222cfd451c74fe6bd2fd64b342f7190f -F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0 -F src/parse.y c2b4a6454ad77299b1443e2c483a560a9f16e724 +F src/os_unix.c 75ce49309b8352c7173ce1ef6fc9e8d1f6daab10 +F src/os_win.c 32b8adca3e989565713ff74098b3cb2cb25d6e59 +F src/pager.c 49e23f9898113ddfe90942bdf1c1ef57955d0921 +F src/pager.h 5cb78b8e1adfd5451e600be7719f5a99d87ac3b1 +F src/parse.y 9708365594eea519cdc8504dee425c0a41c79502 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 -F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c +F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9 -F src/pragma.c 015723c48072781d2740e310ab04dc92956b76d1 -F src/prepare.c 931ad0d852a0df48f79adcba6ce79ca5f475625c +F src/pragma.c 8779308bc1ea1901c4bc94dfe9a83d436f73f52c +F src/prepare.c 743e484233c51109666d402f470523553b41797c F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c cdd546d62da7763119ea1fa455a898959e03457f +F src/resolve.c 89f9003e8316ee3a172795459efc2a0274e1d5a8 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c e6daa524bbdfa98f4abdb8cb281498f0047d3161 -F src/shell.c e392dd1ccbb77cc1d75a8367a89b473c24bea019 -F src/sqlite.h.in 39cc33bb08897c748fe3383c29ccf56585704177 +F src/select.c a4641882279becc200f2680f55f3e89d4e7c7f78 +F src/shell.c 2109d54f67c815a100abd7dc6a6e25eddb3b97eb +F src/sqlite.h.in 5a5a22a9b192d81a9e5dee00274e3a0484c4afb1 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 -F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h d1f0866c69d94fe018a32f78c31a043e3fc0d0de +F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5 +F src/sqliteInt.h 4cc782c9a89b3ddd663e7f68af3fa9e5af596f8b F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e -F src/tclsqlite.c 515abd8e33e82aa330eeb54675185a7e1e5b6778 -F src/test1.c f62769c989146149590662ab02de4a813813a9c5 -F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf -F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d -F src/test4.c bf9fa9bece01de08e6f5e02314e4af5c13590dfa +F src/tclsqlite.c 2ecec9937e69bc17560ad886da35195daa7261b8 +F src/test1.c 045d45a4f7eeb5d963f8fc150339f1bad279011a +F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35 +F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c +F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013 -F src/test6.c 938794c970ed6810036c8d28450ca28166524bf7 -F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843 -F src/test8.c 8bcce65e5ee027fbfd7da41d28371aabbfd369ff +F src/test6.c a437f76f9874d2563352a7e6cd0d43217663c220 +F src/test7.c 126b886b53f0358b92aba9b81d3fcbfbe9a93cd6 +F src/test8.c 7ee77ea522ae34aa691dfe407139dec80d4fc039 F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 -F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad -F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e -F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de +F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 +F src/test_autoext.c 5c95b5d435eaa09d6c0e7d90371c5ca8cd567701 +F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16 -F src/test_config.c 09781397ccc24268cb895be0d4c21b4aad651486 +F src/test_config.c 95bb33e9dcaa340a296c0bf0e0ba3d1a1c8004c0 F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc +F src/test_fs.c 8f786bfd0ad48030cf2a06fb1f050e9c60a150d7 F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170 -F src/test_fuzzer.c 1d26aa965120420bc14807da29d4d4541bfa6148 F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a -F src/test_intarray.c 07ddcebe4097d400ffca362770f1d883c112387a +F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61 F src/test_intarray.h b999bb18d090b8d9d9c49d36ec37ef8f341fe169 F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64 F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e -F src/test_malloc.c 6982a357a6a6c24f281b91c89303a5c31075c392 -F src/test_multiplex.c ac0fbc1748e5b86a41a1d7a84654fae0d53a881d +F src/test_malloc.c 2855429b8232107b3296409be2a8eb68345290c2 +F src/test_multiplex.c 5d691eeb6cb6aa7888da28eba5e62a9a857d3c0f F src/test_multiplex.h 9b63b95f07acedee425fdfe49a47197c9bf5f9d8 -F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e +F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25 F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 -F src/test_quota.c 0e0e2e3bf6766b101ecccd8c042b66e44e9be8f5 +F src/test_quota.c 30c64f0ef84734f2231a686df41ed882b0c59bc0 F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb -F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9 +F src/test_rtree.c 1d764c352b5348bb2869ff5f54aff8eadcb96041 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f -F src/test_spellfix.c 76dd8d3111d2f5354c374f71fa23b752bd0b029c -F src/test_sqllog.c 8acb843ddb9928dea8962e31bb09f421a72ffccb +F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e F src/test_stat.c d1569c7a4839f13e80187e2c26b2ab4da2d03935 F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd -F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae +F src/test_syscall.c 16dbe79fb320fadb5acd7a0a59f49e52ab2d2091 F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa -F src/test_thread.c e286f2173563f2a1747c24bcda6b9d030bf4f4e4 -F src/test_vfs.c c6260ef238c1142c8f8bd402db02216afd182ae3 +F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb +F src/test_vfs.c 8e6087a8b3dcc260282074b0efba14b76311120c F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 -F src/test_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12 F src/trigger.c cd95ac64efa60e39faf9b5597443192ff27a22fa -F src/update.c 28d2d098b43a2c70dae399896ea8a02f622410ef +F src/update.c 4c0c6864c4349ba292042e984a56d15985b57f4e F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f -F src/util.c 0af2e515dc0dabacec931bca39525f6c3f1c5455 -F src/vacuum.c 2727bdd08847fcb6b2d2da6d14f018910e8645d3 -F src/vdbe.c f51eb3207594703d24e91335cb16906e894b48aa +F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9 +F src/vacuum.c ddf21cc9577c4cb459d08bee9863a78ec000c5bb +F src/vdbe.c 5f0047130f80c7fd0bc41bc51a653b5542c4fbd5 F src/vdbe.h b52887278cb173e66188da84dfab216bea61119d -F src/vdbeInt.h 79abf9b31be406d35ca77d6999cb2d42aaf91e78 -F src/vdbeapi.c 4c2418161cf45392ba76a7ca92f9a5f06b96f89c -F src/vdbeaux.c 7658c5d9db838db5780f6a1c4d1280d0646e3569 -F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb -F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74 -F src/vdbesort.c c61ca318681c0e7267da8be3abfca8469652a7e9 -F src/vdbetrace.c 8bd5da325fc90f28464335e4cc4ad1407fe30835 +F src/vdbeInt.h c1e830268b75f04a2901dd895b51a637a26c7045 +F src/vdbeapi.c 085cf9bf169b859a6c8fa43791702bac805cb7aa +F src/vdbeaux.c ecb43014bcd3019e5aa2b5561e5c3a447f007a08 +F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69 +F src/vdbemem.c 833005f1cbbf447289f1973dba2a0c2228c7b8ab +F src/vdbesort.c 4fad64071ae120c25f39dcac572d716b9cadeb7f +F src/vdbetrace.c 18cc59cb475e6115129bfde224367d13a35a7d13 F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83 -F src/wal.c f5c7b5027d0ed0e9bc9afeb4a3a8dfea762ec7d2 -F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 -F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b -F src/where.c 613a89f1c9cf965b66e9d90f7f3e7a870596ac80 -F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 +F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d +F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 +F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 +F src/where.c 5c4cbc1e5205d8d534c483fa4495c81513b45dea +F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 @@ -266,7 +281,7 @@ F test/analyze3.test c3c7f6c3951900c188cf94b2d5ee3246d6b3ff89 F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045 F test/analyze5.test 713354664c5ff1853ab2cbcb740f0cf5cb7c802e F test/analyze6.test aa8dae5066bbed35c5f45a507fb87f2d342f2c99 -F test/analyze7.test d3587aa5af75c9048d031b94fceca2534fa75d1d +F test/analyze7.test bd09e89264c664d8d8d2450b6866dcdfae080a13 F test/analyze8.test 4ca170de2ba30ccb1af2c0406803db72262f9691 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b @@ -279,11 +294,11 @@ F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966 F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0 -F test/auth.test 304e82f31592820d3bde26ab6b75deaa123e1a6f -F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882 +F test/auth.test 4a4c3b034fff7750513520defa910f376c96ab49 +F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf -F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991 +F test/autoindex1.test f88146c4c889ea0afbb620e49d83b5fbf5ee4d06 F test/autovacuum.test 9f22a7733f39c56ef6a5665d10145ac25d8cb574 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 @@ -291,7 +306,7 @@ F test/backcompat.test ecd841f3a3bfb81518721879cc56a760670e3198 F test/backup.test c9cdd23a495864b9edf75a9fa66f5cb7e10fcf62 F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf F test/backup4.test 4d90389daeb781fe718816a4fc836ad1b06791d8 -F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0 +F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135 F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f @@ -312,21 +327,24 @@ F test/boundary3.tcl 8901d6a503d0bf64251dd81cc74e5ad3add4b119 F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45 F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983 F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b +F test/btreefault.test f52c593513bda80a506c848325c73c840590884d F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0 -F test/cache.test f64136b0893c293d0b910ed057b3b711249099a7 -F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360 +F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de +F test/capi2.test e8b18cc61090b6e5e388f54d6b125d711d1b265a F test/capi3.test 56ab450125ead38846cbae7e5b6a216686c3cffa F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3c.test 93d24621c9ff84da9da060f30431e0453db1cdb0 F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1 F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 -F test/check.test 193f47ed43a8d29aca12b30cd30ceb105fbe710d +F test/check.test 2eb93611139a7dfaed3be80067c7dc5ceb5fb287 +F test/close.test e37610d60e9c9b9979a981f3f50071d7dff28616 +F test/closure01.test dbb28f1ea9eeaf0a53ec5bc0fed352e479def8c7 F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 F test/collate1.test fd02c4d8afc71879c4bb952586389961a21fb0ce F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49 -F test/collate3.test d28d2cfab2c3a3d4628ae4b2b7afc9965daa3b4c -F test/collate4.test d37682293d3c32223dec2e6afdeaf9de18415248 +F test/collate3.test 79558a286362cb9ed603c6fa543f1cda7f563f0f +F test/collate4.test 031f7265c13308b724ba3c49f41cc04612bd92b1 F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6 F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907 F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868 @@ -335,7 +353,7 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 F test/colmeta.test 087c42997754b8c648819832241daf724f813322 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b -F test/conflict.test cabc41f7616675df71b4fddabca3bd5d9221915a +F test/conflict.test 0b3922d2304a14a47e3ccd61bbd6824327af659b F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0 F test/corrupt3.test 889d7cdb811800303aa722d7813fe8a4299cf726 @@ -357,7 +375,7 @@ F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651 F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418 F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc -F test/crash5.test 13b9ca94e048194bca070e26160ce76b406c56be +F test/crash5.test 05dd3aa9dbb751a22d5cdaf22a9c49b6667aa219 F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba F test/crash7.test 1a194c4900a255258cf94b7fcbfd29536db572df F test/crash8.test 38767cb504bbe491de6be4a7006b154973a2309f @@ -367,52 +385,53 @@ F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47 F test/date.test f3228180c87bbe5d39c9397bf001c0095c3821b9 F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b -F test/dbstatus2.test bf7396af964b89e39435babbcdf296ae8fc5f10a +F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2 F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701 F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66 F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d -F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f +F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2 F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e F test/distinct.test 84da1414b2e6887fffd5ed571311b344c5b082ce F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 -F test/e_createtable.test 0a2465736199cb5e084645a8714ee04299b81721 +F test/e_createtable.test d4e17024b1831e7480f5736cf4e02516a5c90927 F test/e_delete.test 89aa84d3d1bd284a0689ede04bce10226a5aeaa5 F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235 F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894 -F test/e_fkey.test a79ab1d3213c7ac64621eec28f8e8bb219775445 +F test/e_fkey.test 79a985d95340c6072a884359426ce95cf33e656a F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_insert.test d5331cc95e101af2508159fc98b6801631659ffe F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216 F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6 -F test/e_select.test 07e8d81268ba1ffcaf1dc4bec48956af150c42f9 +F test/e_select.test d5af998a402740d8f0488158d22075df2b6f88fa F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92 F test/e_update.test 161d5dc6a3ed9dd08f5264d13e20735d7a89f00c -F test/e_uri.test bc240fbc6cbbbdff832ee05858432a25961ab36a +F test/e_uri.test 8f2f56b29456a3f846276fa4e0993d4ef8a15b79 F test/e_vacuum.test 331da289ae186656cf5f2eb27f577a89c0c221af F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea -F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad +F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473 F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40 F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020 F test/eqp.test 46aa946dd55c90635327898275d3e533d23a9845 -F test/errmsg.test 3bb606db9d040cc6854459f8f5e5a2bcd9b7fd2a +F test/errmsg.test 050717f1c6a5685de9c79f5f9f6b83d7c592f73a F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb -F test/exclusive2.test 372be98f6de44dd78734e364b7b626ea211761a6 +F test/exclusive2.test 881193eccec225cfed9d7f744b65e57f26adee25 F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30 F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a -F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e +F test/filefmt.test dbee33e57818249cdffbbb7b13464635217beff1 F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da -F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f +F test/fkey2.test 06e0b4cc9e1b3271ae2ae6feeb19755468432111 F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e -F test/fkey4.test c6c8f9f9be885f95c85c7bceb26f243ad906fd49 -F test/fkey_malloc.test c3a12acd053c976de09036498eef09b83afa4a80 +F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d +F test/fkey5.test 0bf64f2d19ad80433ca0b24edbf604a18b353d5f +F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51 F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7 @@ -459,7 +478,7 @@ F test/fts3ae.test ce32a13b34b0260928e4213b4481acf801533bda F test/fts3af.test d394978c534eabf22dd0837e718b913fd66b499c F test/fts3ag.test 0b7d303f61ae5d620c4efb5e825713ea34ff9441 F test/fts3ah.test dc9f66c32c296f1bc8bcc4535126bddfeca62894 -F test/fts3ai.test d29cee6ed653e30de478066881cec8aa766531b2 +F test/fts3ai.test 24058fdc6e9e5102c1fd8459591b114b6a85d285 F test/fts3aj.test 0ed71e1dd9b03b843a857dc3eb9b15630e0104fc F test/fts3ak.test bd14deafe9d1586e8e9bf032411026ac4f8c925d F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f @@ -468,7 +487,7 @@ F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18 F test/fts3ao.test e7b80272efcced57d1d087a9da5c690dd7c21fd9 F test/fts3atoken.test fb398ab50aa232489e2a17f9b29d7ad3a3885f36 F test/fts3auto.test 74315a7377403a57ba82a652a33704197fe1e4be -F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0 +F test/fts3aux1.test 03cec2dea379931c219dd4406817485caa69d1d8 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c @@ -483,12 +502,13 @@ F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297 F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851 F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a +F test/fts3expr3.test 1bfb762b53a794f990f3dffaae8bbea5736422f7 F test/fts3fault.test cb72dccb0a3b9f730f16c5240f3fcb9303eb1660 F test/fts3fault2.test 3198eef2804deea7cac8403e771d9cbcb752d887 F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641 F test/fts3malloc.test b86ea33db9e8c58c0c2f8027a9fcadaf6a1568be F test/fts3matchinfo.test ecb08f586d027eb03941bcfcded6cb9d8ccb3a66 -F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844 +F test/fts3near.test 12895557870b0f9af7cc0be81a0171abb2d12f12 F test/fts3prefix.test b36d4f00b128a51e7b386cc013a874246d9d7dc1 F test/fts3prefix2.test 477ca96e67f60745b7ac931cfa6e9b080c562da5 F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2 @@ -496,16 +516,18 @@ F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0 F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2 F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2 F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659 +F test/fts3tok1.test 4d9e7401679dc71f6b2f76416309b923210bfdbe +F test/fts3tok_err.test 41e5413139c2ef536ffadfcd1584ee50b1665ec0 F test/fts4aa.test 95f448fb02c4a976968b08d1b4ce134e720946ae F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8 -F test/fts4content.test 17b2360f7d1a9a7e5aa8022783f5c5731b6dfd4f +F test/fts4content.test 6efc53b4fd03cab167e6998d2b0b7d4b7d419ee6 F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7 F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891 F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7 -F test/fts4unicode.test aad033abdcfa0f87ce5f56468f59fdf2a0acbcef +F test/fts4unicode.test 25ccad45896f8e50f6a694cff738a35f798cdb40 F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test 0d89043dab9a8853358d14c68e028ee0093bf066 +F test/func.test b0fc34fdc36897769651975a2b0a606312753643 F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74 @@ -514,16 +536,16 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 -F test/fuzzer1.test a2e93bb1e19513dd6bf9c63d3d7c4673c983ca19 -F test/fuzzerfault.test ff2282c81797b6a355f0748d8b54c7287c5d2b25 -F test/hook.test 5f3749de6462a6b87b4209b74adf7df5ac2df639 +F test/fuzzer1.test 41bd5aa6ae0cf18d06342a4476e3cad98604ae48 +F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536 +F test/hook.test 45cb22b940c3cc0af616ba7430f666e245711a48 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/in.test 5941096407d8c133b9eff15bd3e666624b6cbde3 F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617 F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3 -F test/incrblob.test 34765fa6fb5d8e0f256fc7d6497c04b205398849 +F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328 F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19 F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7 F test/incrblob4.test 09be37d3dd996a31ea6993bba7837ece549414a8 @@ -531,12 +553,13 @@ F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597 F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0 F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32 F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b -F test/incrvacuum_ioerr.test 22f208d01c528403240e05beecc41dc98ed01637 +F test/incrvacuum3.test 2ffa9e4a23f072bd7902b9ae6471f8822a6522a7 +F test/incrvacuum_ioerr.test 6ae2f783424e47a0033304808fe27789cf93e635 F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097 F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6 F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7 F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026 -F test/index5.test edc8c64ca78bee140c21ce3836820fadf47906bb +F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33 F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 @@ -549,12 +572,13 @@ F test/instr.test a34e1d46a9eefb098a7167ef0e730a4a3d82fba0 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4 F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc F test/intpkey.test 7af30f6ae852d8d1c2b70e4bf1551946742e92d8 -F test/io.test 36d251507d72e92b965fb2f0801c2f0b56335bcf +F test/io.test ecf44cc81664ad54d8253e2d88fc705b6554abe3 F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8 F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 +F test/ioerr6.test cf25523b921d1a6a0e5b536cd4acb3c3d979ea52 F test/join.test 8d63cc4d230a7affafa4b6ab0b97c49b8ccb365c F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 @@ -572,8 +596,8 @@ F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 F test/like.test 0e5412f4dac4a849f613e1ef8b529d56a6e31d08 F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da -F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e -F test/loadext.test 2b5e249c51c986a5aff1f0950cf7ba30976c8f22 +F test/limit.test cc0ab63385239b63c72452b0e93700bf5e8f0b99 +F test/loadext.test 92e6dfefd1229c3ef4aaabd87419efd8fa57a7a5 F test/loadext2.test 0bcaeb4d81cd5b6e883fdfea3c1bdbe1f173cbca F test/lock.test 87af515b0c4cf928576d0f89946d67d7c265dfb4 F test/lock2.test 5242d8ac4e2d59c403aebff606af449b455aceff @@ -586,8 +610,8 @@ F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95 F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2 F test/main.test 39c4bb8a157f57298ed1659d6df89d9f35aaf2c8 F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9 -F test/malloc.test bc745155ff4252d4f35ec8316625b0dfe2abc659 -F test/malloc3.test 3e9eb921c4314acf4fb64230868fdb2e1ce60eea +F test/malloc.test fd368e31fe98d4779ed80442f311ed9f03bcd1f7 +F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a F test/malloc4.test 957337613002b7058a85116493a262f679f3a261 F test/malloc5.test a577cbbcc1594c7763b9b3515b3633555751c7f0 F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151 @@ -601,19 +625,19 @@ F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4 F test/mallocD.test f78c295e8e18ea3029e65ca08278690e00c22100 F test/mallocE.test db1ed69d7eded1b080952e2a7c37f364ad241b08 F test/mallocF.test 2d5c590ebc2fc7f0dcebdf5aa8498b9aed69107e -F test/mallocG.test 4584d0d8ddb8009f16ca0c8bab1fa37f6358efa2 +F test/mallocG.test 0ff91b65c50bdaba680fb75d87fe4ad35bb7934f F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6 F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9 -F test/malloc_common.tcl 2930895b0962823ec679853e67e58dd6d8198b3c +F test/malloc_common.tcl 9a98856549bfb3fab205edbc1317216edc52e70d F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f -F test/memdb.test 708a028d6d373e5b3842e4bdc8ba80998c9a4da6 +F test/memdb.test db5260330676de007be8530d6ecc7c9ab2b06ad3 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9 -F test/minmax.test c61518429e66e228efc79661fbd2dc3e4924ec44 +F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef @@ -623,8 +647,10 @@ F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738 F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 -F test/misc7.test f00dad9a004da659330013e6f21819d018b683d3 +F test/misc7.test dd82ec9250b89178b96cd28b2aca70639d21e5b3 F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054 +F test/mmap1.test 93d167b328255cbe6679fe1e1a23be1b1197d07b +F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022 F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256 F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101 @@ -632,26 +658,29 @@ F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf -F test/notify2.test 9503e51b9a272a5405c205ad61b7623d5a9ca489 +F test/notify2.test ce23eb522c9e1fff6443f96376fe67872202061c F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a -F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 +F test/notnull.test 2afad748d18fd66d01f66463de73b3e2501fb226 F test/null.test a8b09b8ed87852742343b33441a9240022108993 +F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 F test/orderby1.test f33968647da5c546528fe4d2bf86c6a6a2e5a7ae F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04 +F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99 +F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4 F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 -F test/pager1.test f4c57e14583da2183fe31555c67fb32feda96092 -F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 +F test/pager1.test 30e63afd425fea12285e9ec5fa1fd000808031f1 +F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f -F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442 +F test/pagerfault.test 8483e65d33d5636e5b7656204bb274d826e728d9 F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401 F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7 -F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0 +F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6 F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 -F test/permutations.test 360b92859c0af814b3fe10b68746936389606501 -F test/pragma.test a62f73293b0f0d79b0c87f8dd32d46fe53b0bd17 +F test/permutations.test d997a947ab8aabb15f763d50a030b3c11e8ef1b6 +F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178 F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 @@ -664,9 +693,11 @@ F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d +F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce +F test/resolver01.test d1b487fc567bdb70b5dd09ccaaa877ddc61a233e F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07 F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test f777404492adb0e00868fd706a3721328fd3af48 @@ -694,9 +725,11 @@ F test/select6.test e76bd10a56988f15726c097a5d5a7966fe82d3b2 F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea -F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532 +F test/selectA.test 99cf21df033b93033ea4f34aba14a500f48f04fe F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25 F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977 +F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394 +F test/selectE.test fc02a1eb04c8eb537091482644b7d778ae8759b7 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879 @@ -706,9 +739,10 @@ F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9 F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21 +F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5 F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf -F test/shell1.test b7896eb84028f3bc8300caf1fc796a73728aad0b +F test/shell1.test 4a2f57952719972c6f862134463f8712e953c038 F test/shell2.test 037d6ad16e873354195d30bb2dc4b5321788154a F test/shell3.test 9196c42772d575685e722c92b4b39053c6ebba59 F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9 @@ -721,31 +755,31 @@ F test/softheap1.test c16709a16ad79fa43b32929b2e623d1d117ccf53 F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5 F test/speed1.test f2974a91d79f58507ada01864c0e323093065452 F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb -F test/speed1p.test c4a469f29f135f4d76c55b1f2a52f36e209466cc +F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8 F test/speed2.test 53177056baf6556dcbdcf032bbdfc41c1aa74ded F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b -F test/spellfix.test 2953e9da0e46dab5f83059ef6bfdebca66e13418 +F test/spellfix.test bea537caf587df30d430c2c6a8fe9f64b8712834 F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test be8d477306006ec696bc86757cfb34bec79447ce F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 -F test/subquery.test d4aea23ac267463d4aa604bf937c3992347b20f7 +F test/subquery.test 869562de9e8c5d8147e0451a2ce5b58cf55ce389 F test/subquery2.test 91e1e364072aeff431d1f9689b15147e421d88c7 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 -F test/syscall.test bea9bf329bff733c791310244617c2a76974e64a -F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f +F test/syscall.test a653783d985108c4912cc64d341ffbbb55ad2806 +F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2 F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43 F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c -F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2 +F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d -F test/tester.tcl 2f383e811010b05a83c0f00fc168cae1dd63a6d9 +F test/tester.tcl 3b08771e6d601612fe62d13787db0e50aac4cf7b F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -760,7 +794,7 @@ F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 F test/tkt-2a5629202f.test 1ab32e084e9fc3d36be6dee2617530846a0eb0b6 -F test/tkt-2d1a5c67d.test b028a811049eb472cb2d3a43fc8ce4f6894eebda +F test/tkt-2d1a5c67d.test d371279946622698ab393ff88cad9f5f6d82960b F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28 F test/tkt-31338dca7e.test 6fb8807851964da0d24e942f2e19c7c705b9fb58 F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac @@ -770,12 +804,15 @@ F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7 F test/tkt-3a77c9714e.test 32bb28afa8c63fc76e972e996193139b63551ed9 F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00 F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e +F test/tkt-4dd95f6943.test 3d0ce415d2ee15d3d564121960016b9c7be79407 F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894 F test/tkt-5d863f876e.test c9f36ca503fa154a3655f92a69d2c30da1747bfa F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84 F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f +F test/tkt-6bfb98dfc0.test 24780633627b5cfc0635a5500c2389ebfb563336 F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf F test/tkt-78e04e52ea.test 703e0bfb23d543edf0426a97e3bbd0ca346508ec +F test/tkt-7a31705a7e6.test 5a7889fdb095ffbe1622413e0145de1637d421bd F test/tkt-7bbfb7d442.test dfa5c8097a8c353ae40705d6cddeb1f99c18b81a F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8 F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7 @@ -800,6 +837,7 @@ F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7 F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4 F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035 F test/tkt-fc62af4523.test 72825d3febdedcd5593a27989fc05accdbfc2bb4 +F test/tkt-fc7bd6358f.test 634bb4af7d661e82d6b61b80c86727bad698e08f F test/tkt1435.test f8c52c41de6e5ca02f1845f3a46e18e25cadac00 F test/tkt1443.test bacc311da5c96a227bf8c167e77a30c99f8e8368 F test/tkt1444.test a9d72f9e942708bd82dde6c707da61c489e213e9 @@ -822,7 +860,7 @@ F test/tkt2285.test cca17be61cf600b397188e77e7143844d2b977e9 F test/tkt2332.test fc955609b958ca86dfa102832243370a0cc84070 F test/tkt2339.test 73bd17818924cd2ac442e5fd9916b58565739450 F test/tkt2391.test ab7a11be7402da8b51a5be603425367aa0684567 -F test/tkt2409.test 464d55beb32e3b12ce2b4bbf9857d063c4c34297 +F test/tkt2409.test be0d60e7d283f639dccea4b0b5e1cd3a4851fb5b F test/tkt2450.test 77ed94863f2049c1420288ddfea2d41e5e0971d6 F test/tkt2565.test 8be666e927cb207aae88188f31c331870878b650 F test/tkt2640.test 28134f5d1e05658ef182520cf0b680fa3de5211b @@ -831,7 +869,7 @@ F test/tkt2686.test 6ee01c9b9e9c48f6d3a1fdd553b1cc4258f903d6 F test/tkt2767.test 569000d842678f9cf2db7e0d1b27cbc9011381b0 F test/tkt2817.test f31839e01f4243cff7399ef654d3af3558cb8d8d F test/tkt2820.test 39940276b3436d125deb7d8ebeee053e4cf13213 -F test/tkt2822.test f391776423a7c0d0949edfce375708bfb0f3141e +F test/tkt2822.test c3589494fba643e039bcf0bfde7554ff6028406d F test/tkt2832.test a9b0b74a02dca166a04d9e37739c414b10929caa F test/tkt2854.test e432965db29e27e16f539b2ba7f502eb2ccc49af F test/tkt2920.test a8737380e4ae6424e00c0273dc12775704efbebf @@ -849,7 +887,7 @@ F test/tkt3357.test 77c37c6482b526fe89941ce951c22d011f5922ed F test/tkt3419.test 1bbf36d7ea03b638c15804251287c2391f5c1f6b F test/tkt3424.test 61f831bd2b071bd128fa5d00fbda57e656ca5812 F test/tkt3442.test 0adb70e9fe9cb750a702065a68ad647409dbc158 -F test/tkt3457.test eb68bb3b19c8677cff06c639ff15d206dbf17fd6 +F test/tkt3457.test 44e980fe5334753dcc27b94fa4deabc485a92f74 F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19 F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218 F test/tkt3508.test d75704db9501625c7f7deec119fcaf1696aefb7d @@ -864,7 +902,7 @@ F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595 F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33 -F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df +F test/tkt3762.test 4d439ff7abdc8d9323150269d182c37c2d514576 F test/tkt3773.test 7bca904d2a647a6a4a291bd86d7fd7c73855b789 F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267 F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449 @@ -889,16 +927,17 @@ F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22 F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732 -F test/trigger1.test 30f343f91586765874a28ad539c06f5a5f049931 +F test/transitive1.test d04aa9023e425d6f2d4aa61dd81ee9e102f89062 +F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03 F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816 -F test/trigger3.test d2c60d8be271c355d61727411e753181e877230a +F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945 F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83 F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9 F test/trigger7.test b39e6dee1debe0ff9c2ef66326668f149f07c9c4 F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4 F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31 -F test/triggerA.test e0aaba16d3547193d36bbd82a1b0ed75e9c88d40 +F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332 F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe F test/triggerC.test a7b4367392c755bc5fd5fff88011753e6b6afe90 F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650 @@ -906,7 +945,7 @@ F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84 F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a -F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2 +F test/unique.test cadb172bbd5a2e83cd644d186ccd602085e54edc F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46 F test/unordered.test 93dce7b6c97a817a4fe26980c484605a4511f614 F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172 @@ -918,8 +957,8 @@ F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b -F test/view.test b182a67ec43f490b156b5a710827a341be83dd17 -F test/vtab1.test 36c9935e4be3b6350b31b6b697561b6fc3ab349a +F test/view.test 4057630287bfa5955628fe90a13d4c225d1c7352 +F test/vtab1.test 4403f987860ebddef1ce2de6db7216421035339d F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 @@ -937,11 +976,11 @@ F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_shared.test 82f463886e18d7f8395a4b6167c91815efe54839 -F test/wal.test a040047d7f2b9f34bc4d597964e5e7c09609c635 +F test/wal.test 3a6ebdf0287b38b5537c07c517b30dda9aaac317 F test/wal2.test d4b470f13c87f6d8268b004380afa04c3c67cb90 F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0 F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c -F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437 +F test/wal5.test 8f888b50f66b78821e61ed0e233ded5de378224b F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3 F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd F test/wal8.test b3ee739fe8f7586aaebdc2367f477ebcf3e3b034 @@ -953,7 +992,7 @@ F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2 F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142 F test/walcrash3.test 595e44c6197f0d0aa509fc135be2fd0209d11a2c -F test/walfault.test 97394d8de82a99f7abf1c12ed229640607fd0ad2 +F test/walfault.test 54ad6e849c727f4da463964b9eb8c8e8e155cf82 F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483 F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496 @@ -962,16 +1001,16 @@ F test/walro.test a31deb621033442a76c3a61e44929250d06f81b1 F test/walshared.test 6dda2293880c300baf5d791c307f653094585761 F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e -F test/where.test 9714e6f292d70c22e78e1cecb3d896457186b9b7 -F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554 +F test/where.test 15ac8611c9439a2c5f4a6ac10cfe4c1ec9854c24 +F test/where2.test 399b3178289925a0aa976b3d60ef139740540ecd F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a -F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f +F test/where8.test d6a283eb7348a8967d44e2a753f117ab0d21d4f3 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 -F test/where9.test bcab47eff78f1412a6aec1d6b8a3939d4a9db098 +F test/where9.test 1b4387c6eacc9a32b28b4d837c27f857c785d0d8 F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5 @@ -979,11 +1018,11 @@ F test/whereD.test 3f3ee93825c94804f1fc91eef2de0d365981759a F test/whereE.test 7bd34945797efef15819368479bacc34215e4e1d F test/whereF.test a0e296643cabe5278379bc1a0aa158cf3c54a1c9 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 -F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9 +F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 -F test/zerodamage.test e7f77fded01dfcdf92ac2c5400f1e35d7a21463c +F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd F tool/build-all-msvc.bat 74fb6e5cca66ebdb6c9bbafb2f8b802f08146d38 x -F tool/build-shell.sh b64a481901fc9ffe5ca8812a2a9255b6cfb77381 +F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367 F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2 @@ -997,8 +1036,8 @@ F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79 F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 8bce31074e4cbe631bb7676526a048335f4c9f02 -F tool/mksqlite3c.tcl 589c7f44e990be1b8443cfe4808dce392b0327fa -F tool/mksqlite3h.tcl 78013ad79a5e492e5f764f3c7a8ef834255061f8 +F tool/mksqlite3c.tcl a61fe62a2895ca6458c463fccf1211ca1c000fcf +F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12 F tool/mksqlite3internalh.tcl 3dca7bb5374cee003379b8cbac73714f610ef795 F tool/mkvsix.tcl 0be7f7a591f1e83f9199cb82911b66668ca484c9 F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091 @@ -1006,9 +1045,9 @@ F tool/omittest.tcl 4665982e95a6e5c1bd806cf7bc3dea95be422d77 F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 -F tool/showdb.c aca2644aa4de7c0cad5821e50bbd55397e0974b8 +F tool/showdb.c 525ecc443578647703051308ad50a93de6ba2c4b F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02 -F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9 +F tool/showwal.c 3f7f7da5ec0cba51b1449a75f700493377da57b5 F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b F tool/spaceanal.tcl 76f583a246a0b027f423252339e711f13198932e @@ -1026,7 +1065,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P 3d0609c958040d049b86f69d750ffcef764417df -R 9f4348d1a37f598f7d9d8be10eff6aea +P 7a9aa21c3506a10ab9465540e81071b39bca447d +R c103e20e6479251bb5b95c9b2e375810 +T +bgcolor * #d0c0ff +T +sym-release * +T +sym-version-3.7.17 * U drh -Z 2c8e31332e45b7b8f7af42318006a1b7 +Z 416a23d38a1b2c089b32c7a6efcbc3bc diff --git a/manifest.uuid b/manifest.uuid index 18f298f..eb32e70 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c0e09560d26f0a6456be9dd3447f5311eb4f238f +118a3b35693b134d56ebd780123b7fd6f1497668 diff --git a/src/analyze.c b/src/analyze.c index 632fdc1..9a3e959 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -473,7 +473,7 @@ static void analyzeOneTable( /* Do not gather statistics on views or virtual tables */ return; } - if( memcmp(pTab->zName, "sqlite_", 7)==0 ){ + if( sqlite3_strnicmp(pTab->zName, "sqlite_", 7)==0 ){ /* Do not gather statistics on system tables */ return; } @@ -883,7 +883,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; - if( memcmp(z, "unordered", 10)==0 ){ + if( strcmp(z, "unordered")==0 ){ pIndex->bUnordered = 1; break; } diff --git a/src/attach.c b/src/attach.c index 6682c91..b8e1219 100644 --- a/src/attach.c +++ b/src/attach.c @@ -109,7 +109,7 @@ static void attachFunc( } } - /* Allocate the new entry in the db->aDb[] array and initialise the schema + /* Allocate the new entry in the db->aDb[] array and initialize the schema ** hash tables. */ if( db->aDb==db->aDbStatic ){ @@ -126,7 +126,7 @@ static void attachFunc( /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may - ** or may not be initialised. + ** or may not be initialized. */ flags = db->openFlags; rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); diff --git a/src/backup.c b/src/backup.c index b234716..252f61c 100644 --- a/src/backup.c +++ b/src/backup.c @@ -212,7 +212,12 @@ static int isFatalError(int rc){ ** page iSrcPg from the source database. Copy this data into the ** destination database. */ -static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ +static int backupOnePage( + sqlite3_backup *p, /* Backup handle */ + Pgno iSrcPg, /* Source database page to backup */ + const u8 *zSrcData, /* Source database page data */ + int bUpdate /* True for an update, false otherwise */ +){ Pager * const pDestPager = sqlite3BtreePager(p->pDest); const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc); int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); @@ -285,6 +290,9 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ */ memcpy(zOut, zIn, nCopy); ((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0; + if( iOff==0 && bUpdate==0 ){ + sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc)); + } } sqlite3PagerUnref(pDestPg); } @@ -389,9 +397,10 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ const Pgno iSrcPg = p->iNext; /* Source page number */ if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){ DbPage *pSrcPg; /* Source page object */ - rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); + rc = sqlite3PagerAcquire(pSrcPager, iSrcPg, &pSrcPg, + PAGER_ACQUIRE_READONLY); if( rc==SQLITE_OK ){ - rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg)); + rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0); sqlite3PagerUnref(pSrcPg); } } @@ -454,7 +463,6 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ nDestTruncate = nSrcPage * (pgszSrc/pgszDest); } assert( nDestTruncate>0 ); - sqlite3PagerTruncateImage(pDestPager, nDestTruncate); if( pgszSrc=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest )); - /* This call ensures that all data required to recreate the original + /* This block ensures that all data required to recreate the original ** database has been stored in the journal for pDestPager and the ** journal synced to disk. So at this point we may safely modify ** the database file in any way, knowing that if a power failure ** occurs, the original database will be reconstructed from the ** journal file. */ - rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); + sqlite3PagerPagecount(pDestPager, &nDstPage); + for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){ + if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){ + DbPage *pPg; + rc = sqlite3PagerGet(pDestPager, iPg, &pPg); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerWrite(pPg); + sqlite3PagerUnref(pPg); + } + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); + } /* Write the extra pages and truncate the database file as required */ iEnd = MIN(PENDING_BYTE + pgszDest, iSize); @@ -511,6 +534,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ rc = sqlite3PagerSync(pDestPager); } }else{ + sqlite3PagerTruncateImage(pDestPager, nDestTruncate); rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } @@ -639,7 +663,7 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){ int rc; assert( p->pDestDb ); sqlite3_mutex_enter(p->pDestDb->mutex); - rc = backupOnePage(p, iPage, aData); + rc = backupOnePage(p, iPage, aData, 1); sqlite3_mutex_leave(p->pDestDb->mutex); assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); if( rc!=SQLITE_OK ){ diff --git a/src/bitvec.c b/src/bitvec.c index 8d805a6..52184aa 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -72,7 +72,7 @@ /* ** A bitmap is an instance of the following structure. ** -** This bitmap records the existance of zero or more bits +** This bitmap records the existence of zero or more bits ** with values between 1 and iSize, inclusive. ** ** There are three possible representations of the bitmap. diff --git a/src/btree.c b/src/btree.c index 246843b..3ca6058 100644 --- a/src/btree.c +++ b/src/btree.c @@ -43,6 +43,25 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */ */ #define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1) +/* +** Values passed as the 5th argument to allocateBtreePage() +*/ +#define BTALLOC_ANY 0 /* Allocate any page */ +#define BTALLOC_EXACT 1 /* Allocate exact page if possible */ +#define BTALLOC_LE 2 /* Allocate any page <= the parameter */ + +/* +** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not +** defined, or 0 if it is. For example: +** +** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum); +*/ +#ifndef SQLITE_OMIT_AUTOVACUUM +#define IfNotOmitAV(expr) (expr) +#else +#define IfNotOmitAV(expr) 0 +#endif + #ifndef SQLITE_OMIT_SHARED_CACHE /* ** A list of BtShared objects that are eligible for participation @@ -556,6 +575,19 @@ static void btreeClearHasContent(BtShared *pBt){ pBt->pHasContent = 0; } +/* +** Release all of the apPage[] pages for a cursor. +*/ +static void btreeReleaseAllCursorPages(BtCursor *pCur){ + int i; + for(i=0; i<=pCur->iPage; i++){ + releasePage(pCur->apPage[i]); + pCur->apPage[i] = 0; + } + pCur->iPage = -1; +} + + /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. @@ -595,12 +627,7 @@ static int saveCursorPosition(BtCursor *pCur){ assert( !pCur->apPage[0]->intKey || !pCur->pKey ); if( rc==SQLITE_OK ){ - int i; - for(i=0; i<=pCur->iPage; i++){ - releasePage(pCur->apPage[i]); - pCur->apPage[i] = 0; - } - pCur->iPage = -1; + btreeReleaseAllCursorPages(pCur); pCur->eState = CURSOR_REQUIRESEEK; } @@ -618,11 +645,15 @@ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pExcept==0 || pExcept->pBt==pBt ); for(p=pBt->pCursor; p; p=p->pNext){ - if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && - p->eState==CURSOR_VALID ){ - int rc = saveCursorPosition(p); - if( SQLITE_OK!=rc ){ - return rc; + if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){ + if( p->eState==CURSOR_VALID ){ + int rc = saveCursorPosition(p); + if( SQLITE_OK!=rc ){ + return rc; + } + }else{ + testcase( p->iPage>0 ); + btreeReleaseAllCursorPages(p); } } } @@ -1550,13 +1581,17 @@ static int btreeGetPage( BtShared *pBt, /* The btree */ Pgno pgno, /* Number of the page to fetch */ MemPage **ppPage, /* Return the page in this parameter */ - int noContent /* Do not load page content if true */ + int noContent, /* Do not load page content if true */ + int bReadonly /* True if a read-only (mmap) page is ok */ ){ int rc; DbPage *pDbPage; + int flags = (noContent ? PAGER_ACQUIRE_NOCONTENT : 0) + | (bReadonly ? PAGER_ACQUIRE_READONLY : 0); + assert( noContent==0 || bReadonly==0 ); assert( sqlite3_mutex_held(pBt->mutex) ); - rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent); + rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, flags); if( rc ) return rc; *ppPage = btreePageFromDbPage(pDbPage, pgno, pBt); return SQLITE_OK; @@ -1599,9 +1634,10 @@ u32 sqlite3BtreeLastPage(Btree *p){ ** may remain unchanged, or it may be set to an invalid value. */ static int getAndInitPage( - BtShared *pBt, /* The database file */ - Pgno pgno, /* Number of the page to get */ - MemPage **ppPage /* Write the page pointer here */ + BtShared *pBt, /* The database file */ + Pgno pgno, /* Number of the page to get */ + MemPage **ppPage, /* Write the page pointer here */ + int bReadonly /* True if a read-only (mmap) page is ok */ ){ int rc; assert( sqlite3_mutex_held(pBt->mutex) ); @@ -1609,7 +1645,7 @@ static int getAndInitPage( if( pgno>btreePagecount(pBt) ){ rc = SQLITE_CORRUPT_BKPT; }else{ - rc = btreeGetPage(pBt, pgno, ppPage, 0); + rc = btreeGetPage(pBt, pgno, ppPage, 0, bReadonly); if( rc==SQLITE_OK ){ rc = btreeInitPage(*ppPage); if( rc!=SQLITE_OK ){ @@ -1840,6 +1876,7 @@ int sqlite3BtreeOpen( rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ + sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap); rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ @@ -2106,6 +2143,19 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){ return SQLITE_OK; } +/* +** Change the limit on the amount of the database file that may be +** memory mapped. +*/ +int sqlite3BtreeSetMmapLimit(Btree *p, sqlite3_int64 szMmap){ + BtShared *pBt = p->pBt; + assert( sqlite3_mutex_held(p->db->mutex) ); + sqlite3BtreeEnter(p); + sqlite3PagerSetMmapLimit(pBt->pPager, szMmap); + sqlite3BtreeLeave(p); + return SQLITE_OK; +} + /* ** Change the way data is synced to disk in order to increase or decrease ** how well the database resists damage due to OS crashes and power @@ -2210,7 +2260,7 @@ int sqlite3BtreeGetPageSize(Btree *p){ ** known that the shared b-tree mutex is held, but the mutex on the ** database handle that owns *p is not. In this case if sqlite3BtreeEnter() ** were to be called, it might collide with some other operation on the -** database handle that owns *p, causing undefined behaviour. +** database handle that owns *p, causing undefined behavior. */ int sqlite3BtreeGetReserveNoMutex(Btree *p){ assert( sqlite3_mutex_held(p->pBt->mutex) ); @@ -2331,7 +2381,7 @@ static int lockBtree(BtShared *pBt){ assert( pBt->pPage1==0 ); rc = sqlite3PagerSharedLock(pBt->pPager); if( rc!=SQLITE_OK ) return rc; - rc = btreeGetPage(pBt, 1, &pPage1, 0); + rc = btreeGetPage(pBt, 1, &pPage1, 0, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is @@ -2467,6 +2517,29 @@ page1_init_failed: return rc; } +#ifndef NDEBUG +/* +** Return the number of cursors open on pBt. This is for use +** in assert() expressions, so it is only compiled if NDEBUG is not +** defined. +** +** Only write cursors are counted if wrOnly is true. If wrOnly is +** false then all cursors are counted. +** +** For the purposes of this routine, a cursor is any cursor that +** is capable of reading or writing to the databse. Cursors that +** have been tripped into the CURSOR_FAULT state are not counted. +*/ +static int countValidCursors(BtShared *pBt, int wrOnly){ + BtCursor *pCur; + int r = 0; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( (wrOnly==0 || pCur->wrFlag) && pCur->eState!=CURSOR_FAULT ) r++; + } + return r; +} +#endif + /* ** If there are no outstanding cursors and we are not in the middle ** of a transaction but there is a read lock on the database, then @@ -2477,7 +2550,7 @@ page1_init_failed: */ static void unlockBtreeIfUnused(BtShared *pBt){ assert( sqlite3_mutex_held(pBt->mutex) ); - assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE ); + assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE ); if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ assert( pBt->pPage1->aData ); assert( sqlite3PagerRefcount(pBt->pPager)==1 ); @@ -2595,6 +2668,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ goto trans_begun; } + assert( IfNotOmitAV(pBt->bDoTruncate)==0 ); /* Write transactions are not possible on a read-only database */ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ @@ -2889,7 +2963,7 @@ static int relocatePage( ** iPtrPage. */ if( eType!=PTRMAP_ROOTPAGE ){ - rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); + rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -2911,24 +2985,23 @@ static int relocatePage( static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8); /* -** Perform a single step of an incremental-vacuum. If successful, -** return SQLITE_OK. If there is no work to do (and therefore no -** point in calling this function again), return SQLITE_DONE. +** Perform a single step of an incremental-vacuum. If successful, return +** SQLITE_OK. If there is no work to do (and therefore no point in +** calling this function again), return SQLITE_DONE. Or, if an error +** occurs, return some other error code. ** -** More specificly, this function attempts to re-organize the -** database so that the last page of the file currently in use -** is no longer in use. +** More specificly, this function attempts to re-organize the database so +** that the last page of the file currently in use is no longer in use. ** -** If the nFin parameter is non-zero, this function assumes -** that the caller will keep calling incrVacuumStep() until -** it returns SQLITE_DONE or an error, and that nFin is the -** number of pages the database file will contain after this -** process is complete. If nFin is zero, it is assumed that -** incrVacuumStep() will be called a finite amount of times -** which may or may not empty the freelist. A full autovacuum -** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0. +** Parameter nFin is the number of pages that this database would contain +** were this function called until it returns SQLITE_DONE. +** +** If the bCommit parameter is non-zero, this function assumes that the +** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE +** or an error. bCommit is passed true for an auto-vacuum-on-commmit +** operation, or false for an incremental vacuum. */ -static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ +static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ Pgno nFreeList; /* Number of pages still on the free-list */ int rc; @@ -2953,15 +3026,15 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ } if( eType==PTRMAP_FREEPAGE ){ - if( nFin==0 ){ + if( bCommit==0 ){ /* Remove the page from the files free-list. This is not required - ** if nFin is non-zero. In that case, the free-list will be + ** if bCommit is non-zero. In that case, the free-list will be ** truncated to zero after this function returns, so it doesn't ** matter if it still contains some garbage entries. */ Pgno iFreePg; MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1); + rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT); if( rc!=SQLITE_OK ){ return rc; } @@ -2971,34 +3044,37 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ } else { Pgno iFreePg; /* Index of free page to move pLastPg to */ MemPage *pLastPg; + u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */ + Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */ - rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); + rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0, 0); if( rc!=SQLITE_OK ){ return rc; } - /* If nFin is zero, this loop runs exactly once and page pLastPg + /* If bCommit is zero, this loop runs exactly once and page pLastPg ** is swapped with the first free page pulled off the free list. ** - ** On the other hand, if nFin is greater than zero, then keep + ** On the other hand, if bCommit is greater than zero, then keep ** looping until a free-page located within the first nFin pages ** of the file is found. */ + if( bCommit==0 ){ + eMode = BTALLOC_LE; + iNear = nFin; + } do { MemPage *pFreePg; - rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0); + rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode); if( rc!=SQLITE_OK ){ releasePage(pLastPg); return rc; } releasePage(pFreePg); - }while( nFin!=0 && iFreePg>nFin ); + }while( bCommit && iFreePg>nFin ); assert( iFreePgpDbPage); - if( rc==SQLITE_OK ){ - rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0); - } + rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, bCommit); releasePage(pLastPg); if( rc!=SQLITE_OK ){ return rc; @@ -3006,29 +3082,39 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ } } - if( nFin==0 ){ - iLastPg--; - while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ - if( PTRMAP_ISPAGE(pBt, iLastPg) ){ - MemPage *pPg; - rc = btreeGetPage(pBt, iLastPg, &pPg, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite3PagerWrite(pPg->pDbPage); - releasePage(pPg); - if( rc!=SQLITE_OK ){ - return rc; - } - } + if( bCommit==0 ){ + do { iLastPg--; - } - sqlite3PagerTruncateImage(pBt->pPager, iLastPg); + }while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) ); + pBt->bDoTruncate = 1; pBt->nPage = iLastPg; } return SQLITE_OK; } +/* +** The database opened by the first argument is an auto-vacuum database +** nOrig pages in size containing nFree free pages. Return the expected +** size of the database in pages following an auto-vacuum operation. +*/ +static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){ + int nEntry; /* Number of entries on one ptrmap page */ + Pgno nPtrmap; /* Number of PtrMap pages to be freed */ + Pgno nFin; /* Return value */ + + nEntry = pBt->usableSize/5; + nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; + nFin = nOrig - nFree - nPtrmap; + if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinautoVacuum ){ rc = SQLITE_DONE; }else{ - invalidateAllOverflowCache(pBt); - rc = incrVacuumStep(pBt, 0, btreePagecount(pBt)); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); - put4byte(&pBt->pPage1->aData[28], pBt->nPage); + Pgno nOrig = btreePagecount(pBt); + Pgno nFree = get4byte(&pBt->pPage1->aData[36]); + Pgno nFin = finalDbSize(pBt, nOrig, nFree); + + if( nOrig0 ){ + rc = saveAllCursors(pBt, 0, 0); + if( rc==SQLITE_OK ){ + invalidateAllOverflowCache(pBt); + rc = incrVacuumStep(pBt, nFin, nOrig, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); + put4byte(&pBt->pPage1->aData[28], pBt->nPage); + } + }else{ + rc = SQLITE_DONE; } } sqlite3BtreeLeave(p); @@ -3077,9 +3176,7 @@ static int autoVacuumCommit(BtShared *pBt){ if( !pBt->incrVacuum ){ Pgno nFin; /* Number of pages in database after autovacuuming */ Pgno nFree; /* Number of pages on the freelist initially */ - Pgno nPtrmap; /* Number of PtrMap pages to be freed */ Pgno iFree; /* The next page to be freed */ - int nEntry; /* Number of entries on one ptrmap page */ Pgno nOrig; /* Database size before freeing */ nOrig = btreePagecount(pBt); @@ -3092,26 +3189,20 @@ static int autoVacuumCommit(BtShared *pBt){ } nFree = get4byte(&pBt->pPage1->aData[36]); - nEntry = pBt->usableSize/5; - nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; - nFin = nOrig - nFree - nPtrmap; - if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinnOrig ) return SQLITE_CORRUPT_BKPT; - + if( nFinnFin && rc==SQLITE_OK; iFree--){ - rc = incrVacuumStep(pBt, nFin, iFree); + rc = incrVacuumStep(pBt, nFin, iFree, 1); } if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); put4byte(&pBt->pPage1->aData[32], 0); put4byte(&pBt->pPage1->aData[36], 0); put4byte(&pBt->pPage1->aData[28], nFin); - sqlite3PagerTruncateImage(pBt->pPager, nFin); + pBt->bDoTruncate = 1; pBt->nPage = nFin; } if( rc!=SQLITE_OK ){ @@ -3119,7 +3210,7 @@ static int autoVacuumCommit(BtShared *pBt){ } } - assert( nRef==sqlite3PagerRefcount(pPager) ); + assert( nRef>=sqlite3PagerRefcount(pPager) ); return rc; } @@ -3166,6 +3257,9 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ return rc; } } + if( pBt->bDoTruncate ){ + sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); + } #endif rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0); sqlite3BtreeLeave(p); @@ -3181,7 +3275,9 @@ static void btreeEndTransaction(Btree *p){ BtShared *pBt = p->pBt; assert( sqlite3BtreeHoldsMutex(p) ); - btreeClearHasContent(pBt); +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->bDoTruncate = 0; +#endif if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ /* If there are other active statements that belong to this database ** handle, downgrade to a read-only transaction. The other statements @@ -3256,6 +3352,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ return rc; } pBt->inTransaction = TRANS_READ; + btreeClearHasContent(pBt); } btreeEndTransaction(p); @@ -3277,27 +3374,6 @@ int sqlite3BtreeCommit(Btree *p){ return rc; } -#ifndef NDEBUG -/* -** Return the number of write-cursors open on this handle. This is for use -** in assert() expressions, so it is only compiled if NDEBUG is not -** defined. -** -** For the purposes of this routine, a write-cursor is any cursor that -** is capable of writing to the databse. That means the cursor was -** originally opened for writing and the cursor has not be disabled -** by having its state changed to CURSOR_FAULT. -*/ -static int countWriteCursors(BtShared *pBt){ - BtCursor *pCur; - int r = 0; - for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++; - } - return r; -} -#endif - /* ** This routine sets the state to CURSOR_FAULT and the error ** code to errCode for every cursor on BtShared that pBtree @@ -3369,7 +3445,7 @@ int sqlite3BtreeRollback(Btree *p, int tripCode){ /* The rollback may have destroyed the pPage1->aData value. So ** call btreeGetPage() on page 1 again to make ** sure pPage1->aData is set correctly. */ - if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ + if( btreeGetPage(pBt, 1, &pPage1, 0, 0)==SQLITE_OK ){ int nPage = get4byte(28+(u8*)pPage1->aData); testcase( nPage==0 ); if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage); @@ -3377,8 +3453,9 @@ int sqlite3BtreeRollback(Btree *p, int tripCode){ pBt->nPage = nPage; releasePage(pPage1); } - assert( countWriteCursors(pBt)==0 ); + assert( countValidCursors(pBt, 1)==0 ); pBt->inTransaction = TRANS_READ; + btreeClearHasContent(pBt); } btreeEndTransaction(p); @@ -3803,7 +3880,7 @@ static int getOverflowPage( assert( next==0 || rc==SQLITE_DONE ); if( rc==SQLITE_OK ){ - rc = btreeGetPage(pBt, ovfl, &pPage, 0); + rc = btreeGetPage(pBt, ovfl, &pPage, 0, (ppPage==0)); assert( rc==SQLITE_OK || pPage==0 ); if( rc==SQLITE_OK ){ next = get4byte(pPage->aData); @@ -4024,7 +4101,9 @@ static int accessPayload( { DbPage *pDbPage; - rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage); + rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage, + (eOp==0 ? PAGER_ACQUIRE_READONLY : 0) + ); if( rc==SQLITE_OK ){ aPayload = sqlite3PagerGetData(pDbPage); nextPage = get4byte(aPayload); @@ -4203,10 +4282,11 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPageiPage>=0 ); if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ return SQLITE_CORRUPT_BKPT; } - rc = getAndInitPage(pBt, newPgno, &pNewPage); + rc = getAndInitPage(pBt, newPgno, &pNewPage, (pCur->wrFlag==0)); if( rc ) return rc; pCur->apPage[i+1] = pNewPage; pCur->aiIdx[i+1] = 0; @@ -4323,7 +4403,7 @@ static int moveToRoot(BtCursor *pCur){ pCur->eState = CURSOR_INVALID; return SQLITE_OK; }else{ - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); + rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0], pCur->wrFlag==0); if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; @@ -4853,21 +4933,23 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ ** an error. *ppPage and *pPgno are undefined in the event of an error. ** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned. ** -** If the "nearby" parameter is not 0, then a (feeble) effort is made to +** If the "nearby" parameter is not 0, then an effort is made to ** locate a page close to the page number "nearby". This can be used in an ** attempt to keep related pages close to each other in the database file, ** which in turn can make database access faster. ** -** If the "exact" parameter is not 0, and the page-number nearby exists -** anywhere on the free-list, then it is guarenteed to be returned. This -** is only used by auto-vacuum databases when allocating a new table. +** If the eMode parameter is BTALLOC_EXACT and the nearby page exists +** anywhere on the free-list, then it is guaranteed to be returned. If +** eMode is BTALLOC_LT then the page returned will be less than or equal +** to nearby if any such page exists. If eMode is BTALLOC_ANY then there +** are no restrictions on which page is returned. */ static int allocateBtreePage( - BtShared *pBt, - MemPage **ppPage, - Pgno *pPgno, - Pgno nearby, - u8 exact + BtShared *pBt, /* The btree */ + MemPage **ppPage, /* Store pointer to the allocated page here */ + Pgno *pPgno, /* Store the page number here */ + Pgno nearby, /* Search for a page near this one */ + u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */ ){ MemPage *pPage1; int rc; @@ -4878,6 +4960,7 @@ static int allocateBtreePage( Pgno mxPage; /* Total size of the database file */ assert( sqlite3_mutex_held(pBt->mutex) ); + assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); n = get4byte(&pPage1->aData[36]); @@ -4890,21 +4973,24 @@ static int allocateBtreePage( Pgno iTrunk; u8 searchList = 0; /* If the free-list must be searched for 'nearby' */ - /* If the 'exact' parameter was true and a query of the pointer-map + /* If eMode==BTALLOC_EXACT and a query of the pointer-map ** shows that the page 'nearby' is somewhere on the free-list, then ** the entire-list will be searched for that page. */ #ifndef SQLITE_OMIT_AUTOVACUUM - if( exact && nearby<=mxPage ){ - u8 eType; - assert( nearby>0 ); - assert( pBt->autoVacuum ); - rc = ptrmapGet(pBt, nearby, &eType, 0); - if( rc ) return rc; - if( eType==PTRMAP_FREEPAGE ){ - searchList = 1; + if( eMode==BTALLOC_EXACT ){ + if( nearby<=mxPage ){ + u8 eType; + assert( nearby>0 ); + assert( pBt->autoVacuum ); + rc = ptrmapGet(pBt, nearby, &eType, 0); + if( rc ) return rc; + if( eType==PTRMAP_FREEPAGE ){ + searchList = 1; + } } - *pPgno = nearby; + }else if( eMode==BTALLOC_LE ){ + searchList = 1; } #endif @@ -4917,7 +5003,8 @@ static int allocateBtreePage( /* The code within this loop is run only once if the 'searchList' variable ** is not true. Otherwise, it runs once for each trunk-page on the - ** free-list until the page 'nearby' is located. + ** free-list until the page 'nearby' is located (eMode==BTALLOC_EXACT) + ** or until a page less than 'nearby' is located (eMode==BTALLOC_LT) */ do { pPrevTrunk = pTrunk; @@ -4930,7 +5017,7 @@ static int allocateBtreePage( if( iTrunk>mxPage ){ rc = SQLITE_CORRUPT_BKPT; }else{ - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0, 0); } if( rc ){ pTrunk = 0; @@ -4959,11 +5046,13 @@ static int allocateBtreePage( rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; #ifndef SQLITE_OMIT_AUTOVACUUM - }else if( searchList && nearby==iTrunk ){ + }else if( searchList + && (nearby==iTrunk || (iTrunkpDbPage); @@ -4992,7 +5081,7 @@ static int allocateBtreePage( goto end_allocate_page; } testcase( iNewTrunk==mxPage ); - rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); + rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0, 0); if( rc!=SQLITE_OK ){ goto end_allocate_page; } @@ -5026,14 +5115,24 @@ static int allocateBtreePage( unsigned char *aData = pTrunk->aData; if( nearby>0 ){ u32 i; - int dist; closest = 0; - dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby); - for(i=1; ipDbPage); if( rc!=SQLITE_OK ){ @@ -5074,8 +5175,26 @@ static int allocateBtreePage( pPrevTrunk = 0; }while( searchList ); }else{ - /* There are no pages on the freelist, so create a new page at the - ** end of the file */ + /* There are no pages on the freelist, so append a new page to the + ** database image. + ** + ** Normally, new pages allocated by this block can be requested from the + ** pager layer with the 'no-content' flag set. This prevents the pager + ** from trying to read the pages content from disk. However, if the + ** current transaction has already run one or more incremental-vacuum + ** steps, then the page we are about to allocate may contain content + ** that is required in the event of a rollback. In this case, do + ** not set the no-content flag. This causes the pager to load and journal + ** the current page content before overwriting it. + ** + ** Note that the pager will not actually attempt to load or journal + ** content for any page that really does lie past the end of the database + ** file on disk. So the effects of disabling the no-content optimization + ** here are confined to those pages that lie between the end of the + ** database image and the end of the database file. + */ + int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate)); + rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); if( rc ) return rc; pBt->nPage++; @@ -5090,7 +5209,7 @@ static int allocateBtreePage( MemPage *pPg = 0; TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1); + rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); @@ -5104,7 +5223,7 @@ static int allocateBtreePage( *pPgno = pBt->nPage; assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = btreeGetPage(pBt, *pPgno, ppPage, 1); + rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent, 0); if( rc ) return rc; rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ @@ -5172,7 +5291,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ /* If the secure_delete option is enabled, then ** always fully overwrite deleted information with zeros. */ - if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) ) + if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0, 0))!=0) ) || ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0) ){ goto freepage_out; @@ -5199,7 +5318,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ u32 nLeaf; /* Initial number of leaf cells on trunk page */ iTrunk = get4byte(&pPage1->aData[32]); - rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0, 0); if( rc!=SQLITE_OK ){ goto freepage_out; } @@ -5245,7 +5364,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ ** first trunk in the free-list is full. Either way, the page being freed ** will become the new first trunk page in the free-list. */ - if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ + if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0, 0)) ){ goto freepage_out; } rc = sqlite3PagerWrite(pPage->pDbPage); @@ -5432,7 +5551,7 @@ static int fillInCell( ** If this is the first overflow page, then write a partial entry ** to the pointer-map. If we write nothing to this pointer-map slot, ** then the optimistic overflow chain processing in clearCell() - ** may misinterpret the uninitialised values and delete the + ** may misinterpret the uninitialized values and delete the ** wrong pages from the database. */ if( pBt->autoVacuum && rc==SQLITE_OK ){ @@ -6046,7 +6165,7 @@ static int balance_nonroot( } pgno = get4byte(pRight); while( 1 ){ - rc = getAndInitPage(pBt, pgno, &apOld[i]); + rc = getAndInitPage(pBt, pgno, &apOld[i], 0); if( rc ){ memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; @@ -6905,7 +7024,7 @@ int sqlite3BtreeInsert( insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); - /* If no error has occured and pPage has an overflow cell, call balance() + /* If no error has occurred and pPage has an overflow cell, call balance() ** to redistribute the cells within the tree. Since balance() may move ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey ** variables. @@ -7119,7 +7238,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ ** be moved to the allocated page (unless the allocated page happens ** to reside at pgnoRoot). */ - rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1); + rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT); if( rc!=SQLITE_OK ){ return rc; } @@ -7134,10 +7253,17 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ u8 eType = 0; Pgno iPtrPage = 0; + /* Save the positions of any open cursors. This is required in + ** case they are holding a reference to an xFetch reference + ** corresponding to page pgnoRoot. */ + rc = saveAllCursors(pBt, 0, 0); releasePage(pPageMove); + if( rc!=SQLITE_OK ){ + return rc; + } /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -7158,7 +7284,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ if( rc!=SQLITE_OK ){ return rc; } - rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -7234,7 +7360,7 @@ static int clearDatabasePage( return SQLITE_CORRUPT_BKPT; } - rc = getAndInitPage(pBt, pgno, &pPage); + rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; for(i=0; inCell; i++){ pCell = findCell(pPage, i); @@ -7336,7 +7462,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ return SQLITE_LOCKED_SHAREDCACHE; } - rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); + rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0, 0); if( rc ) return rc; rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ){ @@ -7371,7 +7497,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ */ MemPage *pMove; releasePage(pPage); - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -7381,7 +7507,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ return rc; } pMove = 0; - rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0, 0); freePage(pMove, &rc); releasePage(pMove); if( rc!=SQLITE_OK ){ @@ -7793,7 +7919,7 @@ static int checkTreePage( usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ + if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0, 0))!=0 ){ checkAppendMsg(pCheck, zContext, "unable to get the page. error code=%d", rc); return 0; @@ -8026,7 +8152,7 @@ char *sqlite3BtreeIntegrityCheck( } i = PENDING_BYTE_PAGE(pBt); if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); - sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000); + sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); sCheck.errMsg.useMalloc = 2; /* Check the integrity of the freelist @@ -8265,6 +8391,17 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ return SQLITE_ABORT; } + /* Save the positions of all other cursors open on this table. This is + ** required in case any of them are holding references to an xFetch + ** version of the b-tree page modified by the accessPayload call below. + ** + ** Note that pCsr must be open on a BTREE_INTKEY table and saveCursorPosition() + ** and hence saveAllCursors() cannot fail on a BTREE_INTKEY table, hence + ** saveAllCursors can only return SQLITE_OK. + */ + VVA_ONLY(rc =) saveAllCursors(pCsr->pBt, pCsr->pgnoRoot, pCsr); + assert( rc==SQLITE_OK ); + /* Check some assumptions: ** (a) the cursor is open for writing, ** (b) there is a read/write transaction open, diff --git a/src/btree.h b/src/btree.h index d4c9fe3..ace0f8c 100644 --- a/src/btree.h +++ b/src/btree.h @@ -63,6 +63,7 @@ int sqlite3BtreeOpen( int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); +int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64); int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int); int sqlite3BtreeSyncDisabled(Btree*); int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); @@ -139,6 +140,7 @@ int sqlite3BtreeNewDb(Btree *p); #define BTREE_TEXT_ENCODING 5 #define BTREE_USER_VERSION 6 #define BTREE_INCR_VACUUM 7 +#define BTREE_APPLICATION_ID 8 /* ** Values that may be OR'd together to form the second argument of an diff --git a/src/btreeInt.h b/src/btreeInt.h index b157dec..ce3c549 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -411,6 +411,7 @@ struct BtShared { #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ + u8 bDoTruncate; /* True to truncate db on commit */ #endif u8 inTransaction; /* Transaction state */ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ diff --git a/src/build.c b/src/build.c index c21f017..3c91cdc 100644 --- a/src/build.c +++ b/src/build.c @@ -2100,7 +2100,7 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){ /* Drop all SQLITE_MASTER table and index entries that refer to the ** table. The program name loops through the master table and deletes ** every row that refers to a table of the same name as the one being - ** dropped. Triggers are handled seperately because a trigger can be + ** dropped. Triggers are handled separately because a trigger can be ** created in the temp database that refers to a table in another ** database. */ @@ -2392,9 +2392,6 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ int tnum; /* Root page of index */ Vdbe *v; /* Generate code into this virtual machine */ KeyInfo *pKey; /* KeyInfo for index */ -#ifdef SQLITE_OMIT_MERGE_SORT - int regIdxKey; /* Registers containing the index key */ -#endif int regRecord; /* Register holding assemblied index record */ sqlite3 *db = pParse->db; /* The database connection */ int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema); @@ -2422,13 +2419,9 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ (char *)pKey, P4_KEYINFO_HANDOFF); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); -#ifndef SQLITE_OMIT_MERGE_SORT /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO); -#else - iSorter = iTab; -#endif /* Open the table. Loop through all rows of the table, inserting index ** records into the sorter. */ @@ -2436,7 +2429,6 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); regRecord = sqlite3GetTempReg(pParse); -#ifndef SQLITE_OMIT_MERGE_SORT sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); @@ -2447,8 +2439,8 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ sqlite3VdbeAddOp2(v, OP_Goto, 0, j2); addr2 = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord); - sqlite3HaltConstraint( - pParse, OE_Abort, "indexed columns are not unique", P4_STATIC + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE, + OE_Abort, "indexed columns are not unique", P4_STATIC ); }else{ addr2 = sqlite3VdbeCurrentAddr(v); @@ -2456,30 +2448,6 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord); sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); -#else - regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1); - addr2 = addr1 + 1; - if( pIndex->onError!=OE_None ){ - const int regRowid = regIdxKey + pIndex->nColumn; - const int j2 = sqlite3VdbeCurrentAddr(v) + 2; - void * const pRegKey = SQLITE_INT_TO_PTR(regIdxKey); - - /* The registers accessed by the OP_IsUnique opcode were allocated - ** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey() - ** call above. Just before that function was freed they were released - ** (made available to the compiler for reuse) using - ** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique - ** opcode use the values stored within seems dangerous. However, since - ** we can be sure that no other temp registers have been allocated - ** since sqlite3ReleaseTempRange() was called, it is safe to do so. - */ - sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32); - sqlite3HaltConstraint( - pParse, OE_Abort, "indexed columns are not unique", P4_STATIC); - } - sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0); - sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); -#endif sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); sqlite3VdbeJumpHere(v, addr1); @@ -2594,7 +2562,7 @@ Index *sqlite3CreateIndex( assert( pTab!=0 ); assert( pParse->nErr==0 ); if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 - && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){ + && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){ sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); goto exit_create_index; } @@ -2691,10 +2659,8 @@ Index *sqlite3CreateIndex( for(i=0; inExpr; i++){ Expr *pExpr = pList->a[i].pExpr; if( pExpr ){ - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr); - if( pColl ){ - nExtra += (1 + sqlite3Strlen30(pColl->zName)); - } + assert( pExpr->op==TK_COLLATE ); + nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken)); } } @@ -2755,7 +2721,6 @@ Index *sqlite3CreateIndex( const char *zColName = pListItem->zName; Column *pTabCol; int requestedSortOrder; - CollSeq *pColl; /* Collating sequence */ char *zColl; /* Collation sequence name */ for(j=0, pTabCol=pTab->aCol; jnCol; j++, pTabCol++){ @@ -2768,11 +2733,10 @@ Index *sqlite3CreateIndex( goto exit_create_index; } pIndex->aiColumn[i] = j; - if( pListItem->pExpr - && (pColl = sqlite3ExprCollSeq(pParse, pListItem->pExpr))!=0 - ){ + if( pListItem->pExpr ){ int nColl; - zColl = pColl->zName; + assert( pListItem->pExpr->op==TK_COLLATE ); + zColl = pListItem->pExpr->u.zToken; nColl = sqlite3Strlen30(zColl) + 1; assert( nExtra>=nColl ); memcpy(zExtra, zColl, nColl); @@ -2781,9 +2745,7 @@ Index *sqlite3CreateIndex( nExtra -= nColl; }else{ zColl = pTab->aCol[j].zColl; - if( !zColl ){ - zColl = "BINARY"; - } + if( !zColl ) zColl = "BINARY"; } if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){ goto exit_create_index; @@ -2839,7 +2801,7 @@ Index *sqlite3CreateIndex( ** However the ON CONFLICT clauses are different. If both this ** constraint and the previous equivalent constraint have explicit ** ON CONFLICT clauses this is an error. Otherwise, use the - ** explicitly specified behaviour for the index. + ** explicitly specified behavior for the index. */ if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){ sqlite3ErrorMsg(pParse, @@ -3692,12 +3654,19 @@ void sqlite3MayAbort(Parse *pParse){ ** error. The onError parameter determines which (if any) of the statement ** and/or current transaction is rolled back. */ -void sqlite3HaltConstraint(Parse *pParse, int onError, char *p4, int p4type){ +void sqlite3HaltConstraint( + Parse *pParse, /* Parsing context */ + int errCode, /* extended error code */ + int onError, /* Constraint type */ + char *p4, /* Error message */ + int p4type /* P4_STATIC or P4_TRANSIENT */ +){ Vdbe *v = sqlite3GetVdbe(pParse); + assert( (errCode&0xff)==SQLITE_CONSTRAINT ); if( onError==OE_Abort ){ sqlite3MayAbort(pParse); } - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type); + sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type); } /* diff --git a/src/ctime.c b/src/ctime.c index 5dee724..60595ff 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -48,15 +48,15 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_COVERAGE_TEST "COVERAGE_TEST", #endif -#ifdef SQLITE_CURDIR - "CURDIR", -#endif #ifdef SQLITE_DEBUG "DEBUG", #endif #ifdef 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 "DISABLE_DIRSYNC", #endif @@ -147,6 +147,9 @@ static const char * const azCompileOpt[] = { #ifdef 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 @@ -204,11 +207,6 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_OMIT_CHECK "OMIT_CHECK", #endif -/* // redundant -** #ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS -** "OMIT_COMPILEOPTION_DIAGS", -** #endif -*/ #ifdef SQLITE_OMIT_COMPLETE "OMIT_COMPLETE", #endif @@ -263,9 +261,6 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_OMIT_MEMORYDB "OMIT_MEMORYDB", #endif -#ifdef SQLITE_OMIT_MERGE_SORT - "OMIT_MERGE_SORT", -#endif #ifdef SQLITE_OMIT_OR_OPTIMIZATION "OMIT_OR_OPTIMIZATION", #endif @@ -353,13 +348,13 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_TCL "TCL", #endif -#ifdef SQLITE_TEMP_STORE +#if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc) "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), #endif #ifdef SQLITE_TEST "TEST", #endif -#ifdef SQLITE_THREADSAFE +#if defined(SQLITE_THREADSAFE) "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), #endif #ifdef SQLITE_USE_ALLOCA @@ -385,8 +380,11 @@ int sqlite3_compileoption_used(const char *zOptName){ /* Since ArraySize(azCompileOpt) is normally in single digits, a ** linear search is adequate. No need for a binary search. */ for(i=0; idb; + int iDb = sqlite3SchemaToIndex(db, pView->pSchema); - pDup = sqlite3SelectDup(db, pView->pSelect, 0); - if( pWhere ){ - SrcList *pFrom; - - pWhere = sqlite3ExprDup(db, pWhere, 0); - pFrom = sqlite3SrcListAppend(db, 0, 0, 0); - if( pFrom ){ - assert( pFrom->nSrc==1 ); - pFrom->a[0].zAlias = sqlite3DbStrDup(db, pView->zName); - pFrom->a[0].pSelect = pDup; - assert( pFrom->a[0].pOn==0 ); - assert( pFrom->a[0].pUsing==0 ); - }else{ - sqlite3SelectDelete(db, pDup); - } - pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); - if( pDup ) pDup->selFlags |= SF_Materialize; + pWhere = sqlite3ExprDup(db, pWhere, 0); + pFrom = sqlite3SrcListAppend(db, 0, 0, 0); + + if( pFrom ){ + assert( pFrom->nSrc==1 ); + pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); + pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName); + assert( pFrom->a[0].pOn==0 ); + assert( pFrom->a[0].pUsing==0 ); } + + pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); + if( pSel ) pSel->selFlags |= SF_Materialize; + sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); - sqlite3Select(pParse, pDup, &dest); - sqlite3SelectDelete(db, pDup); + sqlite3Select(pParse, pSel, &dest); + sqlite3SelectDelete(db, pSel); } #endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */ diff --git a/src/expr.c b/src/expr.c index 9ca34ec..660397e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -116,12 +116,7 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ } assert( op!=TK_REGISTER || p->op2!=TK_COLLATE ); if( op==TK_COLLATE ){ - if( db->init.busy ){ - /* Do not report errors when parsing while the schema */ - pColl = sqlite3FindCollSeq(db, ENC(db), p->u.zToken, 0); - }else{ - pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); - } + pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } if( p->pTab!=0 @@ -638,7 +633,7 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ */ ynVar i; for(i=0; inzVar; i++){ - if( pParse->azVar[i] && memcmp(pParse->azVar[i],z,n+1)==0 ){ + if( pParse->azVar[i] && strcmp(pParse->azVar[i],z)==0 ){ pExpr->iColumn = x = (ynVar)i+1; break; } @@ -1214,6 +1209,7 @@ static int selectNodeIsConstant(Walker *pWalker, Select *NotUsed){ } static int exprIsConst(Expr *p, int initFlag){ Walker w; + memset(&w, 0, sizeof(w)); w.u.i = initFlag; w.xExprCallback = exprNodeIsConstant; w.xSelectCallback = selectNodeIsConstant; @@ -1456,10 +1452,11 @@ int sqlite3CodeOnce(Parse *pParse){ ** ** The returned value of this function indicates the b-tree type, as follows: ** -** IN_INDEX_ROWID - The cursor was opened on a database table. -** IN_INDEX_INDEX - The cursor was opened on a database index. -** IN_INDEX_EPH - The cursor was opened on a specially created and -** populated epheremal table. +** IN_INDEX_ROWID - The cursor was opened on a database table. +** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index. +** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index. +** IN_INDEX_EPH - The cursor was opened on a specially created and +** populated epheremal table. ** ** An existing b-tree might be used if the RHS expression pX is a simple ** subquery such as: @@ -1582,7 +1579,8 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb, pKey,P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); - eType = IN_INDEX_INDEX; + assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); + eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; sqlite3VdbeJumpHere(v, iAddr); if( prNotFound && !pTab->aCol[iCol].notNull ){ @@ -2935,7 +2933,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); }else{ - sqlite3HaltConstraint(pParse, pExpr->affinity, pExpr->u.zToken, 0); + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, + pExpr->affinity, pExpr->u.zToken, 0); } break; @@ -3281,6 +3280,12 @@ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ sqlite3ExplainPush(pOut); sqlite3ExplainExpr(pOut, pList->a[i].pExpr); sqlite3ExplainPop(pOut); + if( pList->a[i].zName ){ + sqlite3ExplainPrintf(pOut, " AS %s", pList->a[i].zName); + } + if( pList->a[i].bSpanIsTab ){ + sqlite3ExplainPrintf(pOut, " (%s)", pList->a[i].zSpan); + } if( inExpr-1 ){ sqlite3ExplainNL(pOut); } @@ -3419,8 +3424,8 @@ void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){ Walker w; if( pParse->cookieGoto ) return; if( OptimizationDisabled(pParse->db, SQLITE_FactorOutConst) ) return; + memset(&w, 0, sizeof(w)); w.xExprCallback = evalConstExpr; - w.xSelectCallback = 0; w.pParse = pParse; sqlite3WalkExpr(&w, pExpr); } @@ -3533,7 +3538,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ int r1, r2; assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 ); - if( NEVER(v==0) ) return; /* Existance of VDBE checked by caller */ + if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ if( NEVER(pExpr==0) ) return; /* No way this can happen */ op = pExpr->op; switch( op ){ @@ -3653,7 +3658,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ int r1, r2; assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 ); - if( NEVER(v==0) ) return; /* Existance of VDBE checked by caller */ + if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */ if( pExpr==0 ) return; /* The value of pExpr->op and op are related as follows: diff --git a/src/fkey.c b/src/fkey.c index 2d01e25..ac35bc1 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -21,8 +21,9 @@ ** -------------------------- ** ** Foreign keys in SQLite come in two flavours: deferred and immediate. -** If an immediate foreign key constraint is violated, SQLITE_CONSTRAINT -** is returned and the current statement transaction rolled back. If a +** If an immediate foreign key constraint is violated, +** SQLITE_CONSTRAINT_FOREIGNKEY is returned and the current +** statement transaction rolled back. If a ** deferred foreign key constraint is violated, no action is taken ** immediately. However if the application attempts to commit the ** transaction before fixing the constraint violation, the attempt fails. @@ -86,7 +87,8 @@ ** Immediate constraints are usually handled similarly. The only difference ** is that the counter used is stored as part of each individual statement ** object (struct Vdbe). If, after the statement has run, its immediate -** constraint counter is greater than zero, it returns SQLITE_CONSTRAINT +** constraint counter is greater than zero, +** it returns SQLITE_CONSTRAINT_FOREIGNKEY ** and the statement transaction is rolled back. An exception is an INSERT ** statement that inserts a single row only (no triggers). In this case, ** instead of using a counter, an exception is thrown immediately if the @@ -142,7 +144,7 @@ ** A foreign key constraint requires that the key columns in the parent ** table are collectively subject to a UNIQUE or PRIMARY KEY constraint. ** Given that pParent is the parent table for foreign key constraint pFKey, -** search the schema a unique index on the parent key columns. +** search the schema for a unique index on the parent key columns. ** ** If successful, zero is returned. If the parent key is an INTEGER PRIMARY ** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx @@ -178,7 +180,7 @@ ** into pParse. If an OOM error occurs, non-zero is returned and the ** pParse->db->mallocFailed flag is set. */ -static int locateFkeyIndex( +int sqlite3FkLocateIndex( Parse *pParse, /* Parse context to store any error in */ Table *pParent, /* Parent table of FK constraint pFKey */ FKey *pFKey, /* Foreign key to find index for */ @@ -275,7 +277,9 @@ static int locateFkeyIndex( if( !pIdx ){ if( !pParse->disableTriggers ){ - sqlite3ErrorMsg(pParse, "foreign key mismatch"); + sqlite3ErrorMsg(pParse, + "foreign key mismatch - \"%w\" referencing \"%w\"", + pFKey->pFrom->zName, pFKey->zTo); } sqlite3DbFree(pParse->db, aiCol); return 1; @@ -424,8 +428,8 @@ static void fkLookupParent( ** incrementing a counter. This is necessary as the VM code is being ** generated for will not open a statement transaction. */ assert( nIncr==1 ); - sqlite3HaltConstraint( - pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, + OE_Abort, "foreign key constraint failed", P4_STATIC ); }else{ if( nIncr>0 && pFKey->isDeferred==0 ){ @@ -665,8 +669,8 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ ** any modifications to the schema are made. This is because statement ** transactions are not able to rollback schema changes. */ sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); - sqlite3HaltConstraint( - pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, + OE_Abort, "foreign key constraint failed", P4_STATIC ); if( iSkip ){ @@ -736,7 +740,7 @@ void sqlite3FkCheck( }else{ pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb); } - if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ + if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) ); if( !isIgnoreErrors || db->mallocFailed ) return; if( pTo==0 ){ @@ -816,7 +820,7 @@ void sqlite3FkCheck( continue; } - if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ + if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ if( !isIgnoreErrors || db->mallocFailed ) return; continue; } @@ -871,7 +875,7 @@ u32 sqlite3FkOldmask( } for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ Index *pIdx = 0; - locateFkeyIndex(pParse, pTab, p, &pIdx, 0); + sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0); if( pIdx ){ for(i=0; inColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); } @@ -997,7 +1001,7 @@ static Trigger *fkActionTrigger( int i; /* Iterator variable */ Expr *pWhen = 0; /* WHEN clause for the trigger */ - if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; + if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; assert( aiCol || pFKey->nCol==1 ); for(i=0; inCol; i++){ diff --git a/src/func.c b/src/func.c index d4655ad..e40fdad 100644 --- a/src/func.c +++ b/src/func.c @@ -693,6 +693,13 @@ static int patternCompare( return *zString==0; } +/* +** The sqlite3_strglob() interface. +*/ +int sqlite3_strglob(const char *zGlobPattern, const char *zString){ + return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0; +} + /* ** Count the number of times that the LIKE operator (or GLOB which is ** just a variation of LIKE) gets called. This is used for testing @@ -962,6 +969,62 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ } } +/* +** The unicode() function. Return the integer unicode code-point value +** for the first character of the input string. +*/ +static void unicodeFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const unsigned char *z = sqlite3_value_text(argv[0]); + (void)argc; + if( z && z[0] ) sqlite3_result_int(context, sqlite3Utf8Read(&z)); +} + +/* +** The char() function takes zero or more arguments, each of which is +** an integer. It constructs a string where each character of the string +** is the unicode character for the corresponding integer argument. +*/ +static void charFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + unsigned char *z, *zOut; + int i; + zOut = z = sqlite3_malloc( argc*4 ); + if( z==0 ){ + sqlite3_result_error_nomem(context); + return; + } + for(i=0; i0x10ffff ) x = 0xfffd; + c = (unsigned)(x & 0x1fffff); + if( c<0x00080 ){ + *zOut++ = (u8)(c&0xFF); + }else if( c<0x00800 ){ + *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); + *zOut++ = 0x80 + (u8)(c & 0x3F); + }else if( c<0x10000 ){ + *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); + *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); + *zOut++ = 0x80 + (u8)(c & 0x3F); + }else{ + *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); + *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); + *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); + *zOut++ = 0x80 + (u8)(c & 0x3F); + } \ + } + sqlite3_result_text(context, (char*)z, (int)(zOut-z), sqlite3_free); +} + /* ** The hex() function. Interpret the argument as a blob. Return ** a hexadecimal rendering as text. @@ -1589,6 +1652,8 @@ void sqlite3RegisterGlobalFunctions(void){ FUNCTION(instr, 2, 0, 0, instrFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ), + FUNCTION(unicode, 1, 0, 0, unicodeFunc ), + FUNCTION(char, -1, 0, 0, charFunc ), FUNCTION(abs, 1, 0, 0, absFunc ), #ifndef SQLITE_OMIT_FLOATING_POINT FUNCTION(round, 1, 0, 0, roundFunc ), diff --git a/src/global.c b/src/global.c index f5da7e7..7b02cf2 100644 --- a/src/global.c +++ b/src/global.c @@ -156,6 +156,8 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = { (void*)0, /* pHeap */ 0, /* nHeap */ 0, 0, /* mnHeap, mxHeap */ + SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */ + SQLITE_MAX_MMAP_SIZE, /* mxMmap */ (void*)0, /* pScratch */ 0, /* szScratch */ 0, /* nScratch */ diff --git a/src/hash.h b/src/hash.h index 990a2d6..82b7c58 100644 --- a/src/hash.h +++ b/src/hash.h @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This is the header file for the generic hash-table implemenation +** This is the header file for the generic hash-table implementation ** used in SQLite. */ #ifndef _SQLITE_HASH_H_ diff --git a/src/insert.c b/src/insert.c index 89a5dc8..9a5661f 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1245,7 +1245,7 @@ void sqlite3GenerateConstraintChecks( case OE_Fail: { char *zMsg; sqlite3VdbeAddOp3(v, OP_HaltIfNull, - SQLITE_CONSTRAINT, onError, regData+i); + SQLITE_CONSTRAINT_NOTNULL, onError, regData+i); zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); @@ -1285,7 +1285,8 @@ void sqlite3GenerateConstraintChecks( }else{ zConsName = 0; } - sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC); + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK, + onError, zConsName, P4_DYNAMIC); } sqlite3VdbeResolveLabel(v, allOk); } @@ -1316,8 +1317,8 @@ void sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Abort: case OE_Fail: { - sqlite3HaltConstraint( - pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY, + onError, "PRIMARY KEY must be unique", P4_STATIC); break; } case OE_Replace: { @@ -1444,7 +1445,8 @@ void sqlite3GenerateConstraintChecks( sqlite3StrAccumAppend(&errMsg, pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); zErr = sqlite3StrAccumFinish(&errMsg); - sqlite3HaltConstraint(pParse, onError, zErr, 0); + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE, + onError, zErr, 0); sqlite3DbFree(errMsg.db, zErr); break; } @@ -1852,8 +1854,8 @@ static int xferOptimization( if( pDest->iPKey>=0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); - sqlite3HaltConstraint( - pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); + sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY, + onError, "PRIMARY KEY must be unique", P4_STATIC); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); }else if( pDest->pIndex==0 ){ diff --git a/src/journal.c b/src/journal.c index 06605cc..fed27be 100644 --- a/src/journal.c +++ b/src/journal.c @@ -59,6 +59,14 @@ static int createFile(JournalFile *p){ assert(p->iSize<=p->nBuf); rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0); } + if( rc!=SQLITE_OK ){ + /* If an error occurred while writing to the file, close it before + ** returning. This way, SQLite uses the in-memory journal data to + ** roll back changes made to the internal page-cache before this + ** function was called. */ + sqlite3OsClose(pReal); + p->pReal = 0; + } } } return rc; diff --git a/src/legacy.c b/src/legacy.c index ebab2de..94649ae 100644 --- a/src/legacy.c +++ b/src/legacy.c @@ -38,7 +38,6 @@ int sqlite3_exec( const char *zLeftover; /* Tail of unprocessed SQL */ sqlite3_stmt *pStmt = 0; /* The current SQL statement */ char **azCols = 0; /* Names of result columns */ - int nRetry = 0; /* Number of retry attempts */ int callbackIsInit; /* True if callback data is initialized */ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; @@ -46,12 +45,12 @@ int sqlite3_exec( sqlite3_mutex_enter(db->mutex); sqlite3Error(db, SQLITE_OK, 0); - while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ + while( rc==SQLITE_OK && zSql[0] ){ int nCol; char **azVals = 0; pStmt = 0; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ continue; @@ -108,11 +107,8 @@ int sqlite3_exec( if( rc!=SQLITE_ROW ){ rc = sqlite3VdbeFinalize((Vdbe *)pStmt); pStmt = 0; - if( rc!=SQLITE_SCHEMA ){ - nRetry = 0; - zSql = zLeftover; - while( sqlite3Isspace(zSql[0]) ) zSql++; - } + zSql = zLeftover; + while( sqlite3Isspace(zSql[0]) ) zSql++; break; } } diff --git a/src/loadext.c b/src/loadext.c index 3fcf500..cdcf6a9 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -378,6 +378,19 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_blob_reopen, sqlite3_vtab_config, sqlite3_vtab_on_conflict, + sqlite3_close_v2, + sqlite3_db_filename, + sqlite3_db_readonly, + sqlite3_db_release_memory, + sqlite3_errstr, + sqlite3_stmt_busy, + sqlite3_stmt_readonly, + sqlite3_stricmp, + sqlite3_uri_boolean, + sqlite3_uri_int64, + sqlite3_uri_parameter, + sqlite3_vsnprintf, + sqlite3_wal_checkpoint_v2 }; /* @@ -402,8 +415,23 @@ static int sqlite3LoadExtension( void *handle; int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*); char *zErrmsg = 0; + const char *zEntry; + char *zAltEntry = 0; void **aHandle; int nMsg = 300 + sqlite3Strlen30(zFile); + int ii; + + /* Shared library endings to try if zFile cannot be loaded as written */ + static const char *azEndings[] = { +#if SQLITE_OS_WIN + "dll" +#elif defined(__APPLE__) + "dylib" +#else + "so" +#endif + }; + if( pzErrMsg ) *pzErrMsg = 0; @@ -420,11 +448,17 @@ static int sqlite3LoadExtension( return SQLITE_ERROR; } - if( zProc==0 ){ - zProc = "sqlite3_extension_init"; - } + zEntry = zProc ? zProc : "sqlite3_extension_init"; handle = sqlite3OsDlOpen(pVfs, zFile); +#if SQLITE_OS_UNIX || SQLITE_OS_WIN + for(ii=0; ii sqlite3_example_init + ** C:/lib/mathfuncs.dll ==> sqlite3_mathfuncs_init + */ + if( xInit==0 && zProc==0 ){ + int iFile, iEntry, c; + int ncFile = sqlite3Strlen30(zFile); + zAltEntry = sqlite3_malloc(ncFile+30); + if( zAltEntry==0 ){ + sqlite3OsDlClose(pVfs, handle); + return SQLITE_NOMEM; + } + memcpy(zAltEntry, "sqlite3_", 8); + for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){} + iFile++; + if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3; + for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){ + if( sqlite3Isalpha(c) ){ + zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c]; + } + } + memcpy(zAltEntry+iEntry, "_init", 6); + zEntry = zAltEntry; + xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*)) + sqlite3OsDlSym(pVfs, handle, zEntry); + } if( xInit==0 ){ if( pzErrMsg ){ - nMsg += sqlite3Strlen30(zProc); + nMsg += sqlite3Strlen30(zEntry); *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg); if( zErrmsg ){ sqlite3_snprintf(nMsg, zErrmsg, - "no entry point [%s] in shared library [%s]", zProc,zFile); + "no entry point [%s] in shared library [%s]", zEntry, zFile); sqlite3OsDlError(pVfs, nMsg-1, zErrmsg); } - sqlite3OsDlClose(pVfs, handle); } + sqlite3OsDlClose(pVfs, handle); + sqlite3_free(zAltEntry); return SQLITE_ERROR; - }else if( xInit(db, &zErrmsg, &sqlite3Apis) ){ + } + sqlite3_free(zAltEntry); + if( xInit(db, &zErrmsg, &sqlite3Apis) ){ if( pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg); } diff --git a/src/main.c b/src/main.c index b52d474..39f6042 100644 --- a/src/main.c +++ b/src/main.c @@ -496,6 +496,19 @@ int sqlite3_config(int op, ...){ } #endif + case SQLITE_CONFIG_MMAP_SIZE: { + sqlite3_int64 szMmap = va_arg(ap, sqlite3_int64); + sqlite3_int64 mxMmap = va_arg(ap, sqlite3_int64); + if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ){ + mxMmap = SQLITE_MAX_MMAP_SIZE; + } + sqlite3GlobalConfig.mxMmap = mxMmap; + if( szMmap<0 ) szMmap = SQLITE_DEFAULT_MMAP_SIZE; + if( szMmap>mxMmap) szMmap = mxMmap; + sqlite3GlobalConfig.szMmap = szMmap; + break; + } + default: { rc = SQLITE_ERROR; break; @@ -885,10 +898,16 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ /* If we reach this point, it means that the database connection has ** closed all sqlite3_stmt and sqlite3_backup objects and has been - ** pased to sqlite3_close (meaning that it is a zombie). Therefore, + ** passed to sqlite3_close (meaning that it is a zombie). Therefore, ** go ahead and free all resources. */ + /* If a transaction is open, roll it back. This also ensures that if + ** any database schemas have been modified by an uncommitted transaction + ** they are reset. And that the required b-tree mutex is held to make + ** the pager rollback and schema reset an atomic operation. */ + sqlite3RollbackAll(db, SQLITE_OK); + /* Free any outstanding Savepoint structures. */ sqlite3CloseSavepoints(db); @@ -989,6 +1008,15 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){ int inTrans = 0; assert( sqlite3_mutex_held(db->mutex) ); sqlite3BeginBenignMalloc(); + + /* Obtain all b-tree mutexes before making any calls to BtreeRollback(). + ** This is important in case the transaction being rolled back has + ** modified the database schema. If the b-tree mutexes are not taken + ** here, then another shared-cache connection might sneak in between + ** the database rollback and schema reset, which can cause false + ** corruption reports in some cases. */ + sqlite3BtreeEnterAll(db); + for(i=0; inDb; i++){ Btree *p = db->aDb[i].pBt; if( p ){ @@ -1002,10 +1030,11 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){ sqlite3VtabRollback(db); sqlite3EndBenignMalloc(); - if( db->flags&SQLITE_InternChanges ){ + if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetAllSchemasOfConnection(db); } + sqlite3BtreeLeaveAll(db); /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; @@ -1016,6 +1045,110 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){ } } +/* +** Return a static string containing the name corresponding to the error code +** specified in the argument. +*/ +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ + defined(SQLITE_DEBUG_OS_TRACE) +const char *sqlite3ErrName(int rc){ + const char *zName = 0; + int i, origRc = rc; + for(i=0; i<2 && zName==0; i++, rc &= 0xff){ + switch( rc ){ + case SQLITE_OK: zName = "SQLITE_OK"; break; + case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; + case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break; + case SQLITE_PERM: zName = "SQLITE_PERM"; break; + case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; + case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break; + case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; + case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break; + case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; + case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break; + case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; + case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; + case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break; + case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break; + case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break; + case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; + case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; + case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break; + case SQLITE_IOERR_SHORT_READ: zName = "SQLITE_IOERR_SHORT_READ"; break; + case SQLITE_IOERR_WRITE: zName = "SQLITE_IOERR_WRITE"; break; + case SQLITE_IOERR_FSYNC: zName = "SQLITE_IOERR_FSYNC"; break; + case SQLITE_IOERR_DIR_FSYNC: zName = "SQLITE_IOERR_DIR_FSYNC"; break; + case SQLITE_IOERR_TRUNCATE: zName = "SQLITE_IOERR_TRUNCATE"; break; + case SQLITE_IOERR_FSTAT: zName = "SQLITE_IOERR_FSTAT"; break; + case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break; + case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break; + case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break; + case SQLITE_IOERR_BLOCKED: zName = "SQLITE_IOERR_BLOCKED"; break; + case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break; + case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break; + case SQLITE_IOERR_CHECKRESERVEDLOCK: + zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; + case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break; + case SQLITE_IOERR_CLOSE: zName = "SQLITE_IOERR_CLOSE"; break; + case SQLITE_IOERR_DIR_CLOSE: zName = "SQLITE_IOERR_DIR_CLOSE"; break; + case SQLITE_IOERR_SHMOPEN: zName = "SQLITE_IOERR_SHMOPEN"; break; + case SQLITE_IOERR_SHMSIZE: zName = "SQLITE_IOERR_SHMSIZE"; break; + case SQLITE_IOERR_SHMLOCK: zName = "SQLITE_IOERR_SHMLOCK"; break; + case SQLITE_IOERR_SHMMAP: zName = "SQLITE_IOERR_SHMMAP"; break; + case SQLITE_IOERR_SEEK: zName = "SQLITE_IOERR_SEEK"; break; + case SQLITE_IOERR_DELETE_NOENT: zName = "SQLITE_IOERR_DELETE_NOENT";break; + case SQLITE_IOERR_MMAP: zName = "SQLITE_IOERR_MMAP"; break; + case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; + case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break; + case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; + case SQLITE_FULL: zName = "SQLITE_FULL"; break; + case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; + case SQLITE_CANTOPEN_NOTEMPDIR: zName = "SQLITE_CANTOPEN_NOTEMPDIR";break; + case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break; + case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break; + case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; + case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; + case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; + case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break; + case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; + case SQLITE_CONSTRAINT_UNIQUE: zName = "SQLITE_CONSTRAINT_UNIQUE"; break; + case SQLITE_CONSTRAINT_TRIGGER: zName = "SQLITE_CONSTRAINT_TRIGGER";break; + case SQLITE_CONSTRAINT_FOREIGNKEY: + zName = "SQLITE_CONSTRAINT_FOREIGNKEY"; break; + case SQLITE_CONSTRAINT_CHECK: zName = "SQLITE_CONSTRAINT_CHECK"; break; + case SQLITE_CONSTRAINT_PRIMARYKEY: + zName = "SQLITE_CONSTRAINT_PRIMARYKEY"; break; + case SQLITE_CONSTRAINT_NOTNULL: zName = "SQLITE_CONSTRAINT_NOTNULL";break; + case SQLITE_CONSTRAINT_COMMITHOOK: + zName = "SQLITE_CONSTRAINT_COMMITHOOK"; break; + case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break; + case SQLITE_CONSTRAINT_FUNCTION: + zName = "SQLITE_CONSTRAINT_FUNCTION"; break; + case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; + case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; + case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; + case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; + case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; + case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; + case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break; + case SQLITE_ROW: zName = "SQLITE_ROW"; break; + case SQLITE_NOTICE: zName = "SQLITE_NOTICE"; break; + case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break; + case SQLITE_NOTICE_RECOVER_ROLLBACK: + zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break; + case SQLITE_WARNING: zName = "SQLITE_WARNING"; break; + case SQLITE_DONE: zName = "SQLITE_DONE"; break; + } + } + if( zName==0 ){ + static char zBuf[50]; + sqlite3_snprintf(sizeof(zBuf), zBuf, "SQLITE_UNKNOWN(%d)", origRc); + zName = zBuf; + } + return zName; +} +#endif + /* ** Return a static string that describes the kind of error specified in the ** argument. @@ -2316,6 +2449,7 @@ static int openDatabase( memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->autoCommit = 1; db->nextAutovac = -1; + db->szMmap = sqlite3GlobalConfig.szMmap; db->nextPagesize = 0; db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger #if SQLITE_DEFAULT_FILE_FORMAT<4 diff --git a/src/memjournal.c b/src/memjournal.c index 3e66e21..0572594 100644 --- a/src/memjournal.c +++ b/src/memjournal.c @@ -230,7 +230,9 @@ static const struct sqlite3_io_methods MemJournalMethods = { 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ - 0 /* xShmUnlock */ + 0, /* xShmUnmap */ + 0, /* xFetch */ + 0 /* xUnfetch */ }; /* diff --git a/src/os.c b/src/os.c index b5e918a..be2ea4c 100644 --- a/src/os.c +++ b/src/os.c @@ -141,6 +141,26 @@ int sqlite3OsShmMap( return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp); } +#if SQLITE_MAX_MMAP_SIZE>0 +/* The real implementation of xFetch and xUnfetch */ +int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){ + DO_OS_MALLOC_TEST(id); + return id->pMethods->xFetch(id, iOff, iAmt, pp); +} +int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){ + return id->pMethods->xUnfetch(id, iOff, p); +} +#else +/* No-op stubs to use when memory-mapped I/O is disabled */ +int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){ + *pp = 0; + return SQLITE_OK; +} +int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){ + return SQLITE_OK; +} +#endif + /* ** The next group of routines are convenience wrappers around the ** VFS methods. diff --git a/src/os.h b/src/os.h index 1ec7d4b..070a2dd 100644 --- a/src/os.h +++ b/src/os.h @@ -99,14 +99,6 @@ # define SQLITE_OS_WINRT 0 #endif -/* -** When compiled for WinCE or WinRT, there is no concept of the current -** directory. - */ -#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT -# define SQLITE_CURDIR 1 -#endif - /* If the SET_FULLSYNC macro is not defined above, then make it ** a no-op */ @@ -259,6 +251,8 @@ int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); int sqlite3OsShmLock(sqlite3_file *id, int, int, int); void sqlite3OsShmBarrier(sqlite3_file *id); int sqlite3OsShmUnmap(sqlite3_file *id, int); +int sqlite3OsFetch(sqlite3_file *id, i64, int, void **); +int sqlite3OsUnfetch(sqlite3_file *, i64, void *); /* diff --git a/src/os_unix.c b/src/os_unix.c index 315f150..abc23a4 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -126,7 +126,7 @@ #include #include #include -#ifndef SQLITE_OMIT_WAL +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 #include #endif @@ -225,6 +225,11 @@ struct unixFile { const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ + int nFetchOut; /* Number of outstanding xFetch refs */ + sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ + sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ + void *pMapRegion; /* Memory mapped region */ #ifdef __QNXNTO__ int sectorSize; /* Device sector size */ int deviceCharacteristics; /* Precomputed device characteristics */ @@ -249,7 +254,9 @@ struct unixFile { unsigned char transCntrChng; /* True if the transaction counter changed */ unsigned char dbUpdate; /* True if any part of database file changed */ unsigned char inNormalWrite; /* True if in a normal write operation */ + #endif + #ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that ** it is larger than the struct CrashFile defined in test6.c. @@ -273,6 +280,7 @@ struct unixFile { #define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */ #define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ +#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */ /* ** Include code that is common to all os_*.c files @@ -306,6 +314,17 @@ struct unixFile { #define threadid 0 #endif +/* +** HAVE_MREMAP defaults to true on Linux and false everywhere else. +*/ +#if !defined(HAVE_MREMAP) +# if defined(__linux__) && defined(_GNU_SOURCE) +# define HAVE_MREMAP 1 +# else +# define HAVE_MREMAP 0 +# endif +#endif + /* ** Different Unix systems declare open() in different ways. Same use ** open(const char*,int,mode_t). Others use open(const char*,int,...). @@ -337,7 +356,7 @@ static int openDirectory(const char*, int*); ** to all overrideable system calls. */ static struct unix_syscall { - const char *zName; /* Name of the sytem call */ + const char *zName; /* Name of the system call */ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ sqlite3_syscall_ptr pDefault; /* Default value */ } aSyscall[] = { @@ -412,11 +431,7 @@ static struct unix_syscall { #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ aSyscall[13].pCurrent) -#if SQLITE_ENABLE_LOCKING_STYLE { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, -#else - { "fchmod", (sqlite3_syscall_ptr)0, 0 }, -#endif #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE @@ -441,8 +456,18 @@ static struct unix_syscall { { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) - { "umask", (sqlite3_syscall_ptr)umask, 0 }, -#define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent) + { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, +#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent) + + { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, +#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent) + +#if HAVE_MREMAP + { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, +#else + { "mremap", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent) }; /* End of the overrideable system calls */ @@ -548,14 +573,7 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ */ static int robust_open(const char *z, int f, mode_t m){ int fd; - mode_t m2; - mode_t origM = 0; - if( m==0 ){ - m2 = SQLITE_DEFAULT_FILE_PERMISSIONS; - }else{ - m2 = m; - origM = osUmask(0); - } + mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; do{ #if defined(O_CLOEXEC) fd = osOpen(z,f|O_CLOEXEC,m2); @@ -563,12 +581,20 @@ static int robust_open(const char *z, int f, mode_t m){ fd = osOpen(z,f,m2); #endif }while( fd<0 && errno==EINTR ); - if( m ){ - osUmask(origM); - } + if( fd>=0 ){ + if( m!=0 ){ + struct stat statbuf; + if( osFstat(fd, &statbuf)==0 + && statbuf.st_size==0 + && (statbuf.st_mode&0777)!=m + ){ + osFchmod(fd, m); + } + } #if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) - if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif + } return fd; } @@ -774,7 +800,6 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { } - /****************************************************************************** ****************** Begin Unique File ID Utility Used By VxWorks *************** ** @@ -1110,7 +1135,6 @@ static int unixLogErrorAtLine( zErr = strerror(iErrno); #endif - assert( errcode!=SQLITE_OK ); if( zPath==0 ) zPath = ""; sqlite3_log(errcode, "os_unix.c:%d: (%d) %s(%s) - %s", @@ -1276,6 +1300,50 @@ static int findInodeInfo( } +/* +** Check a unixFile that is a database. Verify the following: +** +** (1) There is exactly one hard link on the file +** (2) The file is not a symbolic link +** (3) The file has not been renamed or unlinked +** +** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right. +*/ +static void verifyDbFile(unixFile *pFile){ + struct stat buf; + int rc; + if( pFile->ctrlFlags & UNIXFILE_WARNED ){ + /* One or more of the following warnings have already been issued. Do not + ** repeat them so as not to clutter the error log */ + return; + } + rc = osFstat(pFile->h, &buf); + if( rc!=0 ){ + sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } + if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){ + sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } + if( buf.st_nlink>1 ){ + sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } + if( pFile->pInode!=0 + && ((rc = osStat(pFile->zPath, &buf))!=0 + || buf.st_ino!=pFile->pInode->fileId.ino) + ){ + sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } +} + + /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut @@ -1806,9 +1874,13 @@ end_unlock: ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ + assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); return posixUnlock(id, eFileLock, 0); } +static int unixMapfile(unixFile *pFd, i64 nByte); +static void unixUnmapfile(unixFile *pFd); + /* ** This function performs the parts of the "close file" operation ** common to all locking schemes. It closes the directory and file @@ -1821,6 +1893,7 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; + unixUnmapfile(pFile); if( pFile->h>=0 ){ robust_close(pFile, pFile->h, __LINE__); pFile->h = -1; @@ -1847,6 +1920,7 @@ static int closeUnixFile(sqlite3_file *id){ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; unixFile *pFile = (unixFile *)id; + verifyDbFile(pFile); unixUnlock(id, NO_LOCK); unixEnterMutex(); @@ -1915,7 +1989,7 @@ static int nolockClose(sqlite3_file *id) { /****************************************************************************** ************************* Begin dot-file Locking ****************************** ** -** The dotfile locking implementation uses the existance of separate lock +** The dotfile locking implementation uses the existence of separate lock ** files (really a directory) to control access to the database. This works ** on just about every filesystem imaginable. But there are serious downsides: ** @@ -1930,7 +2004,7 @@ static int nolockClose(sqlite3_file *id) { ** ** Dotfile locking works by creating a subdirectory in the same directory as ** the database and with the same name but with a ".lock" extension added. -** The existance of a lock directory implies an EXCLUSIVE lock. All other +** The existence of a lock directory implies an EXCLUSIVE lock. All other ** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. */ @@ -3078,6 +3152,8 @@ static int unixRead( unixFile *pFile = (unixFile *)id; int got; assert( id ); + assert( offset>=0 ); + assert( amt>0 ); /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ @@ -3088,6 +3164,23 @@ static int unixRead( ); #endif +#if SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this read request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); + return SQLITE_OK; + }else{ + int nCopy = pFile->mmapSize - offset; + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } + } +#endif + got = seekAndRead(pFile, offset, pBuf, amt); if( got==amt ){ return SQLITE_OK; @@ -3102,6 +3195,51 @@ static int unixRead( } } +/* +** Attempt to seek the file-descriptor passed as the first argument to +** absolute offset iOff, then attempt to write nBuf bytes of data from +** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise, +** return the actual number of bytes written (which may be less than +** nBuf). +*/ +static int seekAndWriteFd( + int fd, /* File descriptor to write to */ + i64 iOff, /* File offset to begin writing at */ + const void *pBuf, /* Copy data from this buffer to the file */ + int nBuf, /* Size of buffer pBuf in bytes */ + int *piErrno /* OUT: Error number if error occurs */ +){ + int rc = 0; /* Value returned by system call */ + + assert( nBuf==(nBuf&0x1ffff) ); + nBuf &= 0x1ffff; + TIMER_START; + +#if defined(USE_PREAD) + do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); +#elif defined(USE_PREAD64) + do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); +#else + do{ + i64 iSeek = lseek(fd, iOff, SEEK_SET); + SimulateIOError( iSeek-- ); + + if( iSeek!=iOff ){ + if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0); + return -1; + } + rc = osWrite(fd, pBuf, nBuf); + }while( rc<0 && errno==EINTR ); +#endif + + TIMER_END; + OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED)); + + if( rc<0 && piErrno ) *piErrno = errno; + return rc; +} + + /* ** Seek to the offset in id->offset then read cnt bytes into pBuf. ** Return the number of bytes actually read. Update the offset. @@ -3110,39 +3248,7 @@ static int unixRead( ** is set before returning. */ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ - int got; -#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) - i64 newOffset; -#endif - assert( cnt==(cnt&0x1ffff) ); - cnt &= 0x1ffff; - TIMER_START; -#if defined(USE_PREAD) - do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); -#elif defined(USE_PREAD64) - do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); -#else - do{ - newOffset = lseek(id->h, offset, SEEK_SET); - SimulateIOError( newOffset-- ); - if( newOffset!=offset ){ - if( newOffset == -1 ){ - ((unixFile*)id)->lastErrno = errno; - }else{ - ((unixFile*)id)->lastErrno = 0; - } - return -1; - } - got = osWrite(id->h, pBuf, cnt); - }while( got<0 && errno==EINTR ); -#endif - TIMER_END; - if( got<0 ){ - ((unixFile*)id)->lastErrno = errno; - } - - OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); - return got; + return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno); } @@ -3192,6 +3298,23 @@ static int unixWrite( } #endif +#if SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this write request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); + return SQLITE_OK; + }else{ + int nCopy = pFile->mmapSize - offset; + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } + } +#endif + while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; offset += wrote; @@ -3419,7 +3542,7 @@ static int unixSync(sqlite3_file *id, int flags){ } /* Also fsync the directory containing the file if the DIRSYNC flag - ** is set. This is a one-time occurrance. Many systems (examples: AIX) + ** is set. This is a one-time occurrence. Many systems (examples: AIX) ** are unable to fsync a directory, so ignore errors on the fsync. */ if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){ @@ -3474,6 +3597,14 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ } #endif + /* If the file was just truncated to a size smaller than the currently + ** mapped region, reduce the effective mapping size as well. SQLite will + ** use read() and write() to access data beyond this point from now on. + */ + if( nBytemmapSize ){ + pFile->mmapSize = nByte; + } + return SQLITE_OK; } } @@ -3562,6 +3693,19 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ } } + if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ + int rc; + if( pFile->szChunk<=0 ){ + if( robust_ftruncate(pFile->h, nByte) ){ + pFile->lastErrno = errno; + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); + } + } + + rc = unixMapfile(pFile, nByte); + return rc; + } + return SQLITE_OK; } @@ -3629,6 +3773,18 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } + case SQLITE_FCNTL_MMAP_SIZE: { + i64 newLimit = *(i64*)pArg; + if( newLimit>sqlite3GlobalConfig.mxMmap ){ + newLimit = sqlite3GlobalConfig.mxMmap; + } + *(i64*)pArg = pFile->mmapSizeMax; + if( newLimit>=0 ){ + pFile->mmapSizeMax = newLimit; + if( newLimitmmapSize ) pFile->mmapSize = newLimit; + } + return SQLITE_OK; + } #ifdef SQLITE_DEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and @@ -3941,7 +4097,7 @@ static void unixShmPurge(unixFile *pFd){ sqlite3_mutex_free(p->mutex); for(i=0; inRegion; i++){ if( p->h>=0 ){ - munmap(p->apRegion[i], p->szRegion); + osMunmap(p->apRegion[i], p->szRegion); }else{ sqlite3_free(p->apRegion[i]); } @@ -4181,24 +4337,32 @@ static int unixShmMap( if( sStat.st_sizeh, sStat.st_size, nByte)!=0 ){ - rc = unixLogError(SQLITE_IOERR_SHMSIZE, "fallocate", - pShmNode->zFilename); + if( !bExtend ){ goto shmpage_out; } -#else - if( robust_ftruncate(pShmNode->h, nByte) ){ - rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", - pShmNode->zFilename); - goto shmpage_out; + + /* Alternatively, if bExtend is true, extend the file. Do this by + ** writing a single byte to the end of each (OS) page being + ** allocated or extended. Technically, we need only write to the + ** last page in order to extend the file. But writing to all new + ** pages forces the OS to allocate them immediately, which reduces + ** the chances of SIGBUS while accessing the mapped region later on. + */ + else{ + static const int pgsz = 4096; + int iPg; + + /* Write to the last byte of each newly allocated or extended page */ + assert( (nByte % pgsz)==0 ); + for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){ + if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=1 ){ + const char *zFile = pShmNode->zFilename; + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); + goto shmpage_out; + } + } } -#endif } } @@ -4214,7 +4378,7 @@ static int unixShmMap( while(pShmNode->nRegion<=iRegion){ void *pMem; if( pShmNode->h>=0 ){ - pMem = mmap(0, szRegion, + pMem = osMmap(0, szRegion, pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion ); @@ -4431,6 +4595,236 @@ static int unixShmUnmap( # define unixShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ +/* +** If it is currently memory mapped, unmap file pFd. +*/ +static void unixUnmapfile(unixFile *pFd){ + assert( pFd->nFetchOut==0 ); +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->pMapRegion ){ + osMunmap(pFd->pMapRegion, pFd->mmapSizeActual); + pFd->pMapRegion = 0; + pFd->mmapSize = 0; + pFd->mmapSizeActual = 0; + } +#endif +} + +#if SQLITE_MAX_MMAP_SIZE>0 +/* +** Return the system page size. +*/ +static int unixGetPagesize(void){ +#if HAVE_MREMAP + return 512; +#elif defined(_BSD_SOURCE) + return getpagesize(); +#else + return (int)sysconf(_SC_PAGESIZE); +#endif +} +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ + +#if SQLITE_MAX_MMAP_SIZE>0 +/* +** Attempt to set the size of the memory mapping maintained by file +** descriptor pFd to nNew bytes. Any existing mapping is discarded. +** +** If successful, this function sets the following variables: +** +** unixFile.pMapRegion +** unixFile.mmapSize +** unixFile.mmapSizeActual +** +** If unsuccessful, an error message is logged via sqlite3_log() and +** the three variables above are zeroed. In this case SQLite should +** continue accessing the database using the xRead() and xWrite() +** methods. +*/ +static void unixRemapfile( + unixFile *pFd, /* File descriptor object */ + i64 nNew /* Required mapping size */ +){ + const char *zErr = "mmap"; + int h = pFd->h; /* File descriptor open on db file */ + u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ + i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */ + u8 *pNew = 0; /* Location of new mapping */ + int flags = PROT_READ; /* Flags to pass to mmap() */ + + assert( pFd->nFetchOut==0 ); + assert( nNew>pFd->mmapSize ); + assert( nNew<=pFd->mmapSizeMax ); + assert( nNew>0 ); + assert( pFd->mmapSizeActual>=pFd->mmapSize ); + assert( MAP_FAILED!=0 ); + + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; + + if( pOrig ){ + const int szSyspage = unixGetPagesize(); + i64 nReuse = (pFd->mmapSize & ~(szSyspage-1)); + u8 *pReq = &pOrig[nReuse]; + + /* Unmap any pages of the existing mapping that cannot be reused. */ + if( nReuse!=nOrig ){ + osMunmap(pReq, nOrig-nReuse); + } + +#if HAVE_MREMAP + pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); + zErr = "mremap"; +#else + pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); + if( pNew!=MAP_FAILED ){ + if( pNew!=pReq ){ + osMunmap(pNew, nNew - nReuse); + pNew = 0; + }else{ + pNew = pOrig; + } + } +#endif + + /* The attempt to extend the existing mapping failed. Free it. */ + if( pNew==MAP_FAILED || pNew==0 ){ + osMunmap(pOrig, nReuse); + } + } + + /* If pNew is still NULL, try to create an entirely new mapping. */ + if( pNew==0 ){ + pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); + } + + if( pNew==MAP_FAILED ){ + pNew = 0; + nNew = 0; + unixLogError(SQLITE_OK, zErr, pFd->zPath); + + /* If the mmap() above failed, assume that all subsequent mmap() calls + ** will probably fail too. Fall back to using xRead/xWrite exclusively + ** in this case. */ + pFd->mmapSizeMax = 0; + } + pFd->pMapRegion = (void *)pNew; + pFd->mmapSize = pFd->mmapSizeActual = nNew; +} +#endif + +/* +** Memory map or remap the file opened by file-descriptor pFd (if the file +** is already mapped, the existing mapping is replaced by the new). Or, if +** there already exists a mapping for this file, and there are still +** outstanding xFetch() references to it, this function is a no-op. +** +** If parameter nByte is non-negative, then it is the requested size of +** the mapping to create. Otherwise, if nByte is less than zero, then the +** requested size is the size of the file on disk. The actual size of the +** created mapping is either the requested size or the value configured +** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller. +** +** SQLITE_OK is returned if no error occurs (even if the mapping is not +** recreated as a result of outstanding references) or an SQLite error +** code otherwise. +*/ +static int unixMapfile(unixFile *pFd, i64 nByte){ +#if SQLITE_MAX_MMAP_SIZE>0 + i64 nMap = nByte; + int rc; + + assert( nMap>=0 || pFd->nFetchOut==0 ); + if( pFd->nFetchOut>0 ) return SQLITE_OK; + + if( nMap<0 ){ + struct stat statbuf; /* Low-level file information */ + rc = osFstat(pFd->h, &statbuf); + if( rc!=SQLITE_OK ){ + return SQLITE_IOERR_FSTAT; + } + nMap = statbuf.st_size; + } + if( nMap>pFd->mmapSizeMax ){ + nMap = pFd->mmapSizeMax; + } + + if( nMap!=pFd->mmapSize ){ + if( nMap>0 ){ + unixRemapfile(pFd, nMap); + }else{ + unixUnmapfile(pFd); + } + } +#endif + + return SQLITE_OK; +} + +/* +** If possible, return a pointer to a mapping of file fd starting at offset +** iOff. The mapping must be valid for at least nAmt bytes. +** +** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. +** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. +** Finally, if an error does occur, return an SQLite error code. The final +** value of *pp is undefined in this case. +** +** If this function does return a pointer, the caller must eventually +** release the reference by calling unixUnfetch(). +*/ +static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ +#if SQLITE_MAX_MMAP_SIZE>0 + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ +#endif + *pp = 0; + +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ + if( pFd->pMapRegion==0 ){ + int rc = unixMapfile(pFd, -1); + if( rc!=SQLITE_OK ) return rc; + } + if( pFd->mmapSize >= iOff+nAmt ){ + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; + } + } +#endif + return SQLITE_OK; +} + +/* +** If the third argument is non-NULL, then this function releases a +** reference obtained by an earlier call to unixFetch(). The second +** argument passed to this function must be the same as the corresponding +** argument that was passed to the unixFetch() invocation. +** +** Or, if the third argument is NULL, then this function is being called +** to inform the VFS layer that, according to POSIX, any existing mapping +** may now be invalid and should be unmapped. +*/ +static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ + UNUSED_PARAMETER(iOff); + + /* If p==0 (unmap the entire file) then there must be no outstanding + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), + ** then there must be at least one outstanding. */ + assert( (p==0)==(pFd->nFetchOut==0) ); + + /* If p!=0, it must match the iOff value. */ + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); + + if( p ){ + pFd->nFetchOut--; + }else{ + unixUnmapfile(pFd); + } + + assert( pFd->nFetchOut>=0 ); + return SQLITE_OK; +} + /* ** Here ends the implementation of all sqlite3_file methods. ** @@ -4489,7 +4883,9 @@ static const sqlite3_io_methods METHOD = { \ unixShmMap, /* xShmMap */ \ unixShmLock, /* xShmLock */ \ unixShmBarrier, /* xShmBarrier */ \ - unixShmUnmap /* xShmUnmap */ \ + unixShmUnmap, /* xShmUnmap */ \ + unixFetch, /* xFetch */ \ + unixUnfetch, /* xUnfetch */ \ }; \ static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ @@ -4506,7 +4902,7 @@ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ IOMETHODS( posixIoFinder, /* Finder function name */ posixIoMethods, /* sqlite3_io_methods object name */ - 2, /* shared memory is enabled */ + 3, /* shared memory and mmap are enabled */ unixClose, /* xClose method */ unixLock, /* xLock method */ unixUnlock, /* xUnlock method */ @@ -4757,11 +5153,12 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; + pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap; if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; } - if( memcmp(pVfs->zName,"unix-excl",10)==0 ){ + if( strcmp(pVfs->zName,"unix-excl")==0 ){ pNew->ctrlFlags |= UNIXFILE_EXCL; } @@ -4793,7 +5190,7 @@ static int fillInUnixFile( unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ - /* If an error occured in findInodeInfo(), close the file descriptor + /* If an error occurred in findInodeInfo(), close the file descriptor ** immediately, before releasing the mutex. findInodeInfo() may fail ** in two scenarios: ** @@ -4892,15 +5289,15 @@ static int fillInUnixFile( if( h>=0 ) robust_close(pNew, h, __LINE__); h = -1; osUnlink(zFilename); - isDelete = 0; + pNew->ctrlFlags |= UNIXFILE_DELETE; } - if( isDelete ) pNew->ctrlFlags |= UNIXFILE_DELETE; #endif if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); + verifyDbFile(pNew); } return rc; } @@ -5431,7 +5828,7 @@ static int unixDelete( } /* -** Test the existance of or access permissions of file zPath. The +** Test the existence of or access permissions of file zPath. The ** test performed depends on the value of flags: ** ** SQLITE_ACCESS_EXISTS: Return 1 if the file exists @@ -6994,7 +7391,7 @@ int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==22 ); + assert( ArraySize(aSyscall)==24 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ diff --git a/src/os_win.c b/src/os_win.c index 107370c..aeb0881 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -150,11 +150,20 @@ struct winFile { winceLock local; /* Locks obtained by this instance of winFile */ winceLock *shared; /* Global shared lock memory for the file */ #endif +#if SQLITE_MAX_MMAP_SIZE>0 + int nFetchOut; /* Number of outstanding xFetch references */ + HANDLE hMap; /* Handle for accessing memory mapping */ + void *pMapRegion; /* Area memory mapped */ + sqlite3_int64 mmapSize; /* Usable size of mapped region */ + sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */ + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ +#endif }; /* ** Allowed values for winFile.ctrlFlags */ +#define WINFILE_RDONLY 0x02 /* Connection is read only */ #define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ #define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ @@ -308,7 +317,7 @@ static int sqlite3_os_type = 0; ** to all overrideable system calls. */ static struct win_syscall { - const char *zName; /* Name of the sytem call */ + const char *zName; /* Name of the system call */ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ sqlite3_syscall_ptr pDefault; /* Default value */ } aSyscall[] = { @@ -988,7 +997,7 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){ ** (if available). */ -void sqlite3_win32_write_debug(char *zBuf, int nBuf){ +void sqlite3_win32_write_debug(const char *zBuf, int nBuf){ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ @@ -1514,7 +1523,7 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ } #endif if( 0 == dwLen ){ - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", lastErrno, lastErrno); + sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno); }else{ /* copy a maximum of nBuf chars to output buffer */ sqlite3_snprintf(nBuf, zBuf, "%s", zOut); @@ -1557,7 +1566,7 @@ static int winLogErrorAtLine( for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} zMsg[i] = 0; sqlite3_log(errcode, - "os_win.c:%d: (%d) %s(%s) - %s", + "os_win.c:%d: (%lu) %s(%s) - %s", iLine, lastErrno, zFunc, zPath, zMsg ); @@ -1621,9 +1630,10 @@ static void logIoerr(int nRetry){ /************************************************************************* ** This section contains code for WinCE only. */ +#if !defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API /* -** Windows CE does not have a localtime() function. So create a -** substitute. +** The MSVC CRT on Windows CE may not have a localtime() function. So +** create a substitute. */ #include struct tm *__cdecl localtime(const time_t *t) @@ -1647,6 +1657,7 @@ struct tm *__cdecl localtime(const time_t *t) y.tm_sec = pTm.wSecond; return &y; } +#endif #define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] @@ -1668,15 +1679,17 @@ static void winceMutexAcquire(HANDLE h){ ** Create the mutex and shared memory used for locking in the file ** descriptor pFile */ -static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ +static int winceCreateLock(const char *zFilename, winFile *pFile){ LPWSTR zTok; LPWSTR zName; + DWORD lastErrno; + BOOL bLogged = FALSE; BOOL bInit = TRUE; zName = utf8ToUnicode(zFilename); if( zName==0 ){ /* out of memory */ - return FALSE; + return SQLITE_IOERR_NOMEM; } /* Initialize the local lockdata */ @@ -1693,9 +1706,10 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); if (!pFile->hMutex){ pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_ERROR, pFile->lastErrno, "winceCreateLock1", zFilename); + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock1", zFilename); sqlite3_free(zName); - return FALSE; + return SQLITE_IOERR; } /* Acquire the mutex before continuing */ @@ -1712,41 +1726,49 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ /* Set a flag that indicates we're the first to create the memory so it ** must be zero-initialized */ - if (osGetLastError() == ERROR_ALREADY_EXISTS){ + lastErrno = osGetLastError(); + if (lastErrno == ERROR_ALREADY_EXISTS){ bInit = FALSE; } sqlite3_free(zName); /* If we succeeded in making the shared memory handle, map it. */ - if (pFile->hShared){ + if( pFile->hShared ){ pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); /* If mapping failed, close the shared memory handle and erase it */ - if (!pFile->shared){ + if( !pFile->shared ){ pFile->lastErrno = osGetLastError(); - winLogError(SQLITE_ERROR, pFile->lastErrno, - "winceCreateLock2", zFilename); + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock2", zFilename); + bLogged = TRUE; osCloseHandle(pFile->hShared); pFile->hShared = NULL; } } /* If shared memory could not be created, then close the mutex and fail */ - if (pFile->hShared == NULL){ + if( pFile->hShared==NULL ){ + if( !bLogged ){ + pFile->lastErrno = lastErrno; + winLogError(SQLITE_IOERR, pFile->lastErrno, + "winceCreateLock3", zFilename); + bLogged = TRUE; + } winceMutexRelease(pFile->hMutex); osCloseHandle(pFile->hMutex); pFile->hMutex = NULL; - return FALSE; + return SQLITE_IOERR; } /* Initialize the shared memory if we're supposed to */ - if (bInit) { + if( bInit ){ memset(pFile->shared, 0, sizeof(winceLock)); } winceMutexRelease(pFile->hMutex); - return TRUE; + return SQLITE_OK; } /* @@ -1825,7 +1847,8 @@ static BOOL winceLockFile( } /* Want a pending lock? */ - else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE + && nNumberOfBytesToLockLow == 1){ /* If no pending lock has been acquired, then acquire it */ if (pFile->shared->bPending == 0) { pFile->shared->bPending = TRUE; @@ -1835,7 +1858,8 @@ static BOOL winceLockFile( } /* Want a reserved lock? */ - else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE + && nNumberOfBytesToLockLow == 1){ if (pFile->shared->bReserved == 0) { pFile->shared->bReserved = TRUE; pFile->local.bReserved = TRUE; @@ -1878,7 +1902,8 @@ static BOOL winceUnlockFile( /* Did we just have a reader lock? */ else if (pFile->local.nReaders){ - assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1); + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE + || nNumberOfBytesToUnlockLow == 1); pFile->local.nReaders --; if (pFile->local.nReaders == 0) { @@ -1889,7 +1914,8 @@ static BOOL winceUnlockFile( } /* Releasing a pending lock */ - else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE + && nNumberOfBytesToUnlockLow == 1){ if (pFile->local.bPending){ pFile->local.bPending = FALSE; pFile->shared->bPending = FALSE; @@ -1897,7 +1923,8 @@ static BOOL winceUnlockFile( } } /* Releasing a reserved lock */ - else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE + && nNumberOfBytesToUnlockLow == 1){ if (pFile->local.bReserved) { pFile->local.bReserved = FALSE; pFile->shared->bReserved = FALSE; @@ -2000,6 +2027,8 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ DWORD dwRet; /* Value returned by SetFilePointer() */ DWORD lastErrno; /* Value returned by GetLastError() */ + OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); + upperBits = (LONG)((iOffset>>32) & 0x7fffffff); lowerBits = (LONG)(iOffset & 0xffffffff); @@ -2007,7 +2036,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ ** containing the lower 32-bits of the new file-offset. Or, if it fails, ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine - ** whether an error has actually occured, it is also necessary to call + ** whether an error has actually occurred, it is also necessary to call ** GetLastError(). */ dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); @@ -2017,9 +2046,11 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ pFile->lastErrno = lastErrno; winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, "seekWinFile", pFile->zPath); + OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); return 1; } + OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); return 0; #else /* @@ -2036,13 +2067,20 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ pFile->lastErrno = osGetLastError(); winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, "seekWinFile", pFile->zPath); + OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); return 1; } + OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); return 0; #endif } +#if SQLITE_MAX_MMAP_SIZE>0 +/* Forward references to VFS methods */ +static int winUnmapfile(winFile*); +#endif + /* ** Close a file. ** @@ -2062,7 +2100,14 @@ static int winClose(sqlite3_file *id){ #ifndef SQLITE_OMIT_WAL assert( pFile->pShm==0 ); #endif - OSTRACE(("CLOSE %d\n", pFile->h)); + assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); + OSTRACE(("CLOSE file=%p\n", pFile->h)); + +#if SQLITE_MAX_MMAP_SIZE>0 + rc = winUnmapfile(pFile); + if( rc!=SQLITE_OK ) return rc; +#endif + do{ rc = osCloseHandle(pFile->h); /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ @@ -2082,11 +2127,11 @@ static int winClose(sqlite3_file *id){ sqlite3_free(pFile->zDeleteOnClose); } #endif - OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); if( rc ){ pFile->h = NULL; } OpenCounter(-1); + OSTRACE(("CLOSE file=%p, rc=%s\n", pFile->h, rc ? "ok" : "failed")); return rc ? SQLITE_OK : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), "winClose", pFile->zPath); @@ -2111,11 +2156,33 @@ static int winRead( int nRetry = 0; /* Number of retrys */ assert( id!=0 ); + assert( amt>0 ); + assert( offset>=0 ); SimulateIOError(return SQLITE_IOERR_READ); - OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); + OSTRACE(("READ file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n", + pFile->h, pBuf, amt, offset, pFile->locktype)); + +#if SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this read request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); + OSTRACE(("READ-MMAP file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + }else{ + int nCopy = (int)(pFile->mmapSize - offset); + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } + } +#endif #if SQLITE_OS_WINCE if( seekWinFile(pFile, offset) ){ + OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); return SQLITE_FULL; } while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ @@ -2129,6 +2196,7 @@ static int winRead( DWORD lastErrno; if( retryIoerr(&nRetry, &lastErrno) ) continue; pFile->lastErrno = lastErrno; + OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h)); return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, "winRead", pFile->zPath); } @@ -2136,9 +2204,11 @@ static int winRead( if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[nRead], 0, amt-nRead); + OSTRACE(("READ file=%p, rc=SQLITE_IOERR_SHORT_READ\n", pFile->h)); return SQLITE_IOERR_SHORT_READ; } + OSTRACE(("READ file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } @@ -2152,7 +2222,7 @@ static int winWrite( int amt, /* Number of bytes to write */ sqlite3_int64 offset /* Offset into the file to begin writing at */ ){ - int rc = 0; /* True if error has occured, else false */ + int rc = 0; /* True if error has occurred, else false */ winFile *pFile = (winFile*)id; /* File handle */ int nRetry = 0; /* Number of retries */ @@ -2161,7 +2231,26 @@ static int winWrite( SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); - OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); + OSTRACE(("WRITE file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n", + pFile->h, pBuf, amt, offset, pFile->locktype)); + +#if SQLITE_MAX_MMAP_SIZE>0 + /* Deal with as much of this write request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); + OSTRACE(("WRITE-MMAP file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + }else{ + int nCopy = (int)(pFile->mmapSize - offset); + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } + } +#endif #if SQLITE_OS_WINCE rc = seekWinFile(pFile, offset); @@ -2214,13 +2303,16 @@ static int winWrite( if( rc ){ if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) || ( pFile->lastErrno==ERROR_DISK_FULL )){ + OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h)); return SQLITE_FULL; } + OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h)); return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, "winWrite", pFile->zPath); }else{ logIoerr(nRetry); } + OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } @@ -2230,11 +2322,12 @@ static int winWrite( static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ winFile *pFile = (winFile*)id; /* File handle object */ int rc = SQLITE_OK; /* Return code for this function */ + DWORD lastErrno; assert( pFile ); - - OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte)); SimulateIOError(return SQLITE_IOERR_TRUNCATE); + OSTRACE(("TRUNCATE file=%p, size=%lld, lock=%d\n", + pFile->h, nByte, pFile->locktype)); /* If the user has configured a chunk-size for this file, truncate the ** file so that it consists of an integer number of chunks (i.e. the @@ -2248,14 +2341,25 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ if( seekWinFile(pFile, nByte) ){ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, - "winTruncate1", pFile->zPath); - }else if( 0==osSetEndOfFile(pFile->h) ){ - pFile->lastErrno = osGetLastError(); + "winTruncate1", pFile->zPath); + }else if( 0==osSetEndOfFile(pFile->h) && + ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){ + pFile->lastErrno = lastErrno; rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, - "winTruncate2", pFile->zPath); + "winTruncate2", pFile->zPath); } - OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); +#if SQLITE_MAX_MMAP_SIZE>0 + /* If the file was truncated to a size smaller than the currently + ** mapped region, reduce the effective mapping size as well. SQLite will + ** use read() and write() to access data beyond this point from now on. + */ + if( pFile->pMapRegion && nBytemmapSize ){ + pFile->mmapSize = nByte; + } +#endif + + OSTRACE(("TRUNCATE file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); return rc; } @@ -2295,13 +2399,14 @@ static int winSync(sqlite3_file *id, int flags){ || (flags&0x0F)==SQLITE_SYNC_FULL ); - OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype)); - /* Unix cannot, but some systems may return SQLITE_FULL from here. This ** line is to test that doing so does not cause any problems. */ SimulateDiskfullError( return SQLITE_FULL ); + OSTRACE(("SYNC file=%p, flags=%x, lock=%d\n", + pFile->h, flags, pFile->locktype)); + #ifndef SQLITE_TEST UNUSED_PARAMETER(flags); #else @@ -2320,9 +2425,11 @@ static int winSync(sqlite3_file *id, int flags){ rc = osFlushFileBuffers(pFile->h); SimulateIOError( rc=FALSE ); if( rc ){ + OSTRACE(("SYNC file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; }else{ pFile->lastErrno = osGetLastError(); + OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h)); return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, "winSync", pFile->zPath); } @@ -2337,7 +2444,10 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ int rc = SQLITE_OK; assert( id!=0 ); + assert( pSize!=0 ); SimulateIOError(return SQLITE_IOERR_FSTAT); + OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize)); + #if SQLITE_OS_WINRT { FILE_STANDARD_INFO info; @@ -2366,6 +2476,8 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ } } #endif + OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n", + pFile->h, pSize, *pSize, sqlite3ErrName(rc))); return rc; } @@ -2407,6 +2519,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ */ static int getReadLock(winFile *pFile){ int res; + OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); if( isNT() ){ #if SQLITE_OS_WINCE /* @@ -2432,6 +2545,7 @@ static int getReadLock(winFile *pFile){ pFile->lastErrno = osGetLastError(); /* No need to log a failure to lock */ } + OSTRACE(("READ-LOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res))); return res; } @@ -2441,6 +2555,7 @@ static int getReadLock(winFile *pFile){ static int unlockReadLock(winFile *pFile){ int res; DWORD lastErrno; + OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); if( isNT() ){ res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); } @@ -2454,6 +2569,7 @@ static int unlockReadLock(winFile *pFile){ winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, "unlockReadLock", pFile->zPath); } + OSTRACE(("READ-UNLOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res))); return res; } @@ -2492,14 +2608,15 @@ static int winLock(sqlite3_file *id, int locktype){ DWORD lastErrno = NO_ERROR; assert( id!=0 ); - OSTRACE(("LOCK %d %d was %d(%d)\n", - pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); + OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n", + pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); /* If there is already a lock of this type or more restrictive on the ** OsFile, do nothing. Don't use the end_lock: exit path, as ** sqlite3OsEnterMutex() hasn't been called yet. */ if( pFile->locktype>=locktype ){ + OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } @@ -2527,7 +2644,8 @@ static int winLock(sqlite3_file *id, int locktype){ ** If you are using this code as a model for alternative VFSes, do not ** copy this retry logic. It is a hack intended for Windows only. */ - OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt)); + OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, rc=%s\n", + pFile->h, cnt, sqlite3ErrName(res))); if( cnt ) sqlite3_win32_sleep(1); } gotPendingLock = res; @@ -2572,14 +2690,12 @@ static int winLock(sqlite3_file *id, int locktype){ if( locktype==EXCLUSIVE_LOCK && res ){ assert( pFile->locktype>=SHARED_LOCK ); res = unlockReadLock(pFile); - OSTRACE(("unreadlock = %d\n", res)); res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ newLocktype = EXCLUSIVE_LOCK; }else{ lastErrno = osGetLastError(); - OSTRACE(("error-code = %d\n", lastErrno)); getReadLock(pFile); } } @@ -2597,12 +2713,14 @@ static int winLock(sqlite3_file *id, int locktype){ if( res ){ rc = SQLITE_OK; }else{ - OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h, - locktype, newLocktype)); + OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n", + pFile->h, locktype, newLocktype)); pFile->lastErrno = lastErrno; rc = SQLITE_BUSY; } pFile->locktype = (u8)newLocktype; + OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n", + pFile->h, pFile->locktype, sqlite3ErrName(rc))); return rc; } @@ -2616,20 +2734,23 @@ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ winFile *pFile = (winFile*)id; SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut)); assert( id!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; - OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc)); + OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (local)\n", pFile->h, rc)); }else{ - rc = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0); + rc = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0); if( rc ){ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); } rc = !rc; - OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc)); + OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (remote)\n", pFile->h, rc)); } *pResOut = rc; + OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", + pFile->h, pResOut, *pResOut)); return SQLITE_OK; } @@ -2650,8 +2771,8 @@ static int winUnlock(sqlite3_file *id, int locktype){ int rc = SQLITE_OK; assert( pFile!=0 ); assert( locktype<=SHARED_LOCK ); - OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, - pFile->locktype, pFile->sharedLockByte)); + OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", + pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); @@ -2672,6 +2793,8 @@ static int winUnlock(sqlite3_file *id, int locktype){ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); } pFile->locktype = (u8)locktype; + OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n", + pFile->h, pFile->locktype, sqlite3ErrName(rc))); return rc; } @@ -2699,17 +2822,21 @@ static int getTempname(int nBuf, char *zBuf); */ static int winFileControl(sqlite3_file *id, int op, void *pArg){ winFile *pFile = (winFile*)id; + OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg)); switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = pFile->locktype; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_LAST_ERRNO: { *(int*)pArg = (int)pFile->lastErrno; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_CHUNK_SIZE: { pFile->szChunk = *(int *)pArg; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { @@ -2724,20 +2851,25 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ SimulateIOErrorBenign(0); } } + OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); return rc; } + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_PERSIST_WAL: { winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg); + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { winModeBit(pFile, WINFILE_PSOW, (int*)pArg); + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_VFSNAME: { *(char**)pArg = sqlite3_mprintf("win32"); + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_WIN32_AV_RETRY: { @@ -2752,17 +2884,32 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ }else{ a[1] = win32IoerrRetryDelay; } + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname ); + char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname ); if( zTFile ){ getTempname(pFile->pVfs->mxPathname, zTFile); *(char**)pArg = zTFile; } + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } +#if SQLITE_MAX_MMAP_SIZE>0 + case SQLITE_FCNTL_MMAP_SIZE: { + i64 newLimit = *(i64*)pArg; + if( newLimit>sqlite3GlobalConfig.mxMmap ){ + newLimit = sqlite3GlobalConfig.mxMmap; + } + *(i64*)pArg = pFile->mmapSizeMax; + if( newLimit>=0 ) pFile->mmapSizeMax = newLimit; + OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); + return SQLITE_OK; + } +#endif } + OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); return SQLITE_NOTFOUND; } @@ -2790,8 +2937,6 @@ static int winDeviceCharacteristics(sqlite3_file *id){ ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); } -#ifndef SQLITE_OMIT_WAL - /* ** Windows will only let you create file view mappings ** on allocation size granularity boundaries. @@ -2800,6 +2945,8 @@ static int winDeviceCharacteristics(sqlite3_file *id){ */ SYSTEM_INFO winSysInfo; +#ifndef SQLITE_OMIT_WAL + /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the winLockInfo objects used by @@ -2923,6 +3070,9 @@ static int winShmSystemLock( /* Access to the winShmNode object is serialized by the caller */ assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); + OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", + pFile->hFile.h, lockType, ofst, nByte)); + /* Release/Acquire the system-level lock */ if( lockType==_SHM_UNLCK ){ rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); @@ -2940,11 +3090,9 @@ static int winShmSystemLock( rc = SQLITE_BUSY; } - OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", - pFile->hFile.h, - rc==SQLITE_OK ? "ok" : "failed", - lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", - pFile->lastErrno)); + OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", + pFile->hFile.h, (lockType == _SHM_UNLCK) ? "winUnlockFile" : + "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); return rc; } @@ -2964,6 +3112,8 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ winShmNode *p; BOOL bRc; assert( winShmMutexHeld() ); + OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n", + osGetCurrentProcessId(), deleteFlag)); pp = &winShmNodeList; while( (p = *pp)!=0 ){ if( p->nRef==0 ){ @@ -2971,15 +3121,13 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; inRegion; i++){ bRc = osUnmapViewOfFile(p->aRegion[i].pMap); - OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", - (int)osGetCurrentProcessId(), i, - bRc ? "ok" : "failed")); + OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n", + osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); bRc = osCloseHandle(p->aRegion[i].hMap); - OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", - (int)osGetCurrentProcessId(), i, - bRc ? "ok" : "failed")); + OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", + osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); } - if( p->hFile.h != INVALID_HANDLE_VALUE ){ + if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ SimulateIOErrorBenign(1); winClose((sqlite3_file *)&p->hFile); SimulateIOErrorBenign(0); @@ -3059,7 +3207,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ - SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */ + SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); if( SQLITE_OK!=rc ){ goto shm_open_err; @@ -3256,9 +3404,9 @@ static int winShmLock( } } sqlite3_mutex_leave(pShmNode->mutex); - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", - p->id, (int)osGetCurrentProcessId(), p->sharedMask, p->exclMask, - rc ? "failed" : "ok")); + OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", + osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, + sqlite3ErrName(rc))); return rc; } @@ -3379,8 +3527,8 @@ static int winShmMap( NULL, PAGE_READWRITE, 0, nByte, NULL ); #endif - OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", - (int)osGetCurrentProcessId(), pShmNode->nRegion, nByte, + OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", + osGetCurrentProcessId(), pShmNode->nRegion, nByte, hMap ? "ok" : "failed")); if( hMap ){ int iOffset = pShmNode->nRegion*szRegion; @@ -3394,8 +3542,8 @@ static int winShmMap( 0, iOffset - iOffsetShift, szRegion + iOffsetShift ); #endif - OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", - (int)osGetCurrentProcessId(), pShmNode->nRegion, iOffset, + OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n", + osGetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, pMap ? "ok" : "failed")); } if( !pMap ){ @@ -3432,6 +3580,230 @@ shmpage_out: # define winShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ +/* +** Cleans up the mapped region of the specified file, if any. +*/ +#if SQLITE_MAX_MMAP_SIZE>0 +static int winUnmapfile(winFile *pFile){ + assert( pFile!=0 ); + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, " + "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n", + osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion, + pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax)); + if( pFile->pMapRegion ){ + if( !osUnmapViewOfFile(pFile->pMapRegion) ){ + pFile->lastErrno = osGetLastError(); + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, " + "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, + pFile->pMapRegion)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winUnmap1", pFile->zPath); + } + pFile->pMapRegion = 0; + pFile->mmapSize = 0; + pFile->mmapSizeActual = 0; + } + if( pFile->hMap!=NULL ){ + if( !osCloseHandle(pFile->hMap) ){ + pFile->lastErrno = osGetLastError(); + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n", + osGetCurrentProcessId(), pFile, pFile->hMap)); + return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, + "winUnmap2", pFile->zPath); + } + pFile->hMap = NULL; + } + OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFile)); + return SQLITE_OK; +} + +/* +** Memory map or remap the file opened by file-descriptor pFd (if the file +** is already mapped, the existing mapping is replaced by the new). Or, if +** there already exists a mapping for this file, and there are still +** outstanding xFetch() references to it, this function is a no-op. +** +** If parameter nByte is non-negative, then it is the requested size of +** the mapping to create. Otherwise, if nByte is less than zero, then the +** requested size is the size of the file on disk. The actual size of the +** created mapping is either the requested size or the value configured +** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller. +** +** SQLITE_OK is returned if no error occurs (even if the mapping is not +** recreated as a result of outstanding references) or an SQLite error +** code otherwise. +*/ +static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ + sqlite3_int64 nMap = nByte; + int rc; + + assert( nMap>=0 || pFd->nFetchOut==0 ); + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n", + osGetCurrentProcessId(), pFd, nByte)); + + if( pFd->nFetchOut>0 ) return SQLITE_OK; + + if( nMap<0 ){ + rc = winFileSize((sqlite3_file*)pFd, &nMap); + if( rc ){ + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n", + osGetCurrentProcessId(), pFd)); + return SQLITE_IOERR_FSTAT; + } + } + if( nMap>pFd->mmapSizeMax ){ + nMap = pFd->mmapSizeMax; + } + nMap &= ~(sqlite3_int64)(winSysInfo.dwPageSize - 1); + + if( nMap==0 && pFd->mmapSize>0 ){ + winUnmapfile(pFd); + } + if( nMap!=pFd->mmapSize ){ + void *pNew = 0; + DWORD protect = PAGE_READONLY; + DWORD flags = FILE_MAP_READ; + + winUnmapfile(pFd); + if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){ + protect = PAGE_READWRITE; + flags |= FILE_MAP_WRITE; + } +#if SQLITE_OS_WINRT + pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL); +#elif defined(SQLITE_WIN32_HAS_WIDE) + pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect, + (DWORD)((nMap>>32) & 0xffffffff), + (DWORD)(nMap & 0xffffffff), NULL); +#elif defined(SQLITE_WIN32_HAS_ANSI) + pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect, + (DWORD)((nMap>>32) & 0xffffffff), + (DWORD)(nMap & 0xffffffff), NULL); +#endif + if( pFd->hMap==NULL ){ + pFd->lastErrno = osGetLastError(); + rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, + "winMapfile", pFd->zPath); + /* Log the error, but continue normal operation using xRead/xWrite */ + OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n", + osGetCurrentProcessId(), pFd)); + return SQLITE_OK; + } + assert( (nMap % winSysInfo.dwPageSize)==0 ); +#if SQLITE_OS_WINRT + pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, nMap); +#else + assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff ); + pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap); +#endif + if( pNew==NULL ){ + osCloseHandle(pFd->hMap); + pFd->hMap = NULL; + pFd->lastErrno = osGetLastError(); + winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, + "winMapfile", pFd->zPath); + OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n", + osGetCurrentProcessId(), pFd)); + return SQLITE_OK; + } + pFd->pMapRegion = pNew; + pFd->mmapSize = nMap; + pFd->mmapSizeActual = nMap; + } + + OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), pFd)); + return SQLITE_OK; +} +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ + +/* +** If possible, return a pointer to a mapping of file fd starting at offset +** iOff. The mapping must be valid for at least nAmt bytes. +** +** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. +** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. +** Finally, if an error does occur, return an SQLite error code. The final +** value of *pp is undefined in this case. +** +** If this function does return a pointer, the caller must eventually +** release the reference by calling winUnfetch(). +*/ +static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ +#if SQLITE_MAX_MMAP_SIZE>0 + winFile *pFd = (winFile*)fd; /* The underlying database file */ +#endif + *pp = 0; + + OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", + osGetCurrentProcessId(), fd, iOff, nAmt, pp)); + +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ + if( pFd->pMapRegion==0 ){ + int rc = winMapfile(pFd, -1); + if( rc!=SQLITE_OK ){ + OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", + osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); + return rc; + } + } + if( pFd->mmapSize >= iOff+nAmt ){ + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; + } + } +#endif + + OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), fd, pp, *pp)); + return SQLITE_OK; +} + +/* +** If the third argument is non-NULL, then this function releases a +** reference obtained by an earlier call to winFetch(). The second +** argument passed to this function must be the same as the corresponding +** argument that was passed to the winFetch() invocation. +** +** Or, if the third argument is NULL, then this function is being called +** to inform the VFS layer that, according to POSIX, any existing mapping +** may now be invalid and should be unmapped. +*/ +static int winUnfetch(sqlite3_file *fd, i64 iOff, void *p){ +#if SQLITE_MAX_MMAP_SIZE>0 + winFile *pFd = (winFile*)fd; /* The underlying database file */ + + /* If p==0 (unmap the entire file) then there must be no outstanding + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), + ** then there must be at least one outstanding. */ + assert( (p==0)==(pFd->nFetchOut==0) ); + + /* If p!=0, it must match the iOff value. */ + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); + + OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n", + osGetCurrentProcessId(), pFd, iOff, p)); + + if( p ){ + pFd->nFetchOut--; + }else{ + /* FIXME: If Windows truly always prevents truncating or deleting a + ** file while a mapping is held, then the following winUnmapfile() call + ** is unnecessary can can be omitted - potentially improving + ** performance. */ + winUnmapfile(pFd); + } + + assert( pFd->nFetchOut>=0 ); +#endif + + OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n", + osGetCurrentProcessId(), fd)); + return SQLITE_OK; +} + /* ** Here ends the implementation of all sqlite3_file methods. ** @@ -3443,7 +3815,7 @@ shmpage_out: ** sqlite3_file for win32. */ static const sqlite3_io_methods winIoMethod = { - 2, /* iVersion */ + 3, /* iVersion */ winClose, /* xClose */ winRead, /* xRead */ winWrite, /* xWrite */ @@ -3459,7 +3831,9 @@ static const sqlite3_io_methods winIoMethod = { winShmMap, /* xShmMap */ winShmLock, /* xShmLock */ winShmBarrier, /* xShmBarrier */ - winShmUnmap /* xShmUnmap */ + winShmUnmap, /* xShmUnmap */ + winFetch, /* xFetch */ + winUnfetch /* xUnfetch */ }; /**************************************************************************** @@ -3523,6 +3897,7 @@ static int getTempname(int nBuf, char *zBuf){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti); sqlite3_free(zMulti); }else{ + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; } } @@ -3536,6 +3911,7 @@ static int getTempname(int nBuf, char *zBuf){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); sqlite3_free(zUtf8); }else{ + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; } } @@ -3548,6 +3924,7 @@ static int getTempname(int nBuf, char *zBuf){ nTempPath = sqlite3Strlen30(zTempPath); if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){ + OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); return SQLITE_ERROR; } @@ -3565,8 +3942,8 @@ static int getTempname(int nBuf, char *zBuf){ zBuf[j] = 0; zBuf[j+1] = 0; - OSTRACE(("TEMP FILENAME: %s\n", zBuf)); - return SQLITE_OK; + OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf)); + return SQLITE_OK; } /* @@ -3635,9 +4012,7 @@ static int winOpen( int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); -#ifndef NDEBUG int isReadonly = (flags & SQLITE_OPEN_READONLY); -#endif int isReadWrite = (flags & SQLITE_OPEN_READWRITE); #ifndef NDEBUG @@ -3648,6 +4023,9 @@ static int winOpen( )); #endif + OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n", + zUtf8Name, id, flags, pOutFlags)); + /* Check the following statements are true: ** ** (a) Exactly one of the READWRITE and READONLY flags must be set, and @@ -3674,8 +4052,9 @@ static int winOpen( || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); - assert( id!=0 ); - UNUSED_PARAMETER(pVfs); + assert( pFile!=0 ); + memset(pFile, 0, sizeof(winFile)); + pFile->h = INVALID_HANDLE_VALUE; #if SQLITE_OS_WINRT if( !sqlite3_temp_directory ){ @@ -3684,15 +4063,15 @@ static int winOpen( } #endif - pFile->h = INVALID_HANDLE_VALUE; - /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ assert(isDelete && !isOpenJournal); + memset(zTmpname, 0, MAX_PATH+2); rc = getTempname(MAX_PATH+2, zTmpname); if( rc!=SQLITE_OK ){ + OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc))); return rc; } zUtf8Name = zTmpname; @@ -3708,11 +4087,13 @@ static int winOpen( /* Convert the filename to the system encoding. */ zConverted = convertUtf8Filename(zUtf8Name); if( zConverted==0 ){ + OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); return SQLITE_IOERR_NOMEM; } if( winIsDir(zConverted) ){ sqlite3_free(zConverted); + OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name)); return SQLITE_CANTOPEN_ISDIR; } @@ -3803,9 +4184,8 @@ static int winOpen( #endif logIoerr(cnt); - OSTRACE(("OPEN %d %s 0x%lx %s\n", - h, zName, dwDesiredAccess, - h==INVALID_HANDLE_VALUE ? "failed" : "ok")); + OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, + dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = lastErrno; @@ -3813,7 +4193,9 @@ static int winOpen( sqlite3_free(zConverted); if( isReadWrite && !isExclusive ){ return winOpen(pVfs, zName, id, - ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags); + ((flags|SQLITE_OPEN_READONLY) & + ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), + pOutFlags); }else{ return SQLITE_CANTOPEN_BKPT; } @@ -3827,26 +4209,18 @@ static int winOpen( } } - memset(pFile, 0, sizeof(*pFile)); - pFile->pMethod = &winIoMethod; - pFile->h = h; - pFile->lastErrno = NO_ERROR; - pFile->pVfs = pVfs; -#ifndef SQLITE_OMIT_WAL - pFile->pShm = 0; -#endif - pFile->zPath = zName; - if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ - pFile->ctrlFlags |= WINFILE_PSOW; - } + OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, " + "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ? + *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); #if SQLITE_OS_WINCE if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB - && !winceCreateLock(zName, pFile) + && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK ){ osCloseHandle(h); sqlite3_free(zConverted); - return SQLITE_CANTOPEN_BKPT; + OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc))); + return rc; } if( isTemp ){ pFile->zDeleteOnClose = zConverted; @@ -3856,6 +4230,25 @@ static int winOpen( sqlite3_free(zConverted); } + pFile->pMethod = &winIoMethod; + pFile->pVfs = pVfs; + pFile->h = h; + if( isReadonly ){ + pFile->ctrlFlags |= WINFILE_RDONLY; + } + if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ + pFile->ctrlFlags |= WINFILE_PSOW; + } + pFile->lastErrno = NO_ERROR; + pFile->zPath = zName; +#if SQLITE_MAX_MMAP_SIZE>0 + pFile->hMap = NULL; + pFile->pMapRegion = 0; + pFile->mmapSize = 0; + pFile->mmapSizeActual = 0; + pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap; +#endif + OpenCounter(+1); return rc; } @@ -3886,6 +4279,8 @@ static int winDelete( UNUSED_PARAMETER(syncDir); SimulateIOError(return SQLITE_IOERR_DELETE); + OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir)); + zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ return SQLITE_IOERR_NOMEM; @@ -3900,7 +4295,8 @@ static int winDelete( attr = sAttrData.dwFileAttributes; }else{ lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND || lastErrno==ERROR_PATH_NOT_FOUND ){ + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ }else{ rc = SQLITE_ERROR; @@ -3912,7 +4308,8 @@ static int winDelete( #endif if ( attr==INVALID_FILE_ATTRIBUTES ){ lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND || lastErrno==ERROR_PATH_NOT_FOUND ){ + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ }else{ rc = SQLITE_ERROR; @@ -3939,7 +4336,8 @@ static int winDelete( attr = osGetFileAttributesA(zConverted); if ( attr==INVALID_FILE_ATTRIBUTES ){ lastErrno = osGetLastError(); - if( lastErrno==ERROR_FILE_NOT_FOUND || lastErrno==ERROR_PATH_NOT_FOUND ){ + if( lastErrno==ERROR_FILE_NOT_FOUND + || lastErrno==ERROR_PATH_NOT_FOUND ){ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ }else{ rc = SQLITE_ERROR; @@ -3968,12 +4366,12 @@ static int winDelete( logIoerr(cnt); } sqlite3_free(zConverted); - OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" ))); + OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc))); return rc; } /* -** Check the existance and status of a file. +** Check the existence and status of a file. */ static int winAccess( sqlite3_vfs *pVfs, /* Not used on win32 */ @@ -3988,8 +4386,12 @@ static int winAccess( UNUSED_PARAMETER(pVfs); SimulateIOError( return SQLITE_IOERR_ACCESS; ); + OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", + zFilename, flags, pResOut)); + zConverted = convertUtf8Filename(zFilename); if( zConverted==0 ){ + OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM; } if( isNT() ){ @@ -4040,6 +4442,8 @@ static int winAccess( assert(!"Invalid flags argument"); } *pResOut = rc; + OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", + zFilename, pResOut, *pResOut)); return SQLITE_OK; } @@ -4107,16 +4511,12 @@ static int winFullPathname( */ char zOut[MAX_PATH+1]; memset(zOut, 0, MAX_PATH+1); - cygwin_conv_to_win32_path(zRelative, zOut); /* POSIX to Win32 */ + cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut, + MAX_PATH+1); sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", sqlite3_data_directory, zOut); }else{ - /* - ** NOTE: The Cygwin docs state that the maximum length needed - ** for the buffer passed to cygwin_conv_to_full_win32_path - ** is MAX_PATH. - */ - cygwin_conv_to_full_win32_path(zRelative, zFull); + cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull); } return SQLITE_OK; #endif @@ -4274,9 +4674,9 @@ static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ UNUSED_PARAMETER(pVfs); getLastErrorMsg(osGetLastError(), nBuf, zBufOut); } -static void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ +static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ UNUSED_PARAMETER(pVfs); - return (void(*)(void))osGetProcAddressA((HANDLE)pHandle, zSymbol); + return (void(*)(void))osGetProcAddressA((HANDLE)pH, zSym); } static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ UNUSED_PARAMETER(pVfs); @@ -4374,7 +4774,8 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ #endif /* 2^32 - to avoid use of LL and warnings in gcc */ static const sqlite3_int64 max32BitValue = - (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; + (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + + (sqlite3_int64)294967296; #if SQLITE_OS_WINCE SYSTEMTIME time; @@ -4483,7 +4884,6 @@ int sqlite3_os_init(void){ ** correctly. See ticket [bb3a86e890c8e96ab] */ assert( ArraySize(aSyscall)==74 ); -#ifndef SQLITE_OMIT_WAL /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); #if SQLITE_OS_WINRT @@ -4491,8 +4891,8 @@ int sqlite3_os_init(void){ #else osGetSystemInfo(&winSysInfo); #endif - assert(winSysInfo.dwAllocationGranularity > 0); -#endif + assert( winSysInfo.dwAllocationGranularity>0 ); + assert( winSysInfo.dwPageSize>0 ); sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; diff --git a/src/pager.c b/src/pager.c index 5879cf7..1c6a84f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -273,7 +273,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ ** * A write transaction is active. ** * An EXCLUSIVE or greater lock is held on the database file. ** * All writing and syncing of journal and database data has finished. -** If no error occured, all that remains is to finalize the journal to +** If no error occurred, all that remains is to finalize the journal to ** commit the transaction. If an error did occur, the caller will need ** to rollback the transaction. ** @@ -521,7 +521,7 @@ struct PagerSavepoint { ** ** doNotSpill, doNotSyncSpill ** -** These two boolean variables control the behaviour of cache-spills +** These two boolean variables control the behavior of cache-spills ** (calls made by the pcache module to the pagerStress() routine to ** write cached data to the file-system in order to free up memory). ** @@ -655,6 +655,11 @@ struct Pager { PagerSavepoint *aSavepoint; /* Array of active savepoints */ int nSavepoint; /* Number of elements in aSavepoint[] */ char dbFileVers[16]; /* Changes whenever database file changes */ + + u8 bUseFetch; /* True to use xFetch() */ + int nMmapOut; /* Number of mmap pages currently outstanding */ + sqlite3_int64 szMmap; /* Desired maximum mmap size */ + PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ /* ** End of the routinely-changing class members ***************************************************************************/ @@ -765,6 +770,16 @@ static const unsigned char aJournalMagic[] = { # define MEMDB pPager->memDb #endif +/* +** The macro USEFETCH is true if we are allowed to use the xFetch and xUnfetch +** interfaces to access the database using memory-mapped I/O. +*/ +#if SQLITE_MAX_MMAP_SIZE>0 +# define USEFETCH(x) ((x)->bUseFetch) +#else +# define USEFETCH(x) 0 +#endif + /* ** The maximum legal page number is (2^31 - 1). */ @@ -1399,7 +1414,7 @@ static int writeJournalHdr(Pager *pPager){ memset(zHeader, 0, sizeof(aJournalMagic)+4); } - /* The random check-hash initialiser */ + /* The random check-hash initializer */ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); /* The initial database size */ @@ -1838,6 +1853,8 @@ static int pager_error(Pager *pPager, int rc){ return rc; } +static int pager_truncate(Pager *pPager, Pgno nPage); + /* ** This routine ends a transaction. A transaction is usually ended by ** either a COMMIT or a ROLLBACK operation. This routine may be called @@ -1891,7 +1908,7 @@ static int pager_error(Pager *pPager, int rc){ ** to the first error encountered (the journal finalization one) is ** returned. */ -static int pager_end_transaction(Pager *pPager, int hasMaster){ +static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){ int rc = SQLITE_OK; /* Error code from journal finalization operation */ int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ @@ -1977,7 +1994,17 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){ */ rc2 = sqlite3WalEndWriteTransaction(pPager->pWal); assert( rc2==SQLITE_OK ); + }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){ + /* This branch is taken when committing a transaction in rollback-journal + ** mode if the database file on disk is larger than the database image. + ** At this point the journal has been finalized and the transaction + ** successfully committed, but the EXCLUSIVE lock is still held on the + ** file. So it is safe to truncate the database file to its minimum + ** required size. */ + assert( pPager->eLock==EXCLUSIVE_LOCK ); + rc = pager_truncate(pPager, pPager->dbSize); } + if( !pPager->exclusiveMode && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) ){ @@ -2016,7 +2043,7 @@ static void pagerUnlockAndRollback(Pager *pPager){ sqlite3EndBenignMalloc(); }else if( !pPager->exclusiveMode ){ assert( pPager->eState==PAGER_READER ); - pager_end_transaction(pPager, 0); + pager_end_transaction(pPager, 0, 0); } } pager_unlock(pPager); @@ -2240,7 +2267,7 @@ static int pager_playback_one_page( i64 ofst = (pgno-1)*(i64)pPager->pageSize; testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 ); assert( !pagerUseWal(pPager) ); - rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); + rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } @@ -2631,6 +2658,7 @@ static int pager_playback(Pager *pPager, int isHot){ int res = 1; /* Value returned by sqlite3OsAccess() */ char *zMaster = 0; /* Name of master journal file if any */ int needPagerReset; /* True to reset page prior to first page rollback */ + int nPlayback = 0; /* Total number of pages restored from journal */ /* Figure out how many records are in the journal. Abort early if ** the journal is empty. @@ -2731,7 +2759,9 @@ static int pager_playback(Pager *pPager, int isHot){ needPagerReset = 0; } rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0); - if( rc!=SQLITE_OK ){ + if( rc==SQLITE_OK ){ + nPlayback++; + }else{ if( rc==SQLITE_DONE ){ pPager->journalOff = szJ; break; @@ -2791,7 +2821,7 @@ end_playback: rc = sqlite3PagerSync(pPager); } if( rc==SQLITE_OK ){ - rc = pager_end_transaction(pPager, zMaster[0]!='\0'); + rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && zMaster[0] && res ){ @@ -2801,6 +2831,10 @@ end_playback: rc = pager_delmaster(pPager, zMaster); testcase( rc!=SQLITE_OK ); } + if( isHot && nPlayback ){ + sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s", + nPlayback, pPager->zJournal); + } /* The Pager.sectorSize variable may have been updated while rolling ** back a journal created by a process with a different sector size @@ -2822,11 +2856,10 @@ end_playback: ** If an IO error occurs, then the IO error is returned to the caller. ** Otherwise, SQLITE_OK is returned. */ -static int readDbPage(PgHdr *pPg){ +static int readDbPage(PgHdr *pPg, u32 iFrame){ Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */ Pgno pgno = pPg->pgno; /* Page number to read */ int rc = SQLITE_OK; /* Return code */ - int isInWal = 0; /* True if page is in log file */ int pgsz = pPager->pageSize; /* Number of bytes to read */ assert( pPager->eState>=PAGER_READER && !MEMDB ); @@ -2838,11 +2871,13 @@ static int readDbPage(PgHdr *pPg){ return SQLITE_OK; } - if( pagerUseWal(pPager) ){ +#ifndef SQLITE_OMIT_WAL + if( iFrame ){ /* Try to pull the page from the write-ahead log. */ - rc = sqlite3WalRead(pPager->pWal, pgno, &isInWal, pgsz, pPg->pData); - } - if( rc==SQLITE_OK && !isInWal ){ + rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData); + }else +#endif + { i64 iOffset = (pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset); if( rc==SQLITE_IOERR_SHORT_READ ){ @@ -2921,12 +2956,17 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){ Pager *pPager = (Pager *)pCtx; PgHdr *pPg; + assert( pagerUseWal(pPager) ); pPg = sqlite3PagerLookup(pPager, iPg); if( pPg ){ if( sqlite3PcachePageRefcount(pPg)==1 ){ sqlite3PcacheDrop(pPg); }else{ - rc = readDbPage(pPg); + u32 iFrame = 0; + rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); + if( rc==SQLITE_OK ){ + rc = readDbPage(pPg, iFrame); + } if( rc==SQLITE_OK ){ pPager->xReiniter(pPg); } @@ -3070,6 +3110,7 @@ static int pagerBeginReadTransaction(Pager *pPager){ rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); if( rc!=SQLITE_OK || changed ){ pager_reset(pPager); + if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0); } return rc; @@ -3331,6 +3372,29 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){ sqlite3PcacheSetCachesize(pPager->pPCache, mxPage); } +/* +** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap. +*/ +static void pagerFixMaplimit(Pager *pPager){ +#if SQLITE_MAX_MMAP_SIZE>0 + sqlite3_file *fd = pPager->fd; + if( isOpen(fd) ){ + sqlite3_int64 sz; + pPager->bUseFetch = (fd->pMethods->iVersion>=3) && pPager->szMmap>0; + sz = pPager->szMmap; + sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz); + } +#endif +} + +/* +** Change the maximum size of any memory mapping made of the database file. +*/ +void sqlite3PagerSetMmapLimit(Pager *pPager, sqlite3_int64 szMmap){ + pPager->szMmap = szMmap; + pagerFixMaplimit(pPager); +} + /* ** Free as much memory as possible from the pager. */ @@ -3566,6 +3630,7 @@ int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nReserve){ assert( nReserve>=0 && nReserve<1000 ); pPager->nReserve = (i16)nReserve; pagerReportSize(pPager); + pagerFixMaplimit(pPager); } return rc; } @@ -3719,7 +3784,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ ** dirty page were to be discarded from the cache via the pagerStress() ** routine, pagerStress() would not write the current page content to ** the database file. If a savepoint transaction were rolled back after -** this happened, the correct behaviour would be to restore the current +** this happened, the correct behavior would be to restore the current ** content of the page. However, since this content is not present in either ** the database file or the portion of the rollback journal and ** sub-journal rolled back the content could not be restored and the @@ -3743,12 +3808,26 @@ static void assertTruncateConstraint(Pager *pPager){ ** function does not actually modify the database file on disk. It ** just sets the internal state of the pager object so that the ** truncation will be done when the current transaction is committed. +** +** This function is only called right before committing a transaction. +** Once this function has been called, the transaction must either be +** rolled back or committed. It is not safe to call this function and +** then continue writing to the database. */ void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){ assert( pPager->dbSize>=nPage ); assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); pPager->dbSize = nPage; - assertTruncateConstraint(pPager); + + /* At one point the code here called assertTruncateConstraint() to + ** ensure that all pages being truncated away by this operation are, + ** if one or more savepoints are open, present in the savepoint + ** journal so that they can be restored if the savepoint is rolled + ** back. This is no longer necessary as this function is now only + ** called right before committing a transaction. So although the + ** Pager object may still have open savepoints (Pager.nSavepoint!=0), + ** they cannot be rolled back. So the assertTruncateConstraint() call + ** is no longer correct. */ } @@ -3777,6 +3856,81 @@ static int pagerSyncHotJournal(Pager *pPager){ return rc; } +/* +** Obtain a reference to a memory mapped page object for page number pgno. +** The new object will use the pointer pData, obtained from xFetch(). +** If successful, set *ppPage to point to the new page reference +** and return SQLITE_OK. Otherwise, return an SQLite error code and set +** *ppPage to zero. +** +** Page references obtained by calling this function should be released +** by calling pagerReleaseMapPage(). +*/ +static int pagerAcquireMapPage( + Pager *pPager, /* Pager object */ + Pgno pgno, /* Page number */ + void *pData, /* xFetch()'d data for this page */ + PgHdr **ppPage /* OUT: Acquired page object */ +){ + PgHdr *p; /* Memory mapped page to return */ + + if( pPager->pMmapFreelist ){ + *ppPage = p = pPager->pMmapFreelist; + pPager->pMmapFreelist = p->pDirty; + p->pDirty = 0; + memset(p->pExtra, 0, pPager->nExtra); + }else{ + *ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra); + if( p==0 ){ + sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData); + return SQLITE_NOMEM; + } + p->pExtra = (void *)&p[1]; + p->flags = PGHDR_MMAP; + p->nRef = 1; + p->pPager = pPager; + } + + assert( p->pExtra==(void *)&p[1] ); + assert( p->pPage==0 ); + assert( p->flags==PGHDR_MMAP ); + assert( p->pPager==pPager ); + assert( p->nRef==1 ); + + p->pgno = pgno; + p->pData = pData; + pPager->nMmapOut++; + + return SQLITE_OK; +} + +/* +** Release a reference to page pPg. pPg must have been returned by an +** earlier call to pagerAcquireMapPage(). +*/ +static void pagerReleaseMapPage(PgHdr *pPg){ + Pager *pPager = pPg->pPager; + pPager->nMmapOut--; + pPg->pDirty = pPager->pMmapFreelist; + pPager->pMmapFreelist = pPg; + + assert( pPager->fd->pMethods->iVersion>=3 ); + sqlite3OsUnfetch(pPager->fd, (i64)(pPg->pgno-1)*pPager->pageSize, pPg->pData); +} + +/* +** Free all PgHdr objects stored in the Pager.pMmapFreelist list. +*/ +static void pagerFreeMapHdrs(Pager *pPager){ + PgHdr *p; + PgHdr *pNext; + for(p=pPager->pMmapFreelist; p; p=pNext){ + pNext = p->pDirty; + sqlite3_free(p); + } +} + + /* ** Shutdown the page cache. Free all memory and close all files. ** @@ -3797,6 +3951,7 @@ int sqlite3PagerClose(Pager *pPager){ assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); + pagerFreeMapHdrs(pPager); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL @@ -4058,7 +4213,9 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ ** file size will be. */ assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); - if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){ + if( rc==SQLITE_OK + && (pList->pDirty ? pPager->dbSize : pList->pgno+1)>pPager->dbHintSize + ){ sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); pPager->dbHintSize = pPager->dbSize; @@ -4612,6 +4769,7 @@ int sqlite3PagerOpen( /* pPager->pBusyHandlerArg = 0; */ pPager->xReiniter = xReinit; /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ + /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ *ppPager = pPager; return SQLITE_OK; @@ -4801,6 +4959,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ goto failed; } if( bHotJournal ){ + if( pPager->readOnly ){ + rc = SQLITE_READONLY_ROLLBACK; + goto failed; + } + /* Get an EXCLUSIVE lock on the database file. At this point it is ** important that a RESERVED lock is not obtained on the way to the ** EXCLUSIVE lock. If it were, another process might open the @@ -4898,9 +5061,11 @@ int sqlite3PagerSharedLock(Pager *pPager){ ); } - if( !pPager->tempFile - && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0) - ){ + if( !pPager->tempFile && ( + pPager->pBackup + || sqlite3PcachePagecount(pPager->pPCache)>0 + || USEFETCH(pPager) + )){ /* The shared-lock has just been acquired on the database file ** and there are already pages in the cache (from a previous ** read or write transaction). Check to see if the database @@ -4926,7 +5091,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ if( nPage>0 ){ IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); - if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ goto failed; } }else{ @@ -4935,6 +5100,16 @@ int sqlite3PagerSharedLock(Pager *pPager){ if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){ pager_reset(pPager); + + /* Unmap the database file. It is possible that external processes + ** may have truncated the database file and then extended it back + ** to its original size while this process was not holding a lock. + ** In this case there may exist a Pager.pMap mapping that appears + ** to be the right size but is not actually valid. Avoid this + ** possibility by unmapping the db here. */ + if( USEFETCH(pPager) ){ + sqlite3OsUnfetch(pPager->fd, 0, 0); + } } } @@ -4976,7 +5151,7 @@ int sqlite3PagerSharedLock(Pager *pPager){ ** nothing to rollback, so this routine is a no-op. */ static void pagerUnlockIfUnused(Pager *pPager){ - if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){ + if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){ pagerUnlockAndRollback(pPager); } } @@ -5035,13 +5210,27 @@ int sqlite3PagerAcquire( Pager *pPager, /* The pager open on the database file */ Pgno pgno, /* Page number to fetch */ DbPage **ppPage, /* Write a pointer to the page here */ - int noContent /* Do not bother reading content from disk if true */ + int flags /* PAGER_ACQUIRE_XXX flags */ ){ - int rc; - PgHdr *pPg; + int rc = SQLITE_OK; + PgHdr *pPg = 0; + u32 iFrame = 0; /* Frame to read from WAL file */ + const int noContent = (flags & PAGER_ACQUIRE_NOCONTENT); + + /* It is acceptable to use a read-only (mmap) page for any page except + ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY + ** flag was specified by the caller. And so long as the db is not a + ** temporary or in-memory database. */ + const int bMmapOk = (pgno!=1 && USEFETCH(pPager) + && (pPager->eState==PAGER_READER || (flags & PAGER_ACQUIRE_READONLY)) +#ifdef SQLITE_HAS_CODEC + && pPager->xCodec==0 +#endif + ); assert( pPager->eState>=PAGER_READER ); assert( assert_pager_state(pPager) ); + assert( noContent==0 || bMmapOk==0 ); if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; @@ -5052,6 +5241,39 @@ int sqlite3PagerAcquire( if( pPager->errCode!=SQLITE_OK ){ rc = pPager->errCode; }else{ + + if( bMmapOk && pagerUseWal(pPager) ){ + rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); + if( rc!=SQLITE_OK ) goto pager_acquire_err; + } + + if( iFrame==0 && bMmapOk ){ + void *pData = 0; + + rc = sqlite3OsFetch(pPager->fd, + (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData + ); + + if( rc==SQLITE_OK && pData ){ + if( pPager->eState>PAGER_READER ){ + (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); + } + if( pPg==0 ){ + rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg); + }else{ + sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData); + } + if( pPg ){ + assert( rc==SQLITE_OK ); + *ppPage = pPg; + return SQLITE_OK; + } + } + if( rc!=SQLITE_OK ){ + goto pager_acquire_err; + } + } + rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage); } @@ -5110,9 +5332,13 @@ int sqlite3PagerAcquire( memset(pPg->pData, 0, pPager->pageSize); IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ + if( pagerUseWal(pPager) && bMmapOk==0 ){ + rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame); + if( rc!=SQLITE_OK ) goto pager_acquire_err; + } assert( pPg->pPager==pPager ); pPager->aStat[PAGER_STAT_MISS]++; - rc = readDbPage(pPg); + rc = readDbPage(pPg, iFrame); if( rc!=SQLITE_OK ){ goto pager_acquire_err; } @@ -5165,7 +5391,11 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){ void sqlite3PagerUnref(DbPage *pPg){ if( pPg ){ Pager *pPager = pPg->pPager; - sqlite3PcacheRelease(pPg); + if( pPg->flags & PGHDR_MMAP ){ + pagerReleaseMapPage(pPg); + }else{ + sqlite3PcacheRelease(pPg); + } pagerUnlockIfUnused(pPager); } } @@ -5500,6 +5730,7 @@ int sqlite3PagerWrite(DbPage *pDbPage){ Pager *pPager = pPg->pPager; Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize); + assert( (pPg->flags & PGHDR_MMAP)==0 ); assert( pPager->eState>=PAGER_WRITER_LOCKED ); assert( pPager->eState!=PAGER_ERROR ); assert( assert_pager_state(pPager) ); @@ -5699,6 +5930,11 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ pPager->aStat[PAGER_STAT_WRITE]++; } if( rc==SQLITE_OK ){ + /* Update the pager's copy of the change-counter. Otherwise, the + ** next time a read transaction is opened the cache will be + ** flushed (as the change-counter values will not match). */ + const void *pCopy = (const void *)&((const char *)zBuf)[24]; + memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers)); pPager->changeCountDone = 1; } }else{ @@ -5885,36 +6121,6 @@ int sqlite3PagerCommitPhaseOne( #endif if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - /* If this transaction has made the database smaller, then all pages - ** being discarded by the truncation must be written to the journal - ** file. - ** - ** Before reading the pages with page numbers larger than the - ** current value of Pager.dbSize, set dbSize back to the value - ** that it took at the start of the transaction. Otherwise, the - ** calls to sqlite3PagerGet() return zeroed pages instead of - ** reading data from the database file. - */ - if( pPager->dbSizedbOrigSize - && pPager->journalMode!=PAGER_JOURNALMODE_OFF - ){ - Pgno i; /* Iterator variable */ - const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ - const Pgno dbSize = pPager->dbSize; /* Database image size */ - pPager->dbSize = pPager->dbOrigSize; - for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){ - if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){ - PgHdr *pPage; /* Page to journal */ - rc = sqlite3PagerGet(pPager, i, &pPage); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - rc = sqlite3PagerWrite(pPage); - sqlite3PagerUnref(pPage); - if( rc!=SQLITE_OK ) goto commit_phase_one_exit; - } - } - pPager->dbSize = dbSize; - } - /* Write the master journal name into the journal file. If a master ** journal file name has already been written to the journal file, ** or if zMaster is NULL (no master journal), then this call is a no-op. @@ -5942,11 +6148,14 @@ int sqlite3PagerCommitPhaseOne( goto commit_phase_one_exit; } sqlite3PcacheCleanAll(pPager->pPCache); - - /* If the file on disk is not the same size as the database image, - ** then use pager_truncate to grow or shrink the file here. - */ - if( pPager->dbSize!=pPager->dbFileSize ){ + + /* If the file on disk is smaller than the database image, use + ** pager_truncate to grow the file here. This can happen if the database + ** image was extended as part of the current transaction and then the + ** last page in the db image moved to the free-list. In this case the + ** last page is never written out to disk, leaving the database file + ** undersized. Fix this now if it is the case. */ + if( pPager->dbSize>pPager->dbFileSize ){ Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); assert( pPager->eState==PAGER_WRITER_DBMOD ); rc = pager_truncate(pPager, nNew); @@ -6019,7 +6228,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){ } PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - rc = pager_end_transaction(pPager, pPager->setMaster); + rc = pager_end_transaction(pPager, pPager->setMaster, 1); return pager_error(pPager, rc); } @@ -6064,11 +6273,11 @@ int sqlite3PagerRollback(Pager *pPager){ if( pagerUseWal(pPager) ){ int rc2; rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); - rc2 = pager_end_transaction(pPager, pPager->setMaster); + rc2 = pager_end_transaction(pPager, pPager->setMaster, 0); if( rc==SQLITE_OK ) rc = rc2; }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ int eState = pPager->eState; - rc = pager_end_transaction(pPager, 0); + rc = pager_end_transaction(pPager, 0, 0); if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ /* This can happen using journal_mode=off. Move the pager to the error ** state to indicate that the contents of the cache may not be trusted. @@ -6083,7 +6292,7 @@ int sqlite3PagerRollback(Pager *pPager){ } assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK ); - assert( rc==SQLITE_OK || rc==SQLITE_FULL + assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR ); /* If an error occurs during a ROLLBACK, we can no longer trust the pager @@ -6466,7 +6675,8 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){ */ if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){ needSyncPgno = pPg->pgno; - assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); + assert( pPager->journalMode==PAGER_JOURNALMODE_OFF || + pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize ); assert( pPg->flags&PGHDR_DIRTY ); } @@ -6816,11 +7026,12 @@ static int pagerOpenWal(Pager *pPager){ ** (e.g. due to malloc() failure), return an error code. */ if( rc==SQLITE_OK ){ - rc = sqlite3WalOpen(pPager->pVfs, + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, pPager->exclusiveMode, pPager->journalSizeLimit, &pPager->pWal ); } + pagerFixMaplimit(pPager); return rc; } @@ -6911,6 +7122,7 @@ int sqlite3PagerCloseWal(Pager *pPager){ rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); pPager->pWal = 0; + pagerFixMaplimit(pPager); } } return rc; diff --git a/src/pager.h b/src/pager.h index 90f8e6a..6f65913 100644 --- a/src/pager.h +++ b/src/pager.h @@ -78,6 +78,12 @@ typedef struct PgHdr DbPage; #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ +/* +** Flags that make up the mask passed to sqlite3PagerAcquire(). +*/ +#define PAGER_ACQUIRE_NOCONTENT 0x01 /* Do not load data from disk */ +#define PAGER_ACQUIRE_READONLY 0x02 /* Read-only page is acceptable */ + /* ** The remainder of this file contains the declarations of the functions ** that make up the Pager sub-system API. See source code comments for @@ -102,6 +108,7 @@ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); +void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); void sqlite3PagerShrink(Pager*); void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); int sqlite3PagerLockingMode(Pager *, int); diff --git a/src/parse.y b/src/parse.y index 0bfe823..8310b26 100644 --- a/src/parse.y +++ b/src/parse.y @@ -435,8 +435,8 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) // The "distinct" nonterminal is true (1) if the DISTINCT keyword is // present and false (0) if it is not. // -%type distinct {int} -distinct(A) ::= DISTINCT. {A = 1;} +%type distinct {u16} +distinct(A) ::= DISTINCT. {A = SF_Distinct;} distinct(A) ::= ALL. {A = 0;} distinct(A) ::= . {A = 0;} @@ -499,7 +499,8 @@ stl_prefix(A) ::= seltablist(X) joinop(Y). { if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].jointype = (u8)Y; } stl_prefix(A) ::= . {A = 0;} -seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) using_opt(U). { +seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) + on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U); sqlite3SrcListIndexedBy(pParse, A, &I); } @@ -512,25 +513,25 @@ seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) usi as(Z) on_opt(N) using_opt(U). { if( X==0 && Z.n==0 && N==0 && U==0 ){ A = F; + }else if( F->nSrc==1 ){ + A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,0,N,U); + if( A ){ + struct SrcList_item *pNew = &A->a[A->nSrc-1]; + struct SrcList_item *pOld = F->a; + pNew->zName = pOld->zName; + pNew->zDatabase = pOld->zDatabase; + pNew->pSelect = pOld->pSelect; + pOld->zName = pOld->zDatabase = 0; + pOld->pSelect = 0; + } + sqlite3SrcListDelete(pParse->db, F); }else{ Select *pSubquery; sqlite3SrcListShiftJoinType(F); - pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0); + pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0,0); A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,pSubquery,N,U); } } - - // A seltablist_paren nonterminal represents anything in a FROM that - // is contained inside parentheses. This can be either a subquery or - // a grouping of table and subqueries. - // -// %type seltablist_paren {Select*} -// %destructor seltablist_paren {sqlite3SelectDelete(pParse->db, $$);} -// seltablist_paren(A) ::= select(S). {A = S;} -// seltablist_paren(A) ::= seltablist(F). { -// sqlite3SrcListShiftJoinType(F); -// A = sqlite3SelectNew(pParse,0,F,0,0,0,0,0,0,0); -// } %endif SQLITE_OMIT_SUBQUERY %type dbnm {Token} @@ -653,7 +654,8 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;} ////////////////////////// The UPDATE command //////////////////////////////// // %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) orderby_opt(O) limit_opt(L). { +cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) + orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE"); @@ -661,7 +663,8 @@ cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) } %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT -cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). { +cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) + where_opt(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); sqlite3Update(pParse,X,Y,W,R); diff --git a/src/pcache.h b/src/pcache.h index b9135fd..f4d4ad7 100644 --- a/src/pcache.h +++ b/src/pcache.h @@ -53,6 +53,8 @@ struct PgHdr { #define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ #define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ +#define PGHDR_MMAP 0x040 /* This is an mmap page object */ + /* Initialize and shutdown the page cache subsystem */ int sqlite3PcacheInitialize(void); void sqlite3PcacheShutdown(void); diff --git a/src/pragma.c b/src/pragma.c index d3061b2..3056a7d 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -184,6 +184,9 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, { "vdbe_trace", SQLITE_VdbeTrace }, + { "vdbe_addoptrace", SQLITE_VdbeAddopTrace}, + { "vdbe_debug", SQLITE_SqlTrace | SQLITE_VdbeListing + | SQLITE_VdbeTrace }, #endif #ifndef SQLITE_OMIT_CHECK { "ignore_check_constraints", SQLITE_IgnoreChecks }, @@ -316,7 +319,7 @@ void sqlite3Pragma( int rc; /* return value form SQLITE_FCNTL_PRAGMA */ sqlite3 *db = pParse->db; /* The database connection */ Db *pDb; /* The specific database being pragmaed */ - Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db); /* Prepared statement */ + Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ if( v==0 ) return; sqlite3VdbeRunOnlyOnce(v); @@ -399,11 +402,12 @@ void sqlite3Pragma( static const VdbeOpList getCacheSize[] = { { OP_Transaction, 0, 0, 0}, /* 0 */ { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ - { OP_IfPos, 1, 7, 0}, + { OP_IfPos, 1, 8, 0}, { OP_Integer, 0, 2, 0}, { OP_Subtract, 1, 2, 1}, - { OP_IfPos, 1, 7, 0}, + { OP_IfPos, 1, 8, 0}, { OP_Integer, 0, 1, 0}, /* 6 */ + { OP_Noop, 0, 0, 0}, { OP_ResultRow, 1, 1, 0}, }; int addr; @@ -741,6 +745,43 @@ void sqlite3Pragma( } }else + /* + ** PRAGMA [database.]mmap_size(N) + ** + ** Used to set mapping size limit. The mapping size limit is + ** used to limit the aggregate size of all memory mapped regions of the + ** database file. If this parameter is set to zero, then memory mapping + ** is not used at all. If N is negative, then the default memory map + ** limit determined by sqlite3_config(SQLITE_CONFIG_MMAP_SIZE) is set. + ** The parameter N is measured in bytes. + ** + ** This value is advisory. The underlying VFS is free to memory map + ** as little or as much as it wants. Except, if N is set to 0 then the + ** upper layers will never invoke the xFetch interfaces to the VFS. + */ + if( sqlite3StrICmp(zLeft,"mmap_size")==0 ){ + sqlite3_int64 sz; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + if( zRight ){ + int ii; + sqlite3Atoi64(zRight, &sz, 1000, SQLITE_UTF8); + if( sz<0 ) sz = sqlite3GlobalConfig.szMmap; + if( pId2->n==0 ) db->szMmap = sz; + for(ii=db->nDb-1; ii>=0; ii--){ + if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){ + sqlite3BtreeSetMmapLimit(db->aDb[ii].pBt, sz); + } + } + } + sz = -1; + if( sqlite3_file_control(db,zDb,SQLITE_FCNTL_MMAP_SIZE,&sz)==SQLITE_OK ){ +#if SQLITE_MAX_MMAP_SIZE==0 + sz = 0; +#endif + returnSingleInt(pParse, "mmap_size", sz); + } + }else + /* ** PRAGMA temp_store ** PRAGMA temp_store = "default"|"memory"|"file" @@ -948,11 +989,14 @@ void sqlite3Pragma( if( sqlite3ReadSchema(pParse) ) goto pragma_out; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ - int i; + int i, k; int nHidden = 0; Column *pCol; + Index *pPk; + for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){} sqlite3VdbeSetNumCols(v, 6); pParse->nMem = 6; + sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC); @@ -975,8 +1019,14 @@ void sqlite3Pragma( }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, 5); } - sqlite3VdbeAddOp2(v, OP_Integer, - (pCol->colFlags&COLFLAG_PRIMKEY)!=0, 6); + if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){ + k = 0; + }else if( pPk==0 ){ + k = 1; + }else{ + for(k=1; ALWAYS(k<=pTab->nCol) && pPk->aiColumn[k-1]!=i; k++){} + } + sqlite3VdbeAddOp2(v, OP_Integer, k, 6); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6); } } @@ -992,6 +1042,7 @@ void sqlite3Pragma( pTab = pIdx->pTable; sqlite3VdbeSetNumCols(v, 3); pParse->nMem = 3; + sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC); @@ -1018,6 +1069,7 @@ void sqlite3Pragma( int i = 0; sqlite3VdbeSetNumCols(v, 3); pParse->nMem = 3; + sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC); @@ -1081,6 +1133,7 @@ void sqlite3Pragma( int i = 0; sqlite3VdbeSetNumCols(v, 8); pParse->nMem = 8; + sqlite3CodeVerifySchema(pParse, iDb); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", SQLITE_STATIC); @@ -1114,6 +1167,122 @@ void sqlite3Pragma( }else #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ +#ifndef SQLITE_OMIT_FOREIGN_KEY +#ifndef SQLITE_OMIT_TRIGGER + if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){ + FKey *pFK; /* A foreign key constraint */ + Table *pTab; /* Child table contain "REFERENCES" keyword */ + Table *pParent; /* Parent table that child points to */ + Index *pIdx; /* Index in the parent table */ + int i; /* Loop counter: Foreign key number for pTab */ + int j; /* Loop counter: Field of the foreign key */ + HashElem *k; /* Loop counter: Next table in schema */ + int x; /* result variable */ + int regResult; /* 3 registers to hold a result row */ + int regKey; /* Register to hold key for checking the FK */ + int regRow; /* Registers to hold a row from pTab */ + int addrTop; /* Top of a loop checking foreign keys */ + int addrOk; /* Jump here if the key is OK */ + int *aiCols; /* child to parent column mapping */ + + if( sqlite3ReadSchema(pParse) ) goto pragma_out; + regResult = pParse->nMem+1; + pParse->nMem += 4; + regKey = ++pParse->nMem; + regRow = ++pParse->nMem; + v = sqlite3GetVdbe(pParse); + sqlite3VdbeSetNumCols(v, 4); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "rowid", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "parent", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "fkid", SQLITE_STATIC); + sqlite3CodeVerifySchema(pParse, iDb); + k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); + while( k ){ + if( zRight ){ + pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); + k = 0; + }else{ + pTab = (Table*)sqliteHashData(k); + k = sqliteHashNext(k); + } + if( pTab==0 || pTab->pFKey==0 ) continue; + sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); + if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; + sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); + sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName, + P4_TRANSIENT); + for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); + if( pParent==0 ) break; + pIdx = 0; + sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName); + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); + if( x==0 ){ + if( pIdx==0 ){ + sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); + }else{ + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + } + }else{ + k = 0; + break; + } + } + if( pFK ) break; + if( pParse->nTabnTab = i; + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); + for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ + pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb); + assert( pParent!=0 ); + pIdx = 0; + aiCols = 0; + x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); + assert( x==0 ); + addrOk = sqlite3VdbeMakeLabel(v); + if( pIdx==0 ){ + int iKey = pFK->aCol[0].iFrom; + assert( iKey>=0 && iKeynCol ); + if( iKey!=pTab->iPKey ){ + sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); + sqlite3ColumnDefault(v, pTab, iKey, regRow); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); + sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, + sqlite3VdbeCurrentAddr(v)+3); + }else{ + sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); + } + sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); + sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); + }else{ + for(j=0; jnCol; j++){ + sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, + aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); + sqlite3VdbeChangeP4(v, -1, + sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); + sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); + } + sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); + sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0, + pFK->zTo, P4_TRANSIENT); + sqlite3VdbeAddOp2(v, OP_Integer, i-1, regResult+3); + sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4); + sqlite3VdbeResolveLabel(v, addrOk); + sqlite3DbFree(db, aiCols); + } + sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); + sqlite3VdbeJumpHere(v, addrTop); + } + }else +#endif /* !defined(SQLITE_OMIT_TRIGGER) */ +#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ + #ifndef NDEBUG if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ if( zRight ){ @@ -1398,6 +1567,11 @@ void sqlite3Pragma( ** PRAGMA [database.]user_version ** PRAGMA [database.]user_version = ** + ** PRAGMA [database.]freelist_count = + ** + ** PRAGMA [database.]application_id + ** PRAGMA [database.]application_id = + ** ** The pragma's schema_version and user_version are used to set or get ** the value of the schema-version and user-version, respectively. Both ** the schema-version and the user-version are 32-bit signed integers @@ -1419,10 +1593,14 @@ void sqlite3Pragma( if( sqlite3StrICmp(zLeft, "schema_version")==0 || sqlite3StrICmp(zLeft, "user_version")==0 || sqlite3StrICmp(zLeft, "freelist_count")==0 + || sqlite3StrICmp(zLeft, "application_id")==0 ){ int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */ sqlite3VdbeUsesBtree(v, iDb); switch( zLeft[0] ){ + case 'a': case 'A': + iCookie = BTREE_APPLICATION_ID; + break; case 'f': case 'F': iCookie = BTREE_FREE_PAGE_COUNT; break; @@ -1611,7 +1789,7 @@ void sqlite3Pragma( }else #endif #if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD) - if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){ + if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){ #ifdef SQLITE_HAS_CODEC if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){ sqlite3_activate_see(&zRight[4]); diff --git a/src/prepare.c b/src/prepare.c index 5ac8de7..d78d83c 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -179,7 +179,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ /* zMasterSchema and zInitScript are set to point at the master schema ** and initialisation script appropriate for the database being - ** initialised. zMasterName is the name of the master table. + ** initialized. zMasterName is the name of the master table. */ if( !OMIT_TEMPDB && iDb==1 ){ zMasterSchema = temp_master_schema; @@ -259,11 +259,15 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ */ if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */ if( iDb==0 ){ +#ifndef SQLITE_OMIT_UTF16 u8 encoding; /* If opening the main database, set ENC(db). */ encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3; if( encoding==0 ) encoding = SQLITE_UTF8; ENC(db) = encoding; +#else + ENC(db) = SQLITE_UTF8; +#endif }else{ /* If opening an attached database, the encoding much match ENC(db) */ if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){ @@ -400,7 +404,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ } } - /* Once all the other databases have been initialised, load the schema + /* Once all the other databases have been initialized, load the schema ** for the TEMP database. This is loaded last, as the TEMP database ** schema may contain references to objects in other databases. */ @@ -423,7 +427,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){ } /* -** This routine is a no-op if the database schema is already initialised. +** This routine is a no-op if the database schema is already initialized. ** Otherwise, the schema is loaded. An error code is returned. */ int sqlite3ReadSchema(Parse *pParse){ @@ -650,7 +654,6 @@ static int sqlite3Prepare( } #endif - assert( db->init.busy==0 || saveSqlFlag==0 ); if( db->init.busy==0 ){ Vdbe *pVdbe = pParse->pVdbe; sqlite3VdbeSetSql(pVdbe, zSql, (int)(pParse->zTail-zSql), saveSqlFlag); diff --git a/src/resolve.c b/src/resolve.c index 51aab77..91efcaa 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -150,6 +150,35 @@ static int nameInUsingClause(IdList *pUsing, const char *zCol){ return 0; } +/* +** Subqueries stores the original database, table and column names for their +** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". +** Check to see if the zSpan given to this routine matches the zDb, zTab, +** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will +** match anything. +*/ +int sqlite3MatchSpanName( + const char *zSpan, + const char *zCol, + const char *zTab, + const char *zDb +){ + int n; + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} + if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ + return 0; + } + zSpan += n+1; + for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} + if( zTab && (sqlite3StrNICmp(zSpan, zTab, n)!=0 || zTab[n]!=0) ){ + return 0; + } + zSpan += n+1; + if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ + return 0; + } + return 1; +} /* ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up @@ -206,6 +235,20 @@ static int lookupName( pExpr->pTab = 0; ExprSetIrreducible(pExpr); + /* Translate the schema name in zDb into a pointer to the corresponding + ** schema. If not found, pSchema will remain NULL and nothing will match + ** resulting in an appropriate error message toward the end of this routine + */ + if( zDb ){ + for(i=0; inDb; i++){ + assert( db->aDb[i].zName ); + if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){ + pSchema = db->aDb[i].pSchema; + break; + } + } + } + /* Start at the inner-most context and move outward until a match is found */ while( pNC && cnt==0 ){ ExprList *pEList; @@ -214,31 +257,36 @@ static int lookupName( if( pSrcList ){ for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ Table *pTab; - int iDb; Column *pCol; pTab = pItem->pTab; assert( pTab!=0 && pTab->zName!=0 ); - iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( pTab->nCol>0 ); + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){ + int hit = 0; + pEList = pItem->pSelect->pEList; + for(j=0; jnExpr; j++){ + if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){ + cnt++; + cntTab = 2; + pMatch = pItem; + pExpr->iColumn = j; + hit = 1; + } + } + if( hit || zTab==0 ) continue; + } + if( zDb && pTab->pSchema!=pSchema ){ + continue; + } if( zTab ){ - if( pItem->zAlias ){ - char *zTabName = pItem->zAlias; - if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; - }else{ - char *zTabName = pTab->zName; - if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){ - continue; - } - if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){ - continue; - } + const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName; + assert( zTabName!=0 ); + if( sqlite3StrICmp(zTabName, zTab)!=0 ){ + continue; } } if( 0==(cntTab++) ){ - pExpr->iTable = pItem->iCursor; - pExpr->pTab = pTab; - pSchema = pTab->pSchema; pMatch = pItem; } for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ @@ -252,17 +300,19 @@ static int lookupName( if( nameInUsingClause(pItem->pUsing, zCol) ) continue; } cnt++; - pExpr->iTable = pItem->iCursor; - pExpr->pTab = pTab; pMatch = pItem; - pSchema = pTab->pSchema; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; break; } } } - } + if( pMatch ){ + pExpr->iTable = pMatch->iCursor; + pExpr->pTab = pMatch->pTab; + pSchema = pExpr->pTab->pSchema; + } + } /* if( pSrcList ) */ #ifndef SQLITE_OMIT_TRIGGER /* If we have not already resolved the name, then maybe @@ -338,7 +388,10 @@ static int lookupName( ** Note that the expression in the result set should have already been ** resolved by the time the WHERE clause is resolved. */ - if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){ + if( (pEList = pNC->pEList)!=0 + && zTab==0 + && ((pNC->ncFlags & NC_AsMaybe)==0 || cnt==0) + ){ for(j=0; jnExpr; j++){ char *zAs = pEList->a[j].zName; if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ @@ -429,7 +482,9 @@ static int lookupName( lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); - sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); + if( pExpr->op!=TK_AS ){ + sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList); + } /* Increment the nRef value on all name contexts from TopNC up to ** the point where the name matched. */ for(;;){ @@ -597,7 +652,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId); pNC->nErr++; is_agg = 0; - }else if( no_such_func ){ + }else if( no_such_func && pParse->db->init.busy==0 ){ sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId); pNC->nErr++; }else if( wrong_num_args ){ @@ -1033,23 +1088,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ return WRC_Abort; } - /* Set up the local name-context to pass to sqlite3ResolveExprNames() to - ** resolve the result-set expression list. - */ - sNC.ncFlags = NC_AllowAgg; - sNC.pSrcList = p->pSrc; - sNC.pNext = pOuterNC; - - /* Resolve names in the result set. */ - pEList = p->pEList; - assert( pEList!=0 ); - for(i=0; inExpr; i++){ - Expr *pX = pEList->a[i].pExpr; - if( sqlite3ResolveExprNames(&sNC, pX) ){ - return WRC_Abort; - } - } - /* Recursively resolve names in all subqueries */ for(i=0; ipSrc->nSrc; i++){ @@ -1077,6 +1115,23 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } + /* Set up the local name-context to pass to sqlite3ResolveExprNames() to + ** resolve the result-set expression list. + */ + sNC.ncFlags = NC_AllowAgg; + sNC.pSrcList = p->pSrc; + sNC.pNext = pOuterNC; + + /* Resolve names in the result set. */ + pEList = p->pEList; + assert( pEList!=0 ); + for(i=0; inExpr; i++){ + Expr *pX = pEList->a[i].pExpr; + if( sqlite3ResolveExprNames(&sNC, pX) ){ + return WRC_Abort; + } + } + /* If there are no aggregate functions in the result-set, and no GROUP BY ** expression, do not allow aggregates in any of the other expressions. */ @@ -1104,11 +1159,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ ** re-evaluated for each reference to it. */ sNC.pEList = p->pEList; - if( sqlite3ResolveExprNames(&sNC, p->pWhere) || - sqlite3ResolveExprNames(&sNC, p->pHaving) - ){ - return WRC_Abort; - } + sNC.ncFlags |= NC_AsMaybe; + if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; + if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; + sNC.ncFlags &= ~NC_AsMaybe; /* The ORDER BY and GROUP BY clauses may not refer to terms in ** outer queries @@ -1229,6 +1283,7 @@ int sqlite3ResolveExprNames( #endif savedHasAgg = pNC->ncFlags & NC_HasAgg; pNC->ncFlags &= ~NC_HasAgg; + memset(&w, 0, sizeof(w)); w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.pParse = pNC->pParse; @@ -1269,6 +1324,7 @@ void sqlite3ResolveSelectNames( Walker w; assert( p!=0 ); + memset(&w, 0, sizeof(w)); w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.pParse = pParse; diff --git a/src/select.c b/src/select.c index a90877d..f3f1490 100644 --- a/src/select.c +++ b/src/select.c @@ -55,7 +55,7 @@ Select *sqlite3SelectNew( ExprList *pGroupBy, /* the GROUP BY clause */ Expr *pHaving, /* the HAVING clause */ ExprList *pOrderBy, /* the ORDER BY clause */ - int isDistinct, /* true if the DISTINCT keyword is present */ + u16 selFlags, /* Flag parameters, such as SF_Distinct */ Expr *pLimit, /* LIMIT value. NULL means not used */ Expr *pOffset /* OFFSET value. NULL means no offset */ ){ @@ -79,7 +79,7 @@ Select *sqlite3SelectNew( pNew->pGroupBy = pGroupBy; pNew->pHaving = pHaving; pNew->pOrderBy = pOrderBy; - pNew->selFlags = isDistinct ? SF_Distinct : 0; + pNew->selFlags = selFlags; pNew->op = TK_SELECT; pNew->pLimit = pLimit; pNew->pOffset = pOffset; @@ -1336,8 +1336,6 @@ static int selectColumnsFromExprList( /* Get an appropriate name for the column */ p = sqlite3ExprSkipCollate(pEList->a[i].pExpr); - assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue) - || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 ); if( (zName = pEList->a[i].zName)!=0 ){ /* If the column contains an "AS " phrase, use as the name */ zName = sqlite3DbStrDup(db, zName); @@ -1375,6 +1373,9 @@ static int selectColumnsFromExprList( for(j=cnt=0; j1 && sqlite3Isdigit(zName[k]); k--){} + if( zName[k]==':' ) nName = k; zName[nName] = 0; zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); sqlite3DbFree(db, zName); @@ -1706,6 +1707,8 @@ static int multiSelect( int addr = 0; int nLimit; assert( !pPrior->pLimit ); + pPrior->iLimit = p->iLimit; + pPrior->iOffset = p->iOffset; pPrior->pLimit = p->pLimit; pPrior->pOffset = p->pOffset; explainSetInteger(iSub1, pParse->iNextSelectId); @@ -2363,7 +2366,8 @@ static int multiSelectOrderBy( }else{ int nExpr = p->pEList->nExpr; assert( nOrderBy>=nExpr || db->mallocFailed ); - regPrev = sqlite3GetTempRange(pParse, nExpr+1); + regPrev = pParse->nMem+1; + pParse->nMem += nExpr+1; sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev); pKeyDup = sqlite3DbMallocZero(db, sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) ); @@ -2545,12 +2549,6 @@ static int multiSelectOrderBy( sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); - /* Release temporary registers - */ - if( regPrev ){ - sqlite3ReleaseTempRange(pParse, regPrev, nOrderBy+1); - } - /* Jump to the this point in order to terminate the query. */ sqlite3VdbeResolveLabel(v, labelEnd); @@ -2962,12 +2960,15 @@ static int flattenSubquery( Select *pNew; ExprList *pOrderBy = p->pOrderBy; Expr *pLimit = p->pLimit; + Expr *pOffset = p->pOffset; Select *pPrior = p->pPrior; p->pOrderBy = 0; p->pSrc = 0; p->pPrior = 0; p->pLimit = 0; + p->pOffset = 0; pNew = sqlite3SelectDup(db, p, 0); + p->pOffset = pOffset; p->pLimit = pLimit; p->pOrderBy = pOrderBy; p->pSrc = pSrc; @@ -3160,34 +3161,43 @@ static int flattenSubquery( #endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */ /* -** Analyze the SELECT statement passed as an argument to see if it -** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if -** it is, or 0 otherwise. At present, a query is considered to be -** a min()/max() query if: +** Based on the contents of the AggInfo structure indicated by the first +** argument, this function checks if the following are true: ** -** 1. There is a single object in the FROM clause. +** * the query contains just a single aggregate function, +** * the aggregate function is either min() or max(), and +** * the argument to the aggregate function is a column value. ** -** 2. There is a single expression in the result set, and it is -** either min(x) or max(x), where x is a column reference. +** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX +** is returned as appropriate. Also, *ppMinMax is set to point to the +** list of arguments passed to the aggregate before returning. +** +** Or, if the conditions above are not met, *ppMinMax is set to 0 and +** WHERE_ORDERBY_NORMAL is returned. */ -static u8 minMaxQuery(Select *p){ - Expr *pExpr; - ExprList *pEList = p->pEList; +static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){ + int eRet = WHERE_ORDERBY_NORMAL; /* Return value */ - if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL; - pExpr = pEList->a[0].pExpr; - if( pExpr->op!=TK_AGG_FUNCTION ) return 0; - if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0; - pEList = pExpr->x.pList; - if( pEList==0 || pEList->nExpr!=1 ) return 0; - if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){ - return WHERE_ORDERBY_MIN; - }else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){ - return WHERE_ORDERBY_MAX; + *ppMinMax = 0; + if( pAggInfo->nFunc==1 ){ + Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */ + ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */ + + assert( pExpr->op==TK_AGG_FUNCTION ); + if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){ + const char *zFunc = pExpr->u.zToken; + if( sqlite3StrICmp(zFunc, "min")==0 ){ + eRet = WHERE_ORDERBY_MIN; + *ppMinMax = pEList; + }else if( sqlite3StrICmp(zFunc, "max")==0 ){ + eRet = WHERE_ORDERBY_MAX; + *ppMinMax = pEList; + } + } } - return WHERE_ORDERBY_NORMAL; + + assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 ); + return eRet; } /* @@ -3250,6 +3260,69 @@ int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ } return SQLITE_OK; } +/* +** Detect compound SELECT statements that use an ORDER BY clause with +** an alternative collating sequence. +** +** SELECT ... FROM t1 EXCEPT SELECT ... FROM t2 ORDER BY .. COLLATE ... +** +** These are rewritten as a subquery: +** +** SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2) +** ORDER BY ... COLLATE ... +** +** This transformation is necessary because the multiSelectOrderBy() routine +** above that generates the code for a compound SELECT with an ORDER BY clause +** uses a merge algorithm that requires the same collating sequence on the +** result columns as on the ORDER BY clause. See ticket +** http://www.sqlite.org/src/info/6709574d2a +** +** This transformation is only needed for EXCEPT, INTERSECT, and UNION. +** The UNION ALL operator works fine with multiSelectOrderBy() even when +** there are COLLATE terms in the ORDER BY. +*/ +static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ + int i; + Select *pNew; + Select *pX; + sqlite3 *db; + struct ExprList_item *a; + SrcList *pNewSrc; + Parse *pParse; + Token dummy; + + if( p->pPrior==0 ) return WRC_Continue; + if( p->pOrderBy==0 ) return WRC_Continue; + for(pX=p; pX && (pX->op==TK_ALL || pX->op==TK_SELECT); pX=pX->pPrior){} + if( pX==0 ) return WRC_Continue; + a = p->pOrderBy->a; + for(i=p->pOrderBy->nExpr-1; i>=0; i--){ + if( a[i].pExpr->flags & EP_Collate ) break; + } + if( i<0 ) return WRC_Continue; + + /* If we reach this point, that means the transformation is required. */ + + pParse = pWalker->pParse; + db = pParse->db; + pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); + if( pNew==0 ) return WRC_Abort; + memset(&dummy, 0, sizeof(dummy)); + pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0); + if( pNewSrc==0 ) return WRC_Abort; + *pNew = *p; + p->pSrc = pNewSrc; + p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ALL, 0)); + p->op = TK_SELECT; + p->pWhere = 0; + pNew->pGroupBy = 0; + pNew->pHaving = 0; + pNew->pOrderBy = 0; + p->pPrior = 0; + pNew->pLimit = 0; + pNew->pOffset = 0; + return WRC_Continue; +} /* ** This routine is a Walker callback for "expanding" a SELECT statement. @@ -3282,14 +3355,16 @@ static int selectExpander(Walker *pWalker, Select *p){ ExprList *pEList; struct SrcList_item *pFrom; sqlite3 *db = pParse->db; + Expr *pE, *pRight, *pExpr; + u16 selFlags = p->selFlags; + p->selFlags |= SF_Expanded; if( db->mallocFailed ){ return WRC_Abort; } - if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){ + if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ return WRC_Prune; } - p->selFlags |= SF_Expanded; pTabList = p->pSrc; pEList = p->pEList; @@ -3332,6 +3407,12 @@ static int selectExpander(Walker *pWalker, Select *p){ assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom); if( pTab==0 ) return WRC_Abort; + if( pTab->nRef==0xffff ){ + sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535", + pTab->zName); + pFrom->pTab = 0; + return WRC_Abort; + } pTab->nRef++; #if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE) if( pTab->pSelect || IsVirtual(pTab) ){ @@ -3367,7 +3448,7 @@ static int selectExpander(Walker *pWalker, Select *p){ ** that need expanding. */ for(k=0; knExpr; k++){ - Expr *pE = pEList->a[k].pExpr; + pE = pEList->a[k].pExpr; if( pE->op==TK_ALL ) break; assert( pE->op!=TK_DOT || pE->pRight!=0 ); assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) ); @@ -3385,10 +3466,18 @@ static int selectExpander(Walker *pWalker, Select *p){ int longNames = (flags & SQLITE_FullColNames)!=0 && (flags & SQLITE_ShortColNames)==0; + /* When processing FROM-clause subqueries, it is always the case + ** that full_column_names=OFF and short_column_names=ON. The + ** sqlite3ResultSetOfSelect() routine makes it so. */ + assert( (p->selFlags & SF_NestedFrom)==0 + || ((flags & SQLITE_FullColNames)==0 && + (flags & SQLITE_ShortColNames)!=0) ); + for(k=0; knExpr; k++){ - Expr *pE = a[k].pExpr; - assert( pE->op!=TK_DOT || pE->pRight!=0 ); - if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){ + pE = a[k].pExpr; + pRight = pE->pRight; + assert( pE->op!=TK_DOT || pRight!=0 ); + if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pRight->op!=TK_ALL) ){ /* This particular expression does not need to be expanded. */ pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr); @@ -3403,32 +3492,43 @@ static int selectExpander(Walker *pWalker, Select *p){ /* This expression is a "*" or a "TABLE.*" and needs to be ** expanded. */ int tableSeen = 0; /* Set to 1 when TABLE matches */ - char *zTName; /* text of name of TABLE */ + char *zTName = 0; /* text of name of TABLE */ if( pE->op==TK_DOT ){ assert( pE->pLeft!=0 ); assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); zTName = pE->pLeft->u.zToken; - }else{ - zTName = 0; } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; + Select *pSub = pFrom->pSelect; char *zTabName = pFrom->zAlias; + const char *zSchemaName = 0; + int iDb; if( zTabName==0 ){ zTabName = pTab->zName; } if( db->mallocFailed ) break; - if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ - continue; + if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){ + pSub = 0; + if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ + continue; + } + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zSchemaName = iDb>=0 ? db->aDb[iDb].zName : "*"; } - tableSeen = 1; for(j=0; jnCol; j++){ - Expr *pExpr, *pRight; char *zName = pTab->aCol[j].zName; char *zColname; /* The computed column name */ char *zToFree; /* Malloced string that needs to be freed */ Token sColname; /* Computed column name as a token */ + assert( zName ); + if( zTName && pSub + && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0 + ){ + continue; + } + /* If a column is marked as 'hidden' (currently only possible ** for virtual tables), do not include it in the expanded ** result-set list. @@ -3437,6 +3537,7 @@ static int selectExpander(Walker *pWalker, Select *p){ assert(IsVirtual(pTab)); continue; } + tableSeen = 1; if( i>0 && zTName==0 ){ if( (pFrom->jointype & JT_NATURAL)!=0 @@ -3459,6 +3560,10 @@ static int selectExpander(Walker *pWalker, Select *p){ Expr *pLeft; pLeft = sqlite3Expr(db, TK_ID, zTabName); pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); + if( zSchemaName ){ + pLeft = sqlite3Expr(db, TK_ID, zSchemaName); + pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0); + } if( longNames ){ zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName); zToFree = zColname; @@ -3470,6 +3575,18 @@ static int selectExpander(Walker *pWalker, Select *p){ sColname.z = zColname; sColname.n = sqlite3Strlen30(zColname); sqlite3ExprListSetName(pParse, pNew, &sColname, 0); + if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){ + struct ExprList_item *pX = &pNew->a[pNew->nExpr-1]; + if( pSub ){ + pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan); + testcase( pX->zSpan==0 ); + }else{ + pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s", + zSchemaName, zTabName, zColname); + testcase( pX->zSpan==0 ); + } + pX->bSpanIsTab = 1; + } sqlite3DbFree(db, zToFree); } } @@ -3522,10 +3639,13 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){ */ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ Walker w; - w.xSelectCallback = selectExpander; + memset(&w, 0, sizeof(w)); + w.xSelectCallback = convertCompoundSelectToSubquery; w.xExprCallback = exprWalkNoop; w.pParse = pParse; sqlite3WalkSelect(&w, pSelect); + w.xSelectCallback = selectExpander; + sqlite3WalkSelect(&w, pSelect); } @@ -3580,9 +3700,11 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ #ifndef SQLITE_OMIT_SUBQUERY Walker w; + memset(&w, 0, sizeof(w)); w.xSelectCallback = selectAddSubqueryTypeInfo; w.xExprCallback = exprWalkNoop; w.pParse = pParse; + w.bSelectDepthFirst = 1; sqlite3WalkSelect(&w, pSelect); #endif } @@ -3608,6 +3730,7 @@ void sqlite3SelectPrep( sqlite3 *db; if( NEVER(p==0) ) return; db = pParse->db; + if( db->mallocFailed ) return; if( p->selFlags & SF_HasTypeInfo ) return; sqlite3SelectExpand(pParse, p); if( pParse->nErr || db->mallocFailed ) return; @@ -3992,7 +4115,7 @@ int sqlite3Select( pItem->addrFillSub = topAddr+1; VdbeNoopComment((v, "materialize %s", pItem->pTab->zName)); if( pItem->isCorrelated==0 ){ - /* If the subquery is no correlated and if we are not inside of + /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlite3CodeOnce(pParse); @@ -4515,7 +4638,7 @@ int sqlite3Select( ** value of x, the only row required). ** ** A special flag must be passed to sqlite3WhereBegin() to slightly - ** modify behaviour as follows: + ** modify behavior as follows: ** ** + If the query is a "SELECT min(x)", then the loop coded by ** where.c should not iterate over any values with a NULL value @@ -4527,11 +4650,17 @@ int sqlite3Select( ** Refer to code and comments in where.c for details. */ ExprList *pMinMax = 0; - u8 flag = minMaxQuery(p); + u8 flag = WHERE_ORDERBY_NORMAL; + + assert( p->pGroupBy==0 ); + assert( flag==0 ); + if( p->pHaving==0 ){ + flag = minMaxQuery(&sAggInfo, &pMinMax); + } + assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) ); + if( flag ){ - assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) ); - assert( p->pEList->a[0].pExpr->x.pList->nExpr==1 ); - pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0); + pMinMax = sqlite3ExprListDup(db, pMinMax, 0); pDel = pMinMax; if( pMinMax && !db->mallocFailed ){ pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0; @@ -4687,7 +4816,10 @@ void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ sqlite3ExplainPrintf(pVdbe, "(null-select)"); return; } - while( p->pPrior ) p = p->pPrior; + while( p->pPrior ){ + p->pPrior->pNext = p; + p = p->pPrior; + } sqlite3ExplainPush(pVdbe); while( p ){ explainOneSelect(pVdbe, p); diff --git a/src/shell.c b/src/shell.c index 7dd741b..1be2871 100644 --- a/src/shell.c +++ b/src/shell.c @@ -90,7 +90,8 @@ static int enableTimer = 0; #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) -#if !defined(_WIN32) && !defined(WIN32) && !defined(_WRS_KERNEL) +#if !defined(_WIN32) && !defined(WIN32) && !defined(_WRS_KERNEL) \ + && !defined(__minux) #include #include @@ -1525,17 +1526,55 @@ static void resolve_backslashes(char *z){ ** Interpret zArg as a boolean value. Return either 0 or 1. */ static int booleanValue(char *zArg){ - int val = atoi(zArg); - int j; - for(j=0; zArg[j]; j++){ - zArg[j] = ToLower(zArg[j]); + int i; + for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} + if( i>0 && zArg[i]==0 ) return atoi(zArg); + if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){ + return 1; } - if( strcmp(zArg,"on")==0 ){ - val = 1; - }else if( strcmp(zArg,"yes")==0 ){ - val = 1; + if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ + return 0; } - return val; + fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", + zArg); + return 0; +} + +/* +** Interpret zArg as an integer value, possibly with suffixes. +*/ +static sqlite3_int64 integerValue(const char *zArg){ + sqlite3_int64 v = 0; + static const struct { char *zSuffix; int iMult; } aMult[] = { + { "KiB", 1024 }, + { "MiB", 1024*1024 }, + { "GiB", 1024*1024*1024 }, + { "KB", 1000 }, + { "MB", 1000000 }, + { "GB", 1000000000 }, + { "K", 1000 }, + { "M", 1000000 }, + { "G", 1000000000 }, + }; + int i; + int isNeg = 0; + if( zArg[0]=='-' ){ + isNeg = 1; + zArg++; + }else if( zArg[0]=='+' ){ + zArg++; + } + while( isdigit(zArg[0]) ){ + v = v*10 + zArg[0] - '0'; + zArg++; + } + for(i=0; i=3 && strncmp(azArg[0], "backup", n)==0 && nArg>1 && nArg<4){ - const char *zDestFile; - const char *zDb; + if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){ + const char *zDestFile = 0; + const char *zDb = 0; + const char *zKey = 0; sqlite3 *pDest; sqlite3_backup *pBackup; - if( nArg==2 ){ - zDestFile = azArg[1]; - zDb = "main"; - }else{ - zDestFile = azArg[2]; - zDb = azArg[1]; + int j; + for(j=1; jdb, zDb); if( pBackup==0 ){ @@ -1742,7 +1807,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ p->echoOn = booleanValue(azArg[1]); }else - if( c=='e' && strncmp(azArg[0], "exit", n)==0 && nArg==1 ){ + if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ + if( nArg>1 && (rc = atoi(azArg[1]))!=0 ) exit(rc); rc = 2; }else @@ -2212,8 +2278,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE lower(tbl_name) LIKE shellstatic()" " AND type!='meta' AND sql NOTNULL " - "ORDER BY substr(type,2,1), " - " CASE type WHEN 'view' THEN rowid ELSE name END", + "ORDER BY rowid", callback, &data, &zErrMsg); zShellStatic = 0; } @@ -2224,8 +2289,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ " FROM sqlite_master UNION ALL" " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" - "ORDER BY substr(type,2,1)," - " CASE type WHEN 'view' THEN rowid ELSE name END", + "ORDER BY rowid", callback, &data, &zErrMsg ); } @@ -2347,9 +2411,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){ for(i=0; iout, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); } - printf("\n"); + fprintf(p->out, "\n"); } } for(ii=0; iidb, opt); - printf("%d (0x%08x)\n", rc, rc); + fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); @@ -2419,7 +2483,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ case SQLITE_TESTCTRL_PRNG_RESET: if( nArg==2 ){ rc = sqlite3_test_control(testctrl); - printf("%d (0x%08x)\n", rc, rc); + fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes no options\n", azArg[1]); } @@ -2428,9 +2492,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){ /* sqlite3_test_control(int, uint) */ case SQLITE_TESTCTRL_PENDING_BYTE: if( nArg==3 ){ - unsigned int opt = (unsigned int)atoi(azArg[2]); + unsigned int opt = (unsigned int)integerValue(azArg[2]); rc = sqlite3_test_control(testctrl, opt); - printf("%d (0x%08x)\n", rc, rc); + fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single unsigned" " int option\n", azArg[1]); @@ -2443,7 +2507,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ if( nArg==3 ){ int opt = atoi(azArg[2]); rc = sqlite3_test_control(testctrl, opt); - printf("%d (0x%08x)\n", rc, rc); + fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); @@ -2456,7 +2520,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ if( nArg==3 ){ const char *opt = azArg[2]; rc = sqlite3_test_control(testctrl, opt); - printf("%d (0x%08x)\n", rc, rc); + fprintf(p->out, "%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); @@ -2501,7 +2565,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ }else if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ - printf("SQLite %s %s\n" /*extra-version-info*/, + fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); }else @@ -2511,7 +2575,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ - printf("%s\n", zVfsName); + fprintf(p->out, "%s\n", zVfsName); sqlite3_free(zVfsName); } } @@ -2520,7 +2584,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){ #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){ extern int sqlite3WhereTrace; - sqlite3WhereTrace = atoi(azArg[1]); + sqlite3WhereTrace = booleanValue(azArg[1]); }else #endif @@ -2706,6 +2770,10 @@ static int process_input(struct callback_data *p, FILE *in){ free(zSql); zSql = 0; nSql = 0; + }else if( zSql && _all_whitespace(zSql) ){ + free(zSql); + zSql = 0; + nSql = 0; } } if( zSql ){ @@ -2841,6 +2909,7 @@ static const char zOptions[] = " -interactive force interactive I/O\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" + " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif @@ -2960,12 +3029,7 @@ int main(int argc, char **argv){ sqlite3_int64 szHeap; zSize = cmdline_option_value(argc, argv, ++i); - szHeap = atoi(zSize); - for(j=0; (c = zSize[j])!=0; j++){ - if( c=='M' ){ szHeap *= 1000000; break; } - if( c=='K' ){ szHeap *= 1000; break; } - if( c=='G' ){ szHeap *= 1000000000; break; } - } + szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif @@ -2985,6 +3049,9 @@ int main(int argc, char **argv){ extern int sqlite3_multiple_initialize(const char*,int); sqlite3_multiplex_initialize(0, 1); #endif + }else if( strcmp(z,"-mmap")==0 ){ + sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); + sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz); }else if( strcmp(z,"-vfs")==0 ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i)); if( pVfs ){ @@ -3070,6 +3137,8 @@ int main(int argc, char **argv){ stdin_is_interactive = 0; }else if( strcmp(z,"-heap")==0 ){ i++; + }else if( strcmp(z,"-mmap")==0 ){ + i++; }else if( strcmp(z,"-vfs")==0 ){ i++; #ifdef SQLITE_ENABLE_VFSTRACE @@ -3087,7 +3156,7 @@ int main(int argc, char **argv){ z = cmdline_option_value(argc,argv,++i); if( z[0]=='.' ){ rc = do_meta_command(z, &data); - if( rc && bail_on_error ) return rc; + if( rc && bail_on_error ) return rc==2 ? 0 : rc; }else{ open_db(&data); rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg); @@ -3111,6 +3180,7 @@ int main(int argc, char **argv){ */ if( zFirstCmd[0]=='.' ){ rc = do_meta_command(zFirstCmd, &data); + if( rc==2 ) rc = 0; }else{ open_db(&data); rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 48f7381..6608823 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -283,7 +283,7 @@ typedef sqlite_uint64 sqlite3_uint64; ** [sqlite3_blob_close | close] all [BLOB handles], and ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated ** with the [sqlite3] object prior to attempting to close the object. ^If -** sqlite3_close() is called on a [database connection] that still has +** sqlite3_close_v2() is called on a [database connection] that still has ** outstanding [prepared statements], [BLOB handles], and/or ** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation ** of resources is deferred until all [prepared statements], [BLOB handles], @@ -420,6 +420,8 @@ int sqlite3_exec( #define SQLITE_FORMAT 24 /* Auxiliary database format error */ #define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ #define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ +#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ @@ -470,6 +472,7 @@ int sqlite3_exec( #define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) #define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) #define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) +#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) @@ -478,7 +481,19 @@ int sqlite3_exec( #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) +#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8)) #define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) +#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8)) +#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8)) +#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8)) +#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8)) +#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8)) +#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8)) +#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8)) +#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8)) +#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8)) +#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) +#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) /* ** CAPI3REF: Flags For File Open Operations @@ -718,6 +733,9 @@ struct sqlite3_io_methods { void (*xShmBarrier)(sqlite3_file*); int (*xShmUnmap)(sqlite3_file*, int deleteFlag); /* Methods above are valid for version 2 */ + int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); + int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p); + /* Methods above are valid for version 3 */ /* Additional methods may be added in future releases */ }; @@ -854,7 +872,8 @@ struct sqlite3_io_methods { ** it is able to override built-in [PRAGMA] statements. ** **
  • [[SQLITE_FCNTL_BUSYHANDLER]] -** ^This file-control may be invoked by SQLite on the database file handle +** ^The [SQLITE_FCNTL_BUSYHANDLER] +** file-control may be invoked by SQLite on the database file handle ** shortly after it is opened in order to provide a custom VFS with access ** to the connections busy-handler callback. The argument is of type (void **) ** - an array of two (void *) values. The first (void *) actually points @@ -865,13 +884,24 @@ struct sqlite3_io_methods { ** current operation. ** **
  • [[SQLITE_FCNTL_TEMPFILENAME]] -** ^Application can invoke this file-control to have SQLite generate a +** ^Application can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control +** to have SQLite generate a ** temporary filename using the same algorithm that is followed to generate ** temporary filenames for TEMP tables and other internal uses. The ** argument should be a char** which will be filled with the filename ** written into memory obtained from [sqlite3_malloc()]. The caller should ** invoke [sqlite3_free()] on the result to avoid a memory leak. ** +**
  • [[SQLITE_FCNTL_MMAP_SIZE]] +** The [SQLITE_FCNTL_MMAP_SIZE] file control is used to query or set the +** maximum number of bytes that will be used for memory-mapped I/O. +** The argument is a pointer to a value of type sqlite3_int64 that +** is an advisory maximum number of bytes in the file to memory map. The +** pointer is overwritten with the old value. The limit is not changed if +** the value originally pointed to is negative, and so the current limit +** can be queried by passing in a pointer to a negative number. This +** file-control is used internally to implement [PRAGMA mmap_size]. +** ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -890,6 +920,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_PRAGMA 14 #define SQLITE_FCNTL_BUSYHANDLER 15 #define SQLITE_FCNTL_TEMPFILENAME 16 +#define SQLITE_FCNTL_MMAP_SIZE 18 /* ** CAPI3REF: Mutex Handle @@ -1556,7 +1587,9 @@ struct sqlite3_mem_methods { ** page cache implementation into that object.)^ ** ** [[SQLITE_CONFIG_LOG]]
    SQLITE_CONFIG_LOG
    -**
    ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a +**
    The SQLITE_CONFIG_LOG option is used to configure the SQLite +** global [error log]. +** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), ** and a pointer to void. ^If the function pointer is not NULL, it is ** invoked by [sqlite3_log()] to process each logging event. ^If the @@ -1602,12 +1635,12 @@ struct sqlite3_mem_methods { **
    SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE **
    These options are obsolete and should not be used by new code. ** They are retained for backwards compatibility but are now no-ops. -** +**
    ** ** [[SQLITE_CONFIG_SQLLOG]] **
    SQLITE_CONFIG_SQLLOG **
    This option is only available if sqlite is compiled with the -** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should +** [SQLITE_ENABLE_SQLLOG] pre-processor macro defined. The first argument should ** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int). ** The second should be of type (void*). The callback is invoked by the library ** in three separate circumstances, identified by the value passed as the @@ -1617,7 +1650,23 @@ struct sqlite3_mem_methods { ** fourth parameter is 1, then the SQL statement that the third parameter ** points to has just been executed. Or, if the fourth parameter is 2, then ** the connection being passed as the second parameter is being closed. The -** third parameter is passed NULL In this case. +** third parameter is passed NULL In this case. An example of using this +** configuration option can be seen in the "test_sqllog.c" source file in +** the canonical SQLite source tree.
    +** +** [[SQLITE_CONFIG_MMAP_SIZE]] +**
    SQLITE_CONFIG_MMAP_SIZE +**
    SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values +** that are the default mmap size limit (the default setting for +** [PRAGMA mmap_size]) and the maximum allowed mmap size limit. +** The default setting can be overridden by each database connection using +** either the [PRAGMA mmap_size] command, or by using the +** [SQLITE_FCNTL_MMAP_SIZE] file control. The maximum allowed mmap size +** cannot be changed at run-time. Nor may the maximum allowed mmap size +** exceed the compile-time maximum mmap size set by the +** [SQLITE_MAX_MMAP_SIZE] compile-time option. +** If either argument to this option is negative, then that argument is +** changed to its compile-time default. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1641,6 +1690,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #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 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -2474,6 +2524,9 @@ int sqlite3_set_authorizer( ** as each triggered subprogram is entered. The callbacks for triggers ** contain a UTF-8 SQL comment that identifies the trigger.)^ ** +** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit +** the length of [bound parameter] expansion in the output of sqlite3_trace(). +** ** ^The callback function registered by sqlite3_profile() is invoked ** as each SQL statement finishes. ^The profile callback contains ** the original statement text and an estimate of wall-clock time @@ -2665,7 +2718,7 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** sqlite3_open_v2(). ^Setting the cache parameter to "private" is ** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in -** a URI filename, its value overrides any behaviour requested by setting +** a URI filename, its value overrides any behavior requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. ** ** @@ -3012,7 +3065,8 @@ int sqlite3_limit(sqlite3*, int id, int newVal); **
  • ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. +** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY] +** retries will occur before sqlite3_step() gives up and returns an error. **
  • ** **
  • @@ -3216,6 +3270,9 @@ typedef struct sqlite3_context sqlite3_context; ** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). ** ** ^The third argument is the value to bind to the parameter. +** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16() +** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter +** is ignored and the end result is the same as sqlite3_bind_null(). ** ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the @@ -3983,7 +4040,8 @@ SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); SQLITE_DEPRECATED int sqlite3_global_recover(void); SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64); +SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int), + void*,sqlite3_int64); #endif /* @@ -4063,14 +4121,17 @@ int sqlite3_value_numeric_type(sqlite3_value*); ** In those cases, sqlite3_aggregate_context() might be called for the ** first time from within xFinal().)^ ** -** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is -** less than or equal to zero or if a memory allocate error occurs. +** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer +** when first called if N is less than or equal to zero or if a memory +** allocate error occurs. ** ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is ** determined by the N parameter on first successful call. Changing the ** value of N in subsequent call to sqlite3_aggregate_context() within ** the same aggregate function instance will not resize the memory -** allocation.)^ +** allocation.)^ Within the xFinal callback, it is customary to set +** N=0 in calls to sqlite3_aggregate_context(C,N) so that no +** pointless memory allocations occur. ** ** ^SQLite automatically frees the memory allocated by ** sqlite3_aggregate_context() when the aggregate query concludes. @@ -4168,7 +4229,7 @@ void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); ** the content before returning. ** ** The typedef is necessary to work around problems in certain -** C++ compilers. See ticket #2191. +** C++ compilers. */ typedef void (*sqlite3_destructor_type)(void*); #define SQLITE_STATIC ((sqlite3_destructor_type)0) @@ -4967,11 +5028,20 @@ int sqlite3_table_column_metadata( ** ^This interface loads an SQLite extension library from the named file. ** ** ^The sqlite3_load_extension() interface attempts to load an -** SQLite extension library contained in the file zFile. +** [SQLite extension] library contained in the file zFile. If +** the file cannot be loaded directly, attempts are made to load +** with various operating-system specific extensions added. +** So for example, if "samplelib" cannot be loaded, then names like +** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might +** be tried also. ** ** ^The entry point is zProc. -** ^zProc may be 0, in which case the name of the entry point -** defaults to "sqlite3_extension_init". +** ^(zProc may be 0, in which case SQLite will try to come up with an +** entry point name on its own. It first tries "sqlite3_extension_init". +** If that does not work, it constructs a name "sqlite3_X_init" where the +** X is consists of the lower-case equivalent of all ASCII alphabetic +** characters in the filename from the last "/" to the first following +** "." and omitting any initial "lib".)^ ** ^The sqlite3_load_extension() interface returns ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. ** ^If an error occurs and pzErrMsg is not 0, then the @@ -4997,11 +5067,11 @@ int sqlite3_load_extension( ** CAPI3REF: Enable Or Disable Extension Loading ** ** ^So as not to open security holes in older applications that are -** unprepared to deal with extension loading, and as a means of disabling -** extension loading while evaluating user-entered SQL, the following API +** unprepared to deal with [extension loading], and as a means of disabling +** [extension loading] while evaluating user-entered SQL, the following API ** is provided to turn the [sqlite3_load_extension()] mechanism on and off. ** -** ^Extension loading is off by default. See ticket #1863. +** ^Extension loading is off by default. ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. @@ -5013,7 +5083,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff); ** ** ^This interface causes the xEntryPoint() function to be invoked for ** each new [database connection] that is created. The idea here is that -** xEntryPoint() is the entry point for a statically linked SQLite extension +** xEntryPoint() is the entry point for a statically linked [SQLite extension] ** that is to be automatically loaded into all new database connections. ** ** ^(Even though the function prototype shows that xEntryPoint() takes @@ -6364,7 +6434,7 @@ struct sqlite3_pcache_page { ** parameter to help it determined what action to take: ** ** -**
    createFlag Behaviour when page is not already in cache +**
    createFlag Behavior when page is not already in cache **
    0 Do not allocate a new page. Return NULL. **
    1 Allocate a new page if it easy and convenient to do so. ** Otherwise return NULL. @@ -6793,10 +6863,25 @@ int sqlite3_unlock_notify( int sqlite3_stricmp(const char *, const char *); int sqlite3_strnicmp(const char *, const char *, int); +/* +** CAPI3REF: String Globbing +* +** ^The [sqlite3_strglob(P,X)] interface returns zero if string X matches +** the glob pattern P, and it returns non-zero if string X does not match +** the glob pattern P. ^The definition of glob pattern matching used in +** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the +** SQL dialect used by SQLite. ^The sqlite3_strglob(P,X) function is case +** sensitive. +** +** Note that this routine returns zero on a match and non-zero if the strings +** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()]. +*/ +int sqlite3_strglob(const char *zGlob, const char *zStr); + /* ** CAPI3REF: Error Logging Interface ** -** ^The [sqlite3_log()] interface writes a message into the error log +** ^The [sqlite3_log()] interface writes a message into the [error log] ** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. ** ^If logging is enabled, the zFormat string and subsequent arguments are ** used with [sqlite3_snprintf()] to generate the final output string. diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 5abcde2..928bb3b 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -236,6 +236,20 @@ struct sqlite3_api_routines { int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); int (*vtab_config)(sqlite3*,int op,...); int (*vtab_on_conflict)(sqlite3*); + /* Version 3.7.16 and later */ + int (*close_v2)(sqlite3*); + const char *(*db_filename)(sqlite3*,const char*); + int (*db_readonly)(sqlite3*,const char*); + int (*db_release_memory)(sqlite3*); + const char *(*errstr)(int); + int (*stmt_busy)(sqlite3_stmt*); + int (*stmt_readonly)(sqlite3_stmt*); + int (*stricmp)(const char*,const char*); + int (*uri_boolean)(const char*,const char*,int); + sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); + const char *(*uri_parameter)(const char*,const char*); + char *(*vsnprintf)(int,char*,const char*,va_list); + int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); }; /* @@ -439,9 +453,32 @@ struct sqlite3_api_routines { #define sqlite3_blob_reopen sqlite3_api->blob_reopen #define sqlite3_vtab_config sqlite3_api->vtab_config #define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict +/* Version 3.7.16 and later */ +#define sqlite3_close_v2 sqlite3_api->close_v2 +#define sqlite3_db_filename sqlite3_api->db_filename +#define sqlite3_db_readonly sqlite3_api->db_readonly +#define sqlite3_db_release_memory sqlite3_api->db_release_memory +#define sqlite3_errstr sqlite3_api->errstr +#define sqlite3_stmt_busy sqlite3_api->stmt_busy +#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly +#define sqlite3_stricmp sqlite3_api->stricmp +#define sqlite3_uri_boolean sqlite3_api->uri_boolean +#define sqlite3_uri_int64 sqlite3_api->uri_int64 +#define sqlite3_uri_parameter sqlite3_api->uri_parameter +#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf +#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 #endif /* SQLITE_CORE */ -#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; -#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v; +#ifndef SQLITE_CORE + /* This case when the file really is being compiled as a loadable + ** extension */ +# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; +# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; +#else + /* This case when the file is being statically linked into the + ** application */ +# define SQLITE_EXTENSION_INIT1 /*no-op*/ +# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ +#endif #endif /* _SQLITE3EXT_H_ */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2b58a80..5950f23 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -66,6 +66,10 @@ # define _GNU_SOURCE #endif +#if defined(__OpenBSD__) && !defined(_BSD_SOURCE) +# define _BSD_SOURCE +#endif + /* ** Include standard header files as necessary */ @@ -118,11 +122,11 @@ ** We support that for legacy. */ #if !defined(SQLITE_THREADSAFE) -#if defined(THREADSAFE) -# define SQLITE_THREADSAFE THREADSAFE -#else -# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ -#endif +# if defined(THREADSAFE) +# define SQLITE_THREADSAFE THREADSAFE +# else +# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ +# endif #endif /* @@ -200,7 +204,8 @@ ** ** See also ticket #2741. */ -#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__) && SQLITE_THREADSAFE +#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) \ + && !defined(__APPLE__) && SQLITE_THREADSAFE # define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */ #endif @@ -387,6 +392,7 @@ */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 1 +# define SQLITE_TEMP_STORE_xc 1 /* Exclude from ctime.c */ #endif /* @@ -534,6 +540,49 @@ extern const int sqlite3one; # define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) #endif +/* +** Disable MMAP on platforms where it is known to not work +*/ +#if defined(__OpenBSD__) || defined(__QNXNTO__) +# undef SQLITE_MAX_MMAP_SIZE +# define SQLITE_MAX_MMAP_SIZE 0 +#endif + +/* +** Default maximum size of memory used by memory-mapped I/O in the VFS +*/ +#ifdef __APPLE__ +# include +# if TARGET_OS_IPHONE +# undef SQLITE_MAX_MMAP_SIZE +# define SQLITE_MAX_MMAP_SIZE 0 +# endif +#endif +#ifndef SQLITE_MAX_MMAP_SIZE +# if defined(__linux__) \ + || defined(_WIN32) \ + || (defined(__APPLE__) && defined(__MACH__)) \ + || defined(__sun) +# define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */ +# else +# define SQLITE_MAX_MMAP_SIZE 0 +# endif +# define SQLITE_MAX_MMAP_SIZE_xc 1 /* exclude from ctime.c */ +#endif + +/* +** The default MMAP_SIZE is zero on all platforms. Or, even if a larger +** default MMAP_SIZE is specified at compile-time, make sure that it does +** not exceed the maximum mmap size. +*/ +#ifndef SQLITE_DEFAULT_MMAP_SIZE +# define SQLITE_DEFAULT_MMAP_SIZE 0 +# define SQLITE_DEFAULT_MMAP_SIZE_xc 1 /* Exclude from ctime.c */ +#endif +#if SQLITE_DEFAULT_MMAP_SIZE>SQLITE_MAX_MMAP_SIZE +# undef SQLITE_DEFAULT_MMAP_SIZE +# define SQLITE_DEFAULT_MMAP_SIZE SQLITE_MAX_MMAP_SIZE +#endif /* ** An instance of the following structure is used to store the busy-handler @@ -575,6 +624,11 @@ struct BusyHandler { */ #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) +/* +** Determine if the argument is a power of two +*/ +#define IsPowerOfTwo(X) (((X)&((X)-1))==0) + /* ** The following value as a destructor means to use sqlite3DbFree(). ** The sqlite3DbFree() routine requires two parameters instead of the @@ -824,6 +878,7 @@ struct sqlite3 { int nDb; /* Number of backends currently in use */ int flags; /* Miscellaneous flags. See below */ i64 lastRowid; /* ROWID of most recent insert (see above) */ + i64 szMmap; /* Default mmap_size setting */ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ @@ -944,7 +999,7 @@ struct sqlite3 { #define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */ #define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */ #define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */ - /* 0x00000200 Unused */ +#define SQLITE_VdbeAddopTrace 0x00000200 /* Trace sqlite3VdbeAddOp() calls */ #define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */ #define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */ #define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */ @@ -973,6 +1028,7 @@ struct sqlite3 { #define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ #define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ +#define SQLITE_Transitive 0x0200 /* Transitive constraints */ #define SQLITE_AllOpts 0xffff /* All optimizations */ /* @@ -1484,20 +1540,20 @@ struct UnpackedRecord { ** element. */ struct Index { - char *zName; /* Name of this index */ - int *aiColumn; /* Which columns are used by this index. 1st is 0 */ - tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ - Table *pTable; /* The SQL table being indexed */ - char *zColAff; /* String defining the affinity of each column */ - Index *pNext; /* The next index associated with the same table */ - Schema *pSchema; /* Schema containing this index */ - u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ - char **azColl; /* Array of collation sequence names for index */ - int nColumn; /* Number of columns in the table used by this index */ - int tnum; /* Page containing root of this index in database file */ - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ - u8 bUnordered; /* Use this index for == or IN queries only */ + char *zName; /* Name of this index */ + int *aiColumn; /* Which columns are used by this index. 1st is 0 */ + tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */ + Table *pTable; /* The SQL table being indexed */ + char *zColAff; /* String defining the affinity of each column */ + Index *pNext; /* The next index associated with the same table */ + Schema *pSchema; /* Schema containing this index */ + u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ + char **azColl; /* Array of collation sequence names for index */ + int tnum; /* DB Page containing root of this index */ + u16 nColumn; /* Number of columns in table used by this index */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ + unsigned bUnordered:1; /* Use this index for == or IN queries only */ #ifdef SQLITE_ENABLE_STAT3 int nSample; /* Number of elements in aSample[] */ tRowcnt avgEq; /* Average nEq value for key values not in aSample */ @@ -1771,18 +1827,27 @@ struct Expr { ** list of "ID = expr" items in an UPDATE. A list of expressions can ** also be used as the argument to a function, in which case the a.zName ** field is not used. +** +** By default the Expr.zSpan field holds a human-readable description of +** the expression that is used in the generation of error messages and +** column labels. In this case, Expr.zSpan is typically the text of a +** column expression as it exists in a SELECT statement. However, if +** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name +** of the result column in the form: DATABASE.TABLE.COLUMN. This later +** form is used for name resolution with nested FROM clauses. */ struct ExprList { int nExpr; /* Number of expressions on the list */ int iECursor; /* VDBE Cursor associated with this ExprList */ struct ExprList_item { /* For each expression in the list */ - Expr *pExpr; /* The list of expressions */ - char *zName; /* Token associated with this expression */ - char *zSpan; /* Original text of the expression */ - u8 sortOrder; /* 1 for DESC or 0 for ASC */ - u8 done; /* A flag to indicate when processing is finished */ - u16 iOrderByCol; /* For ORDER BY, column number in result set */ - u16 iAlias; /* Index into Parse.aAlias[] for zName */ + Expr *pExpr; /* The list of expressions */ + char *zName; /* Token associated with this expression */ + char *zSpan; /* Original text of the expression */ + u8 sortOrder; /* 1 for DESC or 0 for ASC */ + unsigned done :1; /* A flag to indicate when processing is finished */ + unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */ + u16 iOrderByCol; /* For ORDER BY, column number in result set */ + u16 iAlias; /* Index into Parse.aAlias[] for zName */ } *a; /* Alloc a power of two greater or equal to nExpr */ }; @@ -1950,6 +2015,7 @@ struct WhereLevel { struct InLoop { int iCur; /* The VDBE cursor used by this IN operator */ int addrInTop; /* Top of the IN loop */ + u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */ } *aInLoop; /* Information about each nested IN operator */ } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ @@ -2049,6 +2115,8 @@ struct NameContext { #define NC_HasAgg 0x02 /* One or more aggregate functions seen */ #define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */ #define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */ +#define NC_AsMaybe 0x10 /* Resolve to AS terms of the result set only + ** if no other resolution is available */ /* ** An instance of the following structure contains all information @@ -2102,6 +2170,7 @@ struct Select { #define SF_UseSorter 0x0040 /* Sort using a sorter */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ #define SF_Materialize 0x0100 /* Force materialization of views */ +#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ /* @@ -2483,6 +2552,8 @@ struct Sqlite3Config { void *pHeap; /* Heap storage space */ int nHeap; /* Size of pHeap[] */ int mnReq, mxReq; /* Min and max heap requests sizes */ + sqlite3_int64 szMmap; /* mmap() space per open file */ + sqlite3_int64 mxMmap; /* Maximum value for szMmap */ void *pScratch; /* Scratch memory */ int szScratch; /* Size of each scratch buffer */ int nScratch; /* Number of scratch buffers */ @@ -2517,6 +2588,7 @@ struct Walker { int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ Parse *pParse; /* Parser context. */ int walkerDepth; /* Number of subqueries */ + u8 bSelectDepthFirst; /* Do subqueries first */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ @@ -2814,13 +2886,13 @@ Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, void sqlite3DropIndex(Parse*, SrcList*, int); int sqlite3Select(Parse*, Select*, SelectDest*); Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, - Expr*,ExprList*,int,Expr*,Expr*); + Expr*,ExprList*,u16,Expr*,Expr*); void sqlite3SelectDelete(sqlite3*, Select*); Table *sqlite3SrcListLookup(Parse*, SrcList*); int sqlite3IsReadOnly(Parse*, Table*, int); void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) -Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *); +Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*); #endif void sqlite3DeleteFrom(Parse*, SrcList*, Expr*); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); @@ -2888,7 +2960,7 @@ int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); void sqlite3BeginWriteOperation(Parse*, int, int); void sqlite3MultiWrite(Parse*); void sqlite3MayAbort(Parse*); -void sqlite3HaltConstraint(Parse*, int, char*, int); +void sqlite3HaltConstraint(Parse*, int, int, char*, int); Expr *sqlite3ExprDup(sqlite3*,Expr*,int); ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); @@ -3001,8 +3073,11 @@ int sqlite3VarintLen(u64 v); ** x = putVarint32( A, B ); ** */ -#define getVarint32(A,B) (u8)((*(A)<(u8)0x80) ? ((B) = (u32)*(A)),1 : sqlite3GetVarint32((A), (u32 *)&(B))) -#define putVarint32(A,B) (u8)(((u32)(B)<(u32)0x80) ? (*(A) = (unsigned char)(B)),1 : sqlite3PutVarint32((A), (B))) +#define getVarint32(A,B) \ + (u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite3GetVarint32((A),(u32 *)&(B))) +#define putVarint32(A,B) \ + (u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1:\ + sqlite3PutVarint32((A),(B))) #define getVarint sqlite3GetVarint #define putVarint sqlite3PutVarint @@ -3017,6 +3092,12 @@ void sqlite3Error(sqlite3*, int, const char*,...); void *sqlite3HexToBlob(sqlite3*, const char *z, int n); u8 sqlite3HexToInt(int h); int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); + +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \ + defined(SQLITE_DEBUG_OS_TRACE) +const char *sqlite3ErrName(int); +#endif + const char *sqlite3ErrStr(int); int sqlite3ReadSchema(Parse *pParse); CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); @@ -3071,6 +3152,7 @@ void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); int sqlite3CodeSubselect(Parse *, Expr *, int, int); void sqlite3SelectPrep(Parse*, Select*, NameContext*); +int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); int sqlite3ResolveExprNames(NameContext*, Expr*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); @@ -3209,8 +3291,10 @@ const char *sqlite3JournalModename(int); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite3FkDelete(sqlite3 *, Table*); + int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**); #else #define sqlite3FkDelete(a,b) + #define sqlite3FkLocateIndex(a,b,c,d,e) #endif @@ -3235,7 +3319,8 @@ const char *sqlite3JournalModename(int); #define IN_INDEX_ROWID 1 #define IN_INDEX_EPH 2 -#define IN_INDEX_INDEX 3 +#define IN_INDEX_INDEX_ASC 3 +#define IN_INDEX_INDEX_DESC 4 int sqlite3FindInIndex(Parse *, Expr *, int*); #ifdef SQLITE_ENABLE_ATOMIC_WRITE diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 57dab85..f1bb292 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -1005,7 +1005,7 @@ static int DbTransPostCmd( /* This is a tricky scenario to handle. The most likely cause of an ** error is that the exec() above was an attempt to commit the ** top-level transaction that returned SQLITE_BUSY. Or, less likely, - ** that an IO-error has occured. In either case, throw a Tcl exception + ** that an IO-error has occurred. In either case, throw a Tcl exception ** and try to rollback the transaction. ** ** But it could also be that the user executed one or more BEGIN, @@ -3671,6 +3671,7 @@ static void init_all(Tcl_Interp *interp){ extern int Sqlitetestschema_Init(Tcl_Interp*); extern int Sqlitetestsse_Init(Tcl_Interp*); extern int Sqlitetesttclvar_Init(Tcl_Interp*); + extern int Sqlitetestfs_Init(Tcl_Interp*); extern int SqlitetestThread_Init(Tcl_Interp*); extern int SqlitetestOnefile_Init(); extern int SqlitetestOsinst_Init(Tcl_Interp*); @@ -3682,8 +3683,6 @@ static void init_all(Tcl_Interp *interp){ extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); extern int SqlitetestSyscall_Init(Tcl_Interp*); - extern int Sqlitetestfuzzer_Init(Tcl_Interp*); - extern int Sqlitetestwholenumber_Init(Tcl_Interp*); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) extern int Sqlitetestfts3_Init(Tcl_Interp *interp); @@ -3714,6 +3713,7 @@ static void init_all(Tcl_Interp *interp){ Sqlitetest_mutex_Init(interp); Sqlitetestschema_Init(interp); Sqlitetesttclvar_Init(interp); + Sqlitetestfs_Init(interp); SqlitetestThread_Init(interp); SqlitetestOnefile_Init(interp); SqlitetestOsinst_Init(interp); @@ -3725,8 +3725,6 @@ static void init_all(Tcl_Interp *interp){ Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); - Sqlitetestfuzzer_Init(interp); - Sqlitetestwholenumber_Init(interp); #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) Sqlitetestfts3_Init(interp); diff --git a/src/test1.c b/src/test1.c index bb8d186..a638e48 100644 --- a/src/test1.c +++ b/src/test1.c @@ -113,64 +113,8 @@ int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){ return TCL_OK; } - -const char *sqlite3TestErrorName(int rc){ - const char *zName = 0; - switch( rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; - case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; - case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break; - case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; - case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; - case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; - case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; - case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; - case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; - case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; - case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break; - case SQLITE_ROW: zName = "SQLITE_ROW"; break; - case SQLITE_DONE: zName = "SQLITE_DONE"; break; - case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break; - case SQLITE_IOERR_SHORT_READ: zName = "SQLITE_IOERR_SHORT_READ"; break; - case SQLITE_IOERR_WRITE: zName = "SQLITE_IOERR_WRITE"; break; - case SQLITE_IOERR_FSYNC: zName = "SQLITE_IOERR_FSYNC"; break; - case SQLITE_IOERR_DIR_FSYNC: zName = "SQLITE_IOERR_DIR_FSYNC"; break; - case SQLITE_IOERR_TRUNCATE: zName = "SQLITE_IOERR_TRUNCATE"; break; - case SQLITE_IOERR_FSTAT: zName = "SQLITE_IOERR_FSTAT"; break; - case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break; - case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break; - case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break; - case SQLITE_IOERR_BLOCKED: zName = "SQLITE_IOERR_BLOCKED"; break; - case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break; - case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break; - case SQLITE_IOERR_CHECKRESERVEDLOCK: - zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; - case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break; - case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break; - case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break; - case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break; - default: zName = "SQLITE_Unknown"; break; - } - return zName; -} -#define t1ErrorName sqlite3TestErrorName +extern const char *sqlite3ErrName(int); +#define t1ErrorName sqlite3ErrName /* ** Convert an sqlite3_stmt* into an sqlite3*. This depends on the @@ -737,6 +681,30 @@ static int sqlite_test_close( return TCL_OK; } +/* +** Usage: sqlite3_close_v2 DB +** +** Closes the database opened by sqlite3_open. +*/ +static int sqlite_test_close_v2( + void *NotUsed, + Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ + int argc, /* Number of arguments */ + char **argv /* Text of each argument */ +){ + sqlite3 *db; + int rc; + if( argc!=2 ){ + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " FILENAME\"", 0); + return TCL_ERROR; + } + if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; + rc = sqlite3_close_v2(db); + Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); + return TCL_OK; +} + /* ** Implementation of the x_coalesce() function. ** Return the first argument non-NULL argument. @@ -1715,7 +1683,7 @@ static int test_blob_read( if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte)); }else{ - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); } Tcl_Free((char *)zBuf); @@ -1765,7 +1733,7 @@ static int test_blob_write( } rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset); if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); } return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); @@ -1791,7 +1759,7 @@ static int test_blob_reopen( rc = sqlite3_blob_reopen(pBlob, iRowid); if( rc!=SQLITE_OK ){ - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); } return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); @@ -2001,7 +1969,7 @@ static int test_create_function_v2( ); if( rc!=SQLITE_OK ){ Tcl_ResetResult(interp); - Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -2677,7 +2645,7 @@ static int test_collate( if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -3235,7 +3203,7 @@ static int test_bind_text( rc = sqlite3_bind_text(pStmt, idx, value, bytes, SQLITE_TRANSIENT); if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } @@ -3283,7 +3251,7 @@ static int test_bind_text16( rc = sqlite3_bind_text16(pStmt, idx, (void *)value, bytes, xDel); if( sqlite3TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } @@ -4557,7 +4525,7 @@ static int test_busy_timeout( if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &ms) ) return TCL_ERROR; rc = sqlite3_busy_timeout(db, ms); - Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_OK; } @@ -5078,7 +5046,7 @@ static int file_control_chunksize_test( rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_CHUNK_SIZE, (void *)&nSize); if( rc ){ - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_ERROR; } return TCL_OK; @@ -5115,7 +5083,7 @@ static int file_control_sizehint_test( rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize); if( rc ){ - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_ERROR; } return TCL_OK; @@ -5659,7 +5627,7 @@ static void xLogcallback(void *unused, int err, char *zMsg){ Tcl_Obj *pNew = Tcl_DuplicateObj(logcallback.pObj); Tcl_IncrRefCount(pNew); Tcl_ListObjAppendElement( - 0, pNew, Tcl_NewStringObj(sqlite3TestErrorName(err), -1) + 0, pNew, Tcl_NewStringObj(sqlite3ErrName(err), -1) ); Tcl_ListObjAppendElement(0, pNew, Tcl_NewStringObj(zMsg, -1)); Tcl_EvalObjEx(logcallback.pInterp, pNew, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT); @@ -5831,6 +5799,31 @@ static int test_test_control( return TCL_OK; } +#if SQLITE_OS_UNIX +#include +#include + +static int test_getrusage( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + char buf[1024]; + struct rusage r; + memset(&r, 0, sizeof(r)); + getrusage(RUSAGE_SELF, &r); + + sprintf(buf, "ru_utime=%d.%06d ru_stime=%d.%06d ru_minflt=%d ru_majflt=%d", + (int)r.ru_utime.tv_sec, (int)r.ru_utime.tv_usec, + (int)r.ru_stime.tv_sec, (int)r.ru_stime.tv_usec, + (int)r.ru_minflt, (int)r.ru_majflt + ); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); + return TCL_OK; +} +#endif + #if SQLITE_OS_WIN /* ** Information passed from the main thread into the windows file locker @@ -6002,6 +5995,69 @@ static int optimization_control( return TCL_OK; } +typedef struct sqlite3_api_routines sqlite3_api_routines; +/* +** load_static_extension DB NAME ... +** +** Load one or more statically linked extensions. +*/ +static int tclLoadStaticExtensionCmd( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); + static const struct { + const char *zExtName; + int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); + } aExtension[] = { + { "amatch", sqlite3_amatch_init }, + { "closure", sqlite3_closure_init }, + { "fuzzer", sqlite3_fuzzer_init }, + { "ieee754", sqlite3_ieee_init }, + { "nextchar", sqlite3_nextchar_init }, + { "regexp", sqlite3_regexp_init }, + { "spellfix", sqlite3_spellfix_init }, + { "wholenumber", sqlite3_wholenumber_init }, + }; + sqlite3 *db; + const char *zName; + int i, j, rc; + char *zErrMsg = 0; + if( objc<3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DB NAME ..."); + return TCL_ERROR; + } + if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; + for(j=2; j=ArraySize(aExtension) ){ + Tcl_AppendResult(interp, "no such extension: ", zName, (char*)0); + return TCL_ERROR; + } + rc = aExtension[i].pInit(db, &zErrMsg, 0); + if( rc!=SQLITE_OK || zErrMsg ){ + Tcl_AppendResult(interp, "initialization of ", zName, " failed: ", zErrMsg, + (char*)0); + sqlite3_free(zErrMsg); + return TCL_ERROR; + } + } + return TCL_OK; +} + + /* ** Register commands with the TCL interpreter. */ @@ -6045,6 +6101,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_get_table_printf", (Tcl_CmdProc*)test_get_table_printf }, #endif { "sqlite3_close", (Tcl_CmdProc*)sqlite_test_close }, + { "sqlite3_close_v2", (Tcl_CmdProc*)sqlite_test_close_v2 }, { "sqlite3_create_function", (Tcl_CmdProc*)test_create_function }, { "sqlite3_create_aggregate", (Tcl_CmdProc*)test_create_aggregate }, { "sqlite_register_test_function", (Tcl_CmdProc*)test_register_func }, @@ -6220,6 +6277,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "print_explain_query_plan", test_print_eqp, 0 }, #endif { "sqlite3_test_control", test_test_control }, +#if SQLITE_OS_UNIX + { "getrusage", test_getrusage }, +#endif + { "load_static_extension", tclLoadStaticExtensionCmd }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; @@ -6236,7 +6297,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ #ifdef SQLITE_DEBUG extern int sqlite3WhereTrace; extern int sqlite3OSTrace; - extern int sqlite3VdbeAddopTrace; extern int sqlite3WalTrace; #endif #ifdef SQLITE_TEST @@ -6299,8 +6359,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY); #endif #ifdef SQLITE_DEBUG - Tcl_LinkVar(interp, "sqlite_addop_trace", - (char*)&sqlite3VdbeAddopTrace, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_where_trace", (char*)&sqlite3WhereTrace, TCL_LINK_INT); Tcl_LinkVar(interp, "sqlite_os_trace", diff --git a/src/test2.c b/src/test2.c index 8acdf6f..d130e9d 100644 --- a/src/test2.c +++ b/src/test2.c @@ -19,35 +19,7 @@ #include #include -/* -** Interpret an SQLite error number -*/ -static char *errorName(int rc){ - char *zName; - switch( rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; - case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; - case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; - case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; - case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; - default: zName = "SQLITE_Unknown"; break; - } - return zName; -} +extern const char *sqlite3ErrName(int); /* ** Page size and reserved size used for testing. @@ -87,7 +59,7 @@ static int pager_open( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB, pager_test_reiniter); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3PagerSetCachesize(pPager, nPage); @@ -119,7 +91,7 @@ static int pager_close( pPager = sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerClose(pPager); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -146,7 +118,7 @@ static int pager_rollback( pPager = sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerRollback(pPager); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -173,12 +145,12 @@ static int pager_commit( pPager = sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } rc = sqlite3PagerCommitPhaseTwo(pPager); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -205,7 +177,7 @@ static int pager_stmt_begin( pPager = sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerOpenSavepoint(pPager, 1); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -233,7 +205,7 @@ static int pager_stmt_rollback( rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, 0); sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -260,7 +232,7 @@ static int pager_stmt_commit( pPager = sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_RELEASE, 0); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -353,7 +325,7 @@ static int page_get( rc = sqlite3PagerGet(pPager, pgno, &pPage); } if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%p",pPage); @@ -507,7 +479,7 @@ static int page_write( pPage = (DbPage *)sqlite3TestTextToPtr(argv[1]); rc = sqlite3PagerWrite(pPage); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } pData = sqlite3PagerGetData(pPage); @@ -556,7 +528,7 @@ static int fake_big_file( (SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0 ); if( rc ){ - Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0); + Tcl_AppendResult(interp, "open failed: ", sqlite3ErrName(rc), 0); sqlite3_free(zFile); return TCL_ERROR; } @@ -566,7 +538,7 @@ static int fake_big_file( sqlite3OsCloseFree(fd); sqlite3_free(zFile); if( rc ){ - Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0); + Tcl_AppendResult(interp, "write failed: ", sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; diff --git a/src/test3.c b/src/test3.c index e460c42..e3ed310 100644 --- a/src/test3.c +++ b/src/test3.c @@ -19,31 +19,7 @@ #include #include -/* -** Interpret an SQLite error number -*/ -static char *errorName(int rc){ - char *zName; - switch( rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; - default: zName = "SQLITE_Unknown"; break; - } - return zName; -} +extern const char *sqlite3ErrName(int); /* ** A bogus sqlite3 connection structure for use in the btree @@ -89,7 +65,7 @@ static int btree_open( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); sqlite3_free(zFilename); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3BtreeSetCacheSize(pBt, nCache); @@ -119,7 +95,7 @@ static int btree_close( pBt = sqlite3TestTextToPtr(argv[1]); rc = sqlite3BtreeClose(pBt); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } nRefSqlite3--; @@ -156,7 +132,7 @@ static int btree_begin_transaction( rc = sqlite3BtreeBeginTrans(pBt, 1); sqlite3BtreeLeave(pBt); if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return TCL_OK; @@ -250,7 +226,7 @@ static int btree_cursor( sqlite3BtreeLeave(pBt); if( rc ){ ckfree((char *)pCur); - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pCur); @@ -285,7 +261,7 @@ static int btree_close_cursor( sqlite3BtreeLeave(pBt); ckfree((char *)pCur); if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } return SQLITE_OK; @@ -319,7 +295,7 @@ static int btree_next( rc = sqlite3BtreeNext(pCur, &res); sqlite3BtreeLeave(pCur->pBtree); if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); @@ -354,7 +330,7 @@ static int btree_first( rc = sqlite3BtreeFirst(pCur, &res); sqlite3BtreeLeave(pCur->pBtree); if( rc ){ - Tcl_AppendResult(interp, errorName(rc), 0); + Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",res); diff --git a/src/test4.c b/src/test4.c index 5b4c5a1..a6375c7 100644 --- a/src/test4.c +++ b/src/test4.c @@ -20,6 +20,8 @@ #include #include +extern const char *sqlite3ErrName(int); + /* ** Each thread is controlled by an instance of the following ** structure. @@ -372,34 +374,7 @@ static int tcl_thread_result( return TCL_ERROR; } thread_wait(&threadset[i]); - switch( threadset[i].rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; - case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; - case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; - case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; - case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; - case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; - case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; - case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; - case SQLITE_ROW: zName = "SQLITE_ROW"; break; - case SQLITE_DONE: zName = "SQLITE_DONE"; break; - default: zName = "SQLITE_Unknown"; break; - } + zName = sqlite3ErrName(threadset[i].rc); Tcl_AppendResult(interp, zName, 0); return TCL_OK; } diff --git a/src/test6.c b/src/test6.c index f511be9..c151ea4 100644 --- a/src/test6.c +++ b/src/test6.c @@ -87,7 +87,7 @@ typedef struct WriteBuffer WriteBuffer; ** an aligned write() of an integer number of 512 byte regions, then ** option (3) above is never selected. Instead, each 512 byte region ** is either correctly written or left completely untouched. Similar -** logic governs the behaviour if any of the other ATOMICXXX flags +** logic governs the behavior if any of the other ATOMICXXX flags ** is set. ** ** If either the IOCAP_SAFEAPPEND or IOCAP_SEQUENTIAL flags are set diff --git a/src/test7.c b/src/test7.c index 852cd1d..3cd4a22 100644 --- a/src/test7.c +++ b/src/test7.c @@ -376,6 +376,8 @@ static int tcl_client_colname( return TCL_OK; } +extern const char *sqlite3ErrName(int); + /* ** Usage: client_result ID ** @@ -403,34 +405,7 @@ static int tcl_client_result( return TCL_ERROR; } client_wait(&threadset[i]); - switch( threadset[i].rc ){ - case SQLITE_OK: zName = "SQLITE_OK"; break; - case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; - case SQLITE_PERM: zName = "SQLITE_PERM"; break; - case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; - case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; - case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; - case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; - case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; - case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; - case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; - case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; - case SQLITE_FULL: zName = "SQLITE_FULL"; break; - case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; - case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; - case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; - case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; - case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; - case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; - case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; - case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; - case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; - case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; - case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; - case SQLITE_ROW: zName = "SQLITE_ROW"; break; - case SQLITE_DONE: zName = "SQLITE_DONE"; break; - default: zName = "SQLITE_Unknown"; break; - } + zName = sqlite3ErrName(threadset[i].rc); Tcl_AppendResult(interp, zName, 0); return TCL_OK; } diff --git a/src/test8.c b/src/test8.c index 53cb149..c573933 100644 --- a/src/test8.c +++ b/src/test8.c @@ -1300,7 +1300,7 @@ static sqlite3_module echoModuleV2 = { ** Decode a pointer to an sqlite3 object. */ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); -extern const char *sqlite3TestErrorName(int rc); +extern const char *sqlite3ErrName(int); static void moduleDestroy(void *p){ sqlite3_free(p); @@ -1340,7 +1340,7 @@ static int register_echo_module( ); } - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } @@ -1370,29 +1370,6 @@ static int declare_vtab( return TCL_OK; } -#include "test_spellfix.c" - -/* -** Register the spellfix virtual table module. -*/ -static int register_spellfix_module( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ - sqlite3 *db; - - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; - - sqlite3Spellfix1Register(db); - return TCL_OK; -} - #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ /* @@ -1406,7 +1383,6 @@ int Sqlitetest8_Init(Tcl_Interp *interp){ void *clientData; } aObjCmd[] = { { "register_echo_module", register_echo_module, 0 }, - { "register_spellfix_module", register_spellfix_module, 0 }, { "sqlite3_declare_vtab", declare_vtab, 0 }, }; int i; diff --git a/src/test_async.c b/src/test_async.c index c760eea..b0b9431 100644 --- a/src/test_async.c +++ b/src/test_async.c @@ -23,8 +23,8 @@ #include "sqlite3.h" #include -/* From test1.c */ -const char *sqlite3TestErrorName(int); +/* From main.c */ +extern const char *sqlite3ErrName(int); struct TestAsyncGlobal { @@ -60,7 +60,7 @@ static int testAsyncInit( rc = sqlite3async_initialize(zParent, isDefault); if( rc!=SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; } return TCL_OK; @@ -208,7 +208,7 @@ static int testAsyncControl( } if( rc!=SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; } diff --git a/src/test_autoext.c b/src/test_autoext.c index 6b1e297..b5013f3 100644 --- a/src/test_autoext.c +++ b/src/test_autoext.c @@ -15,7 +15,7 @@ #include "sqlite3ext.h" #ifndef SQLITE_OMIT_LOAD_EXTENSION -static SQLITE_EXTENSION_INIT1 +SQLITE_EXTENSION_INIT1 /* ** The sqr() SQL function returns the square of its input value. diff --git a/src/test_backup.c b/src/test_backup.c index 2727137..e967424 100644 --- a/src/test_backup.c +++ b/src/test_backup.c @@ -17,9 +17,11 @@ #include #include +/* These functions are implemented in main.c. */ +extern const char *sqlite3ErrName(int); + /* These functions are implemented in test1.c. */ -int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); -const char *sqlite3TestErrorName(int); +extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); static int backupTestCmd( ClientData clientData, @@ -70,7 +72,7 @@ static int backupTestCmd( Tcl_DeleteCommand(interp, zCmdName); rc = sqlite3_backup_finish(p); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); break; } @@ -80,7 +82,7 @@ static int backupTestCmd( return TCL_ERROR; } rc = sqlite3_backup_step(p, nPage); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); break; } diff --git a/src/test_config.c b/src/test_config.c index f79b455..534727a 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -57,7 +57,7 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options","casesensitivelike","0",TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_CURDIR +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT Tcl_SetVar2(interp, "sqlite_options", "curdir", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "curdir", "0", TCL_GLOBAL_ONLY); @@ -87,6 +87,12 @@ static void set_options(Tcl_Interp *interp){ Tcl_SetVar2(interp, "sqlite_options", "lfs", "1", TCL_GLOBAL_ONLY); #endif +#if SQLITE_MAX_MMAP_SIZE>0 + Tcl_SetVar2(interp, "sqlite_options", "mmap", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "mmap", "0", TCL_GLOBAL_ONLY); +#endif + #if 1 /* def SQLITE_MEMDEBUG */ Tcl_SetVar2(interp, "sqlite_options", "memdebug", "1", TCL_GLOBAL_ONLY); #else @@ -395,11 +401,7 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double", Tcl_SetVar2(interp, "sqlite_options", "memorymanage", "0", TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_OMIT_MERGE_SORT - Tcl_SetVar2(interp, "sqlite_options", "mergesort", "0", TCL_GLOBAL_ONLY); -#else - Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY); -#endif +Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY); #ifdef SQLITE_OMIT_OR_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "or_opt", "0", TCL_GLOBAL_ONLY); diff --git a/src/test_fuzzer.c b/src/test_fuzzer.c deleted file mode 100644 index 10496f2..0000000 --- a/src/test_fuzzer.c +++ /dev/null @@ -1,1215 +0,0 @@ -/* -** 2011 March 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. -** -************************************************************************* -** -** Code for a demonstration virtual table that generates variations -** on an input word at increasing edit distances from the original. -** -** A fuzzer virtual table is created like this: -** -** CREATE VIRTUAL TABLE f USING fuzzer(); -** -** When it is created, the new fuzzer table must be supplied with the -** name of a "fuzzer data table", which must reside in the same database -** file as the new fuzzer table. The fuzzer data table contains the various -** transformations and their costs that the fuzzer logic uses to generate -** variations. -** -** The fuzzer data table must contain exactly four columns (more precisely, -** the statement "SELECT * FROM " must return records -** that consist of four columns). It does not matter what the columns are -** named. -** -** Each row in the fuzzer data table represents a single character -** transformation. The left most column of the row (column 0) contains an -** integer value - the identifier of the ruleset to which the transformation -** rule belongs (see "MULTIPLE RULE SETS" below). The second column of the -** row (column 0) contains the input character or characters. The third -** column contains the output character or characters. And the fourth column -** contains the integer cost of making the transformation. For example: -** -** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost); -** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100); -** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87); -** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38); -** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40); -** -** The first row inserted into the fuzzer data table by the SQL script -** above indicates that the cost of inserting a letter 'a' is 100. (All -** costs are integers. We recommend that costs be scaled so that the -** average cost is around 100.) The second INSERT statement creates a rule -** saying that the cost of deleting a single letter 'b' is 87. The third -** and fourth INSERT statements mean that the cost of transforming a -** single letter "o" into the two-letter sequence "oe" is 38 and that the -** cost of transforming "oe" back into "o" is 40. -** -** The contents of the fuzzer data table are loaded into main memory when -** a fuzzer table is first created, and may be internally reloaded by the -** system at any subsequent time. Therefore, the fuzzer data table should be -** populated before the fuzzer table is created and not modified thereafter. -** If you do need to modify the contents of the fuzzer data table, it is -** recommended that the associated fuzzer table be dropped, the fuzzer data -** table edited, and the fuzzer table recreated within a single transaction. -** Alternatively, the fuzzer data table can be edited then the database -** connection can be closed and reopened. -** -** Once it has been created, the fuzzer table can be queried as follows: -** -** SELECT word, distance FROM f -** WHERE word MATCH 'abcdefg' -** AND distance<200; -** -** This first query outputs the string "abcdefg" and all strings that -** can be derived from that string by appling the specified transformations. -** The strings are output together with their total transformation cost -** (called "distance") and appear in order of increasing cost. No string -** is output more than once. If there are multiple ways to transform the -** target string into the output string then the lowest cost transform is -** the one that is returned. In the example, the search is limited to -** strings with a total distance of less than 200. -** -** The fuzzer is a read-only table. Any attempt to DELETE, INSERT, or -** UPDATE on a fuzzer table will throw an error. -** -** It is important to put some kind of a limit on the fuzzer output. This -** can be either in the form of a LIMIT clause at the end of the query, -** or better, a "distance -#include -#include -#include - -#ifndef SQLITE_OMIT_VIRTUALTABLE - -/* -** Forward declaration of objects used by this implementation -*/ -typedef struct fuzzer_vtab fuzzer_vtab; -typedef struct fuzzer_cursor fuzzer_cursor; -typedef struct fuzzer_rule fuzzer_rule; -typedef struct fuzzer_seen fuzzer_seen; -typedef struct fuzzer_stem fuzzer_stem; - -/* -** Various types. -** -** fuzzer_cost is the "cost" of an edit operation. -** -** fuzzer_len is the length of a matching string. -** -** fuzzer_ruleid is an ruleset identifier. -*/ -typedef int fuzzer_cost; -typedef signed char fuzzer_len; -typedef int fuzzer_ruleid; - -/* -** Limits -*/ -#define FUZZER_MX_LENGTH 50 /* Maximum length of a rule string */ -#define FUZZER_MX_RULEID 2147483647 /* Maximum rule ID */ -#define FUZZER_MX_COST 1000 /* Maximum single-rule cost */ -#define FUZZER_MX_OUTPUT_LENGTH 100 /* Maximum length of an output string */ - - -/* -** Each transformation rule is stored as an instance of this object. -** All rules are kept on a linked list sorted by rCost. -*/ -struct fuzzer_rule { - fuzzer_rule *pNext; /* Next rule in order of increasing rCost */ - char *zFrom; /* Transform from */ - fuzzer_cost rCost; /* Cost of this transformation */ - fuzzer_len nFrom, nTo; /* Length of the zFrom and zTo strings */ - fuzzer_ruleid iRuleset; /* The rule set to which this rule belongs */ - char zTo[4]; /* Transform to (extra space appended) */ -}; - -/* -** A stem object is used to generate variants. It is also used to record -** previously generated outputs. -** -** Every stem is added to a hash table as it is output. Generation of -** duplicate stems is suppressed. -** -** Active stems (those that might generate new outputs) are kepts on a linked -** list sorted by increasing cost. The cost is the sum of rBaseCost and -** pRule->rCost. -*/ -struct fuzzer_stem { - char *zBasis; /* Word being fuzzed */ - const fuzzer_rule *pRule; /* Current rule to apply */ - fuzzer_stem *pNext; /* Next stem in rCost order */ - fuzzer_stem *pHash; /* Next stem with same hash on zBasis */ - fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */ - fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */ - fuzzer_len nBasis; /* Length of the zBasis string */ - fuzzer_len n; /* Apply pRule at this character offset */ -}; - -/* -** A fuzzer virtual-table object -*/ -struct fuzzer_vtab { - sqlite3_vtab base; /* Base class - must be first */ - char *zClassName; /* Name of this class. Default: "fuzzer" */ - fuzzer_rule *pRule; /* All active rules in this fuzzer */ - int nCursor; /* Number of active cursors */ -}; - -#define FUZZER_HASH 4001 /* Hash table size */ -#define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */ - -/* A fuzzer cursor object */ -struct fuzzer_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - sqlite3_int64 iRowid; /* The rowid of the current word */ - fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */ - fuzzer_cost rLimit; /* Maximum cost of any term */ - fuzzer_stem *pStem; /* Stem with smallest rCostX */ - fuzzer_stem *pDone; /* Stems already processed to completion */ - fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */ - int mxQueue; /* Largest used index in aQueue[] */ - char *zBuf; /* Temporary use buffer */ - int nBuf; /* Bytes allocated for zBuf */ - int nStem; /* Number of stems allocated */ - int iRuleset; /* Only process rules from this ruleset */ - fuzzer_rule nullRule; /* Null rule used first */ - fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */ -}; - -/* -** The two input rule lists are both sorted in order of increasing -** cost. Merge them together into a single list, sorted by cost, and -** return a pointer to the head of that list. -*/ -static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){ - fuzzer_rule head; - fuzzer_rule *pTail; - - pTail = &head; - while( pA && pB ){ - if( pA->rCost<=pB->rCost ){ - pTail->pNext = pA; - pTail = pA; - pA = pA->pNext; - }else{ - pTail->pNext = pB; - pTail = pB; - pB = pB->pNext; - } - } - if( pA==0 ){ - pTail->pNext = pB; - }else{ - pTail->pNext = pA; - } - return head.pNext; -} - -/* -** Statement pStmt currently points to a row in the fuzzer data table. This -** function allocates and populates a fuzzer_rule structure according to -** the content of the row. -** -** If successful, *ppRule is set to point to the new object and SQLITE_OK -** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point -** to an error message and an SQLite error code returned. -*/ -static int fuzzerLoadOneRule( - fuzzer_vtab *p, /* Fuzzer virtual table handle */ - sqlite3_stmt *pStmt, /* Base rule on statements current row */ - fuzzer_rule **ppRule, /* OUT: New rule object */ - char **pzErr /* OUT: Error message */ -){ - sqlite3_int64 iRuleset = sqlite3_column_int64(pStmt, 0); - const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1); - const char *zTo = (const char *)sqlite3_column_text(pStmt, 2); - int nCost = sqlite3_column_int(pStmt, 3); - - int rc = SQLITE_OK; /* Return code */ - int nFrom; /* Size of string zFrom, in bytes */ - int nTo; /* Size of string zTo, in bytes */ - fuzzer_rule *pRule = 0; /* New rule object to return */ - - if( zFrom==0 ) zFrom = ""; - if( zTo==0 ) zTo = ""; - nFrom = (int)strlen(zFrom); - nTo = (int)strlen(zTo); - - /* Silently ignore null transformations */ - if( strcmp(zFrom, zTo)==0 ){ - *ppRule = 0; - return SQLITE_OK; - } - - if( nCost<=0 || nCost>FUZZER_MX_COST ){ - *pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d", - p->zClassName, FUZZER_MX_COST - ); - rc = SQLITE_ERROR; - }else - if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){ - *pzErr = sqlite3_mprintf("%s: maximum string length is %d", - p->zClassName, FUZZER_MX_LENGTH - ); - rc = SQLITE_ERROR; - }else - if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){ - *pzErr = sqlite3_mprintf("%s: ruleset must be between 0 and %d", - p->zClassName, FUZZER_MX_RULEID - ); - rc = SQLITE_ERROR; - }else{ - - 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->nFrom = nFrom; - memcpy(pRule->zFrom, zFrom, nFrom+1); - memcpy(pRule->zTo, zTo, nTo+1); - pRule->nTo = nTo; - pRule->rCost = nCost; - pRule->iRuleset = (int)iRuleset; - } - } - - *ppRule = pRule; - return rc; -} - -/* -** Load the content of the fuzzer data table into memory. -*/ -static int fuzzerLoadRules( - sqlite3 *db, /* Database handle */ - fuzzer_vtab *p, /* Virtual fuzzer table to configure */ - const char *zDb, /* Database containing rules data */ - const char *zData, /* Table containing rules data */ - char **pzErr /* OUT: Error message */ -){ - int rc = SQLITE_OK; /* Return code */ - char *zSql; /* SELECT used to read from rules table */ - fuzzer_rule *pHead = 0; - - zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - int rc2; /* finalize() return code */ - sqlite3_stmt *pStmt = 0; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - *pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db)); - }else if( sqlite3_column_count(pStmt)!=4 ){ - *pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4", - p->zClassName, zData, sqlite3_column_count(pStmt) - ); - rc = SQLITE_ERROR; - }else{ - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ - fuzzer_rule *pRule = 0; - rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr); - if( pRule ){ - pRule->pNext = pHead; - pHead = pRule; - } - } - } - rc2 = sqlite3_finalize(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - } - sqlite3_free(zSql); - - /* All rules are now in a singly linked list starting at pHead. This - ** block sorts them by cost and then sets fuzzer_vtab.pRule to point to - ** point to the head of the sorted list. - */ - if( rc==SQLITE_OK ){ - unsigned int i; - fuzzer_rule *pX; - fuzzer_rule *a[15]; - for(i=0; ipNext; - pX->pNext = 0; - for(i=0; a[i] && ipRule = fuzzerMergeRules(p->pRule, pX); - }else{ - /* An error has occurred. Setting p->pRule to point to the head of the - ** allocated list ensures that the list will be cleaned up in this case. - */ - assert( p->pRule==0 ); - p->pRule = pHead; - } - - return rc; -} - -/* -** This function converts an SQL quoted string into an unquoted string -** and returns a pointer to a buffer allocated using sqlite3_malloc() -** containing the result. The caller should eventually free this buffer -** using sqlite3_free. -** -** Examples: -** -** "abc" becomes abc -** 'xyz' becomes xyz -** [pqr] becomes pqr -** `mno` becomes mno -*/ -static char *fuzzerDequote(const char *zIn){ - int nIn; /* Size of input string, in bytes */ - char *zOut; /* Output (dequoted) string */ - - nIn = (int)strlen(zIn); - zOut = sqlite3_malloc(nIn+1); - if( zOut ){ - char q = zIn[0]; /* Quote character (if any ) */ - - if( q!='[' && q!= '\'' && q!='"' && q!='`' ){ - memcpy(zOut, zIn, nIn+1); - }else{ - int iOut = 0; /* Index of next byte to write to output */ - int iIn; /* Index of next byte to read from input */ - - if( q=='[' ) q = ']'; - for(iIn=1; iInnCursor==0 ); - while( p->pRule ){ - fuzzer_rule *pRule = p->pRule; - p->pRule = pRule->pNext; - sqlite3_free(pRule); - } - sqlite3_free(p); - return SQLITE_OK; -} - -/* -** xConnect/xCreate method for the fuzzer module. Arguments are: -** -** argv[0] -> module name ("fuzzer") -** argv[1] -> database name -** argv[2] -> table name -** argv[3] -> fuzzer rule table name -*/ -static int fuzzerConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - int rc = SQLITE_OK; /* Return code */ - fuzzer_vtab *pNew = 0; /* New virtual table */ - const char *zModule = argv[0]; - const char *zDb = argv[1]; - - if( argc!=4 ){ - *pzErr = sqlite3_mprintf( - "%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule - ); - rc = SQLITE_ERROR; - }else{ - int nModule; /* Length of zModule, in bytes */ - - nModule = (int)strlen(zModule); - pNew = sqlite3_malloc( sizeof(*pNew) + nModule + 1); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - char *zTab; /* Dequoted name of fuzzer data table */ - - memset(pNew, 0, sizeof(*pNew)); - pNew->zClassName = (char*)&pNew[1]; - memcpy(pNew->zClassName, zModule, nModule+1); - - zTab = fuzzerDequote(argv[3]); - if( zTab==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = fuzzerLoadRules(db, pNew, zDb, zTab, pzErr); - sqlite3_free(zTab); - } - - if( rc==SQLITE_OK ){ - rc = sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,ruleset)"); - } - if( rc!=SQLITE_OK ){ - fuzzerDisconnect((sqlite3_vtab *)pNew); - pNew = 0; - } - } - } - - *ppVtab = (sqlite3_vtab *)pNew; - return rc; -} - -/* -** Open a new fuzzer cursor. -*/ -static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - fuzzer_vtab *p = (fuzzer_vtab*)pVTab; - fuzzer_cursor *pCur; - pCur = sqlite3_malloc( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - pCur->pVtab = p; - *ppCursor = &pCur->base; - p->nCursor++; - return SQLITE_OK; -} - -/* -** Free all stems in a list. -*/ -static void fuzzerClearStemList(fuzzer_stem *pStem){ - while( pStem ){ - fuzzer_stem *pNext = pStem->pNext; - sqlite3_free(pStem); - pStem = pNext; - } -} - -/* -** Free up all the memory allocated by a cursor. Set it rLimit to 0 -** to indicate that it is at EOF. -*/ -static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){ - int i; - fuzzerClearStemList(pCur->pStem); - fuzzerClearStemList(pCur->pDone); - for(i=0; iaQueue[i]); - pCur->rLimit = (fuzzer_cost)0; - if( clearHash && pCur->nStem ){ - pCur->mxQueue = 0; - pCur->pStem = 0; - pCur->pDone = 0; - memset(pCur->aQueue, 0, sizeof(pCur->aQueue)); - memset(pCur->apHash, 0, sizeof(pCur->apHash)); - } - pCur->nStem = 0; -} - -/* -** Close a fuzzer cursor. -*/ -static int fuzzerClose(sqlite3_vtab_cursor *cur){ - fuzzer_cursor *pCur = (fuzzer_cursor *)cur; - fuzzerClearCursor(pCur, 0); - sqlite3_free(pCur->zBuf); - pCur->pVtab->nCursor--; - sqlite3_free(pCur); - return SQLITE_OK; -} - -/* -** Compute the current output term for a fuzzer_stem. -*/ -static int fuzzerRender( - fuzzer_stem *pStem, /* The stem to be rendered */ - char **pzBuf, /* Write results into this buffer. realloc if needed */ - int *pnBuf /* Size of the buffer */ -){ - const fuzzer_rule *pRule = pStem->pRule; - int n; /* Size of output term without nul-term */ - char *z; /* Buffer to assemble output term in */ - - n = pStem->nBasis + pRule->nTo - pRule->nFrom; - if( (*pnBuf)n; - z = *pzBuf; - if( n<0 ){ - memcpy(z, pStem->zBasis, pStem->nBasis+1); - }else{ - memcpy(z, pStem->zBasis, n); - memcpy(&z[n], pRule->zTo, pRule->nTo); - memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom], - pStem->nBasis-n-pRule->nFrom+1); - } - - assert( z[pStem->nBasis + pRule->nTo - pRule->nFrom]==0 ); - return SQLITE_OK; -} - -/* -** Compute a hash on zBasis. -*/ -static unsigned int fuzzerHash(const char *z){ - unsigned int h = 0; - while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); } - return h % FUZZER_HASH; -} - -/* -** Current cost of a stem -*/ -static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){ - return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost; -} - -#if 0 -/* -** Print a description of a fuzzer_stem on stderr. -*/ -static void fuzzerStemPrint( - const char *zPrefix, - fuzzer_stem *pStem, - const char *zSuffix -){ - if( pStem->n<0 ){ - fprintf(stderr, "%s[%s](%d)-->self%s", - zPrefix, - pStem->zBasis, pStem->rBaseCost, - zSuffix - ); - }else{ - char *zBuf = 0; - int nBuf = 0; - if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return; - fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s", - zPrefix, - pStem->zBasis, pStem->rBaseCost, zBuf, pStem->, - zSuffix - ); - sqlite3_free(zBuf); - } -} -#endif - -/* -** Return 1 if the string to which the cursor is point has already -** been emitted. Return 0 if not. Return -1 on a memory allocation -** failures. -*/ -static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){ - unsigned int h; - fuzzer_stem *pLookup; - - if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ - return -1; - } - h = fuzzerHash(pCur->zBuf); - pLookup = pCur->apHash[h]; - while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){ - pLookup = pLookup->pHash; - } - return pLookup!=0; -} - -/* -** If argument pRule is NULL, this function returns false. -** -** Otherwise, it returns true if rule pRule should be skipped. A rule -** should be skipped if it does not belong to rule-set iRuleset, or if -** applying it to stem pStem would create a string longer than -** FUZZER_MX_OUTPUT_LENGTH bytes. -*/ -static int fuzzerSkipRule( - const fuzzer_rule *pRule, /* Determine whether or not to skip this */ - fuzzer_stem *pStem, /* Stem rule may be applied to */ - int iRuleset /* Rule-set used by the current query */ -){ - return pRule && ( - (pRule->iRuleset!=iRuleset) - || (pStem->nBasis + pRule->nTo - pRule->nFrom)>FUZZER_MX_OUTPUT_LENGTH - ); -} - -/* -** Advance a fuzzer_stem to its next value. Return 0 if there are -** no more values that can be generated by this fuzzer_stem. Return -** -1 on a memory allocation failure. -*/ -static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){ - const fuzzer_rule *pRule; - while( (pRule = pStem->pRule)!=0 ){ - assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset ); - while( pStem->n < pStem->nBasis - pRule->nFrom ){ - pStem->n++; - if( pRule->nFrom==0 - || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0 - ){ - /* Found a rewrite case. Make sure it is not a duplicate */ - int rc = fuzzerSeen(pCur, pStem); - if( rc<0 ) return -1; - if( rc==0 ){ - fuzzerCost(pStem); - return 1; - } - } - } - pStem->n = -1; - do{ - pRule = pRule->pNext; - }while( fuzzerSkipRule(pRule, pStem, pCur->iRuleset) ); - pStem->pRule = pRule; - if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0; - } - return 0; -} - -/* -** The two input stem lists are both sorted in order of increasing -** rCostX. Merge them together into a single list, sorted by rCostX, and -** return a pointer to the head of that new list. -*/ -static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){ - fuzzer_stem head; - fuzzer_stem *pTail; - - pTail = &head; - while( pA && pB ){ - if( pA->rCostX<=pB->rCostX ){ - pTail->pNext = pA; - pTail = pA; - pA = pA->pNext; - }else{ - pTail->pNext = pB; - pTail = pB; - pB = pB->pNext; - } - } - if( pA==0 ){ - pTail->pNext = pB; - }else{ - pTail->pNext = pA; - } - return head.pNext; -} - -/* -** Load pCur->pStem with the lowest-cost stem. Return a pointer -** to the lowest-cost stem. -*/ -static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){ - fuzzer_stem *pBest, *pX; - int iBest; - int i; - - if( pCur->pStem==0 ){ - iBest = -1; - pBest = 0; - for(i=0; i<=pCur->mxQueue; i++){ - pX = pCur->aQueue[i]; - if( pX==0 ) continue; - if( pBest==0 || pBest->rCostX>pX->rCostX ){ - pBest = pX; - iBest = i; - } - } - if( pBest ){ - pCur->aQueue[iBest] = pBest->pNext; - pBest->pNext = 0; - pCur->pStem = pBest; - } - } - return pCur->pStem; -} - -/* -** Insert pNew into queue of pending stems. Then find the stem -** with the lowest rCostX and move it into pCur->pStem. -** list. The insert is done such the pNew is in the correct order -** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost. -*/ -static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){ - fuzzer_stem *pX; - int i; - - /* If pCur->pStem exists and is greater than pNew, then make pNew - ** the new pCur->pStem and insert the old pCur->pStem instead. - */ - if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){ - pNew->pNext = 0; - pCur->pStem = pNew; - pNew = pX; - } - - /* Insert the new value */ - pNew->pNext = 0; - pX = pNew; - for(i=0; i<=pCur->mxQueue; i++){ - if( pCur->aQueue[i] ){ - pX = fuzzerMergeStems(pX, pCur->aQueue[i]); - pCur->aQueue[i] = 0; - }else{ - pCur->aQueue[i] = pX; - break; - } - } - if( i>pCur->mxQueue ){ - if( imxQueue = i; - pCur->aQueue[i] = pX; - }else{ - assert( pCur->mxQueue==FUZZER_NQUEUE-1 ); - pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]); - pCur->aQueue[FUZZER_NQUEUE-1] = pX; - } - } - - return fuzzerLowestCostStem(pCur); -} - -/* -** Allocate a new fuzzer_stem. Add it to the hash table but do not -** link it into either the pCur->pStem or pCur->pDone lists. -*/ -static fuzzer_stem *fuzzerNewStem( - fuzzer_cursor *pCur, - const char *zWord, - fuzzer_cost rBaseCost -){ - fuzzer_stem *pNew; - fuzzer_rule *pRule; - unsigned int h; - - pNew = sqlite3_malloc( sizeof(*pNew) + (int)strlen(zWord) + 1 ); - if( pNew==0 ) return 0; - memset(pNew, 0, sizeof(*pNew)); - pNew->zBasis = (char*)&pNew[1]; - pNew->nBasis = (int)strlen(zWord); - memcpy(pNew->zBasis, zWord, pNew->nBasis+1); - pRule = pCur->pVtab->pRule; - while( fuzzerSkipRule(pRule, pNew, pCur->iRuleset) ){ - pRule = pRule->pNext; - } - pNew->pRule = pRule; - pNew->n = -1; - pNew->rBaseCost = pNew->rCostX = rBaseCost; - h = fuzzerHash(pNew->zBasis); - pNew->pHash = pCur->apHash[h]; - pCur->apHash[h] = pNew; - pCur->nStem++; - return pNew; -} - - -/* -** Advance a cursor to its next row of output -*/ -static int fuzzerNext(sqlite3_vtab_cursor *cur){ - fuzzer_cursor *pCur = (fuzzer_cursor*)cur; - int rc; - fuzzer_stem *pStem, *pNew; - - pCur->iRowid++; - - /* Use the element the cursor is currently point to to create - ** a new stem and insert the new stem into the priority queue. - */ - pStem = pCur->pStem; - if( pStem->rCostX>0 ){ - rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf); - if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM; - pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX); - if( pNew ){ - if( fuzzerAdvance(pCur, pNew)==0 ){ - pNew->pNext = pCur->pDone; - pCur->pDone = pNew; - }else{ - if( fuzzerInsert(pCur, pNew)==pNew ){ - return SQLITE_OK; - } - } - }else{ - return SQLITE_NOMEM; - } - } - - /* Adjust the priority queue so that the first element of the - ** stem list is the next lowest cost word. - */ - while( (pStem = pCur->pStem)!=0 ){ - int res = fuzzerAdvance(pCur, pStem); - if( res<0 ){ - return SQLITE_NOMEM; - }else if( res>0 ){ - pCur->pStem = 0; - pStem = fuzzerInsert(pCur, pStem); - if( (rc = fuzzerSeen(pCur, pStem))!=0 ){ - if( rc<0 ) return SQLITE_NOMEM; - continue; - } - return SQLITE_OK; /* New word found */ - } - pCur->pStem = 0; - pStem->pNext = pCur->pDone; - pCur->pDone = pStem; - if( fuzzerLowestCostStem(pCur) ){ - rc = fuzzerSeen(pCur, pCur->pStem); - if( rc<0 ) return SQLITE_NOMEM; - if( rc==0 ){ - return SQLITE_OK; - } - } - } - - /* Reach this point only if queue has been exhausted and there is - ** nothing left to be output. */ - pCur->rLimit = (fuzzer_cost)0; - return SQLITE_OK; -} - -/* -** Called to "rewind" a cursor back to the beginning so that -** it starts its output over again. Always called at least once -** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call. -*/ -static int fuzzerFilter( - sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor; - const char *zWord = ""; - fuzzer_stem *pStem; - int idx; - - fuzzerClearCursor(pCur, 1); - pCur->rLimit = 2147483647; - idx = 0; - if( idxNum & 1 ){ - zWord = (const char*)sqlite3_value_text(argv[0]); - idx++; - } - if( idxNum & 2 ){ - pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[idx]); - idx++; - } - if( idxNum & 4 ){ - pCur->iRuleset = (fuzzer_cost)sqlite3_value_int(argv[idx]); - idx++; - } - pCur->nullRule.pNext = pCur->pVtab->pRule; - pCur->nullRule.rCost = 0; - pCur->nullRule.nFrom = 0; - pCur->nullRule.nTo = 0; - pCur->nullRule.zFrom = ""; - pCur->iRowid = 1; - assert( pCur->pStem==0 ); - - /* If the query term is longer than FUZZER_MX_OUTPUT_LENGTH bytes, this - ** query will return zero rows. */ - if( (int)strlen(zWord)pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0); - if( pStem==0 ) return SQLITE_NOMEM; - pStem->pRule = &pCur->nullRule; - pStem->n = pStem->nBasis; - }else{ - pCur->rLimit = 0; - } - - return SQLITE_OK; -} - -/* -** Only the word and distance columns have values. All other columns -** return NULL -*/ -static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - fuzzer_cursor *pCur = (fuzzer_cursor*)cur; - if( i==0 ){ - /* the "word" column */ - if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ - return SQLITE_NOMEM; - } - sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT); - }else if( i==1 ){ - /* the "distance" column */ - sqlite3_result_int(ctx, pCur->pStem->rCostX); - }else{ - /* All other columns are NULL */ - sqlite3_result_null(ctx); - } - return SQLITE_OK; -} - -/* -** The rowid. -*/ -static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - fuzzer_cursor *pCur = (fuzzer_cursor*)cur; - *pRowid = pCur->iRowid; - return SQLITE_OK; -} - -/* -** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal -** that the cursor has nothing more to output. -*/ -static int fuzzerEof(sqlite3_vtab_cursor *cur){ - fuzzer_cursor *pCur = (fuzzer_cursor*)cur; - return pCur->rLimit<=(fuzzer_cost)0; -} - -/* -** Search for terms of these forms: -** -** (A) word MATCH $str -** (B1) distance < $value -** (B2) distance <= $value -** (C) ruleid == $ruleid -** -** The distance< and distance<= are both treated as distance<=. -** The query plan number is a bit vector: -** -** bit 1: Term of the form (A) found -** bit 2: Term like (B1) or (B2) found -** bit 3: Term like (C) found -** -** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set -** then $value is in filter.argv[0] if bit-1 is clear and is in -** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is -** in filter.argv[0] if bit-1 and bit-2 are both zero, is in -** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in -** filter.argv[2] if both bit-1 and bit-2 are set. -*/ -static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int iPlan = 0; - int iDistTerm = -1; - int iRulesetTerm = -1; - int i; - const struct sqlite3_index_constraint *pConstraint; - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( (iPlan & 1)==0 - && pConstraint->iColumn==0 - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH - ){ - iPlan |= 1; - pIdxInfo->aConstraintUsage[i].argvIndex = 1; - pIdxInfo->aConstraintUsage[i].omit = 1; - } - if( (iPlan & 2)==0 - && pConstraint->iColumn==1 - && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT - || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) - ){ - iPlan |= 2; - iDistTerm = i; - } - if( (iPlan & 4)==0 - && pConstraint->iColumn==2 - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= 4; - pIdxInfo->aConstraintUsage[i].omit = 1; - iRulesetTerm = i; - } - } - if( iPlan & 2 ){ - pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0); - } - if( iPlan & 4 ){ - int idx = 1; - if( iPlan & 1 ) idx++; - if( iPlan & 2 ) idx++; - pIdxInfo->aConstraintUsage[iRulesetTerm].argvIndex = idx; - } - pIdxInfo->idxNum = iPlan; - if( pIdxInfo->nOrderBy==1 - && pIdxInfo->aOrderBy[0].iColumn==1 - && pIdxInfo->aOrderBy[0].desc==0 - ){ - pIdxInfo->orderByConsumed = 1; - } - pIdxInfo->estimatedCost = (double)10000; - - return SQLITE_OK; -} - -/* -** A virtual table module that implements the "fuzzer". -*/ -static sqlite3_module fuzzerModule = { - 0, /* iVersion */ - fuzzerConnect, - fuzzerConnect, - fuzzerBestIndex, - fuzzerDisconnect, - fuzzerDisconnect, - fuzzerOpen, /* xOpen - open a cursor */ - fuzzerClose, /* xClose - close a cursor */ - fuzzerFilter, /* xFilter - configure scan constraints */ - fuzzerNext, /* xNext - advance a cursor */ - fuzzerEof, /* xEof - check for end of scan */ - fuzzerColumn, /* xColumn - read data */ - fuzzerRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ -}; - -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - - -/* -** Register the fuzzer virtual table -*/ -int fuzzer_register(sqlite3 *db){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_VIRTUALTABLE - rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0); -#endif - return rc; -} - -#ifdef SQLITE_TEST -#include -/* -** Decode a pointer to an sqlite3 object. -*/ -extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); - -/* -** Register the echo virtual table module. -*/ -static int register_fuzzer_module( - ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - sqlite3 *db; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB"); - return TCL_ERROR; - } - getDbPointer(interp, Tcl_GetString(objv[1]), &db); - fuzzer_register(db); - return TCL_OK; -} - - -/* -** Register commands with the TCL interpreter. -*/ -int Sqlitetestfuzzer_Init(Tcl_Interp *interp){ - static struct { - char *zName; - Tcl_ObjCmdProc *xProc; - void *clientData; - } aObjCmd[] = { - { "register_fuzzer_module", register_fuzzer_module, 0 }, - }; - int i; - for(i=0; i "SQLITE_ERROR" +** sqlite3ErrName(1) -> "SQLITE_ERROR" */ -const char *sqlite3TestErrorName(int); +extern const char *sqlite3ErrName(int); /* ** Transform pointers to text and back again @@ -1072,7 +1072,7 @@ static int test_db_config_lookaside( sqlite3 *db; int bufid; static char azBuf[2][10000]; - int getDbPointer(Tcl_Interp*, const char*, sqlite3**); + extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); if( objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "BUFID SIZE COUNT"); return TCL_ERROR; @@ -1126,7 +1126,7 @@ static int test_config_heap( rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc); } - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -1143,7 +1143,7 @@ static int test_config_error( Tcl_Obj *CONST objv[] ){ sqlite3 *db; - int getDbPointer(Tcl_Interp*, const char*, sqlite3**); + extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); if( objc!=2 && objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, "[DB]"); @@ -1192,7 +1192,7 @@ static int test_config_uri( } rc = sqlite3_config(SQLITE_CONFIG_URI, bOpenUri); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -1221,7 +1221,7 @@ static int test_config_cis( } rc = sqlite3_config(SQLITE_CONFIG_COVERING_INDEX_SCAN, bUseCis); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -1335,7 +1335,7 @@ static int test_db_status( int i, op, resetFlag; const char *zOpName; sqlite3 *db; - int getDbPointer(Tcl_Interp*, const char*, sqlite3**); + extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); static const struct { const char *zName; int op; @@ -1401,7 +1401,7 @@ static int test_install_malloc_faultsim( return TCL_ERROR; } rc = faultsimInstall(isInstall); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -1419,7 +1419,7 @@ static int test_install_memsys3( const sqlite3_mem_methods *sqlite3MemGetMemsys3(void); rc = sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetMemsys3()); #endif - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } diff --git a/src/test_multiplex.c b/src/test_multiplex.c index 23df347..624541b 100644 --- a/src/test_multiplex.c +++ b/src/test_multiplex.c @@ -60,7 +60,7 @@ /* ** These should be defined to be the same as the values in -** sqliteInt.h. They are defined seperately here so that +** sqliteInt.h. They are defined separately here so that ** the multiplex VFS shim can be built as a loadable ** module. */ @@ -1183,7 +1183,7 @@ int sqlite3_multiplex_shutdown(void){ /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST #include -extern const char *sqlite3TestErrorName(int); +extern const char *sqlite3ErrName(int); /* @@ -1212,7 +1212,7 @@ static int test_multiplex_initialize( /* Call sqlite3_multiplex_initialize() */ rc = sqlite3_multiplex_initialize(zName, makeDefault); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } @@ -1237,7 +1237,7 @@ static int test_multiplex_shutdown( /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } @@ -1355,7 +1355,7 @@ static int test_multiplex_control( } rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR; } diff --git a/src/test_mutex.c b/src/test_mutex.c index 0bb7437..c9b4a29 100644 --- a/src/test_mutex.c +++ b/src/test_mutex.c @@ -19,8 +19,8 @@ #include #include -/* defined in test1.c */ -const char *sqlite3TestErrorName(int); +/* defined in main.c */ +extern const char *sqlite3ErrName(int); /* A countable mutex */ struct sqlite3_mutex { @@ -148,7 +148,7 @@ static int test_shutdown( } rc = sqlite3_shutdown(); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -169,7 +169,7 @@ static int test_initialize( } rc = sqlite3_initialize(); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -230,7 +230,7 @@ static int test_install_mutex_counters( g.isInstalled = isInstall; } - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } @@ -354,7 +354,7 @@ static int test_config( } rc = sqlite3_config(i); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_OK; } diff --git a/src/test_quota.c b/src/test_quota.c index 166a512..e590996 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -1295,7 +1295,7 @@ int sqlite3_quota_remove(const char *zFilename){ if( pGroup ){ for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){ pNextFile = pFile->pNext; - diff = memcmp(zFull, pFile->zFilename, nFull); + diff = strncmp(zFull, pFile->zFilename, nFull); if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){ if( pFile->nRef ){ pFile->deleteOnClose = 1; @@ -1325,7 +1325,7 @@ struct TclQuotaCallback { Tcl_Obj *pScript; /* Script to be run */ }; -extern const char *sqlite3TestErrorName(int); +extern const char *sqlite3ErrName(int); /* @@ -1407,7 +1407,7 @@ static int test_quota_initialize( /* Call sqlite3_quota_initialize() */ rc = sqlite3_quota_initialize(zName, makeDefault); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } @@ -1430,7 +1430,7 @@ static int test_quota_shutdown( /* Call sqlite3_quota_shutdown() */ rc = sqlite3_quota_shutdown(); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } @@ -1485,7 +1485,7 @@ static int test_quota_set( /* Invoke sqlite3_quota_set() */ rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } @@ -1511,7 +1511,7 @@ static int test_quota_file( /* Invoke sqlite3_quota_file() */ rc = sqlite3_quota_file(zFilename); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); return TCL_OK; } diff --git a/src/test_rtree.c b/src/test_rtree.c index d3c9e0c..f54ae9b 100644 --- a/src/test_rtree.c +++ b/src/test_rtree.c @@ -254,7 +254,7 @@ static int register_cube_geom( UNUSED_PARAMETER(objv); #else extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); - extern const char *sqlite3TestErrorName(int); + extern const char *sqlite3ErrName(int); sqlite3 *db; int rc; @@ -264,7 +264,7 @@ static int register_cube_geom( } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); #endif return TCL_OK; } @@ -282,7 +282,7 @@ static int register_circle_geom( UNUSED_PARAMETER(objv); #else extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); - extern const char *sqlite3TestErrorName(int); + extern const char *sqlite3ErrName(int); sqlite3 *db; int rc; @@ -292,7 +292,7 @@ static int register_circle_geom( } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); #endif return TCL_OK; } diff --git a/src/test_spellfix.c b/src/test_spellfix.c deleted file mode 100644 index 3f21d73..0000000 --- a/src/test_spellfix.c +++ /dev/null @@ -1,2830 +0,0 @@ -/* -** 2012 April 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. -** -************************************************************************* -** -** This module implements the spellfix1 VIRTUAL TABLE that can be used -** to search a large vocabulary for close matches. See separate -** documentation files (spellfix1.wiki and editdist3.wiki) for details. -*/ -#if SQLITE_CORE -# include "sqliteInt.h" -#else -# include -# include -# include -# include "sqlite3ext.h" - SQLITE_EXTENSION_INIT1 -#endif /* !SQLITE_CORE */ -#include - -/* -** Character classes for ASCII characters: -** -** 0 '' Silent letters: H W -** 1 'A' Any vowel: A E I O U (Y) -** 2 'B' A bilabeal stop or fricative: B F P V W -** 3 'C' Other fricatives or back stops: C G J K Q S X Z -** 4 'D' Alveolar stops: D T -** 5 'H' Letter H at the beginning of a word -** 6 'L' Glide: L -** 7 'R' Semivowel: R -** 8 'M' Nasals: M N -** 9 'Y' Letter Y at the beginning of a word. -** 10 '9' Digits: 0 1 2 3 4 5 6 7 8 9 -** 11 ' ' White space -** 12 '?' Other. -*/ -#define CCLASS_SILENT 0 -#define CCLASS_VOWEL 1 -#define CCLASS_B 2 -#define CCLASS_C 3 -#define CCLASS_D 4 -#define CCLASS_H 5 -#define CCLASS_L 6 -#define CCLASS_R 7 -#define CCLASS_M 8 -#define CCLASS_Y 9 -#define CCLASS_DIGIT 10 -#define CCLASS_SPACE 11 -#define CCLASS_OTHER 12 - -/* -** The following table gives the character class for non-initial ASCII -** characters. -*/ -static const unsigned char midClass[] = { - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE, - /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER, - /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER, - /* ' */ CCLASS_SILENT, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER, - /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER, - /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER, - /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT, - /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT, - /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT, - /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER, - /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER, - /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL, - /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D, - /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C, - /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C, - /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M, - /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B, - /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C, - /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B, - /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_VOWEL, - /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER, - /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER, - /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B, - /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL, - /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT, - /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C, - /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M, - /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C, - /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D, - /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B, - /* x */ CCLASS_C, /* y */ CCLASS_VOWEL, /* z */ CCLASS_C, - /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER, - /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER, -}; -/* -** This tables gives the character class for ASCII characters that form the -** initial character of a word. The only difference from midClass is with -** the letters H, W, and Y. -*/ -static const unsigned char initClass[] = { - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE, - /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER, - /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER, - /* ' */ CCLASS_OTHER, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER, - /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER, - /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER, - /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT, - /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT, - /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT, - /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER, - /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER, - /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL, - /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D, - /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C, - /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C, - /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M, - /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B, - /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C, - /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B, - /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_Y, - /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER, - /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER, - /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B, - /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL, - /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT, - /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C, - /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M, - /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C, - /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D, - /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B, - /* x */ CCLASS_C, /* y */ CCLASS_Y, /* z */ CCLASS_C, - /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER, - /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER, -}; - -/* -** Mapping from the character class number (0-13) to a symbol for each -** character class. Note that initClass[] can be used to map the class -** symbol back into the class number. -*/ -static const unsigned char className[] = ".ABCDHLRMY9 ?"; - -/* -** Generate a "phonetic hash" from a string of ASCII characters -** in zIn[0..nIn-1]. -** -** * Map characters by character class as defined above. -** * Omit double-letters -** * Omit vowels beside R and L -** * Omit T when followed by CH -** * Omit W when followed by R -** * Omit D when followed by J or G -** * Omit K in KN or G in GN at the beginning of a word -** -** Space to hold the result is obtained from sqlite3_malloc() -** -** Return NULL if memory allocation fails. -*/ -static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){ - unsigned char *zOut = sqlite3_malloc( nIn + 1 ); - int i; - int nOut = 0; - char cPrev = 0x77; - char cPrevX = 0x77; - const unsigned char *aClass = initClass; - - if( zOut==0 ) return 0; - if( nIn>2 ){ - switch( zIn[0] ){ - case 'g': - case 'k': { - if( zIn[1]=='n' ){ zIn++; nIn--; } - break; - } - } - } - for(i=0; i=0 ); - if( nOut==0 || c!=zOut[nOut-1] ) zOut[nOut++] = c; - } - zOut[nOut] = 0; - return zOut; -} - -/* -** This is an SQL function wrapper around phoneticHash(). See -** the description of phoneticHash() for additional information. -*/ -static void phoneticHashSqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn; - unsigned char *zOut; - - zIn = sqlite3_value_text(argv[0]); - if( zIn==0 ) return; - zOut = phoneticHash(zIn, sqlite3_value_bytes(argv[0])); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free); - } -} - -/* -** Return the character class number for a character given its -** context. -*/ -static char characterClass(char cPrev, char c){ - return cPrev==0 ? initClass[c&0x7f] : midClass[c&0x7f]; -} - -/* -** Return the cost of inserting or deleting character c immediately -** following character cPrev. If cPrev==0, that means c is the first -** character of the word. -*/ -static int insertOrDeleteCost(char cPrev, char c, char cNext){ - char classC = characterClass(cPrev, c); - char classCprev; - - if( classC==CCLASS_SILENT ){ - /* Insert or delete "silent" characters such as H or W */ - return 1; - } - if( cPrev==c ){ - /* Repeated characters, or miss a repeat */ - return 10; - } - if( classC==CCLASS_VOWEL && (cPrev=='r' || cNext=='r') ){ - return 20; /* Insert a vowel before or after 'r' */ - } - classCprev = characterClass(cPrev, cPrev); - if( classC==classCprev ){ - if( classC==CCLASS_VOWEL ){ - /* Remove or add a new vowel to a vowel cluster */ - return 15; - }else{ - /* Remove or add a consonant not in the same class */ - return 50; - } - } - - /* any other character insertion or deletion */ - return 100; -} - -/* -** Divide the insertion cost by this factor when appending to the -** end of the word. -*/ -#define FINAL_INS_COST_DIV 4 - -/* -** Return the cost of substituting cTo in place of cFrom assuming -** the previous character is cPrev. If cPrev==0 then cTo is the first -** character of the word. -*/ -static int substituteCost(char cPrev, char cFrom, char cTo){ - char classFrom, classTo; - if( cFrom==cTo ){ - /* Exact match */ - return 0; - } - if( cFrom==(cTo^0x20) && ((cTo>='A' && cTo<='Z') || (cTo>='a' && cTo<='z')) ){ - /* differ only in case */ - return 0; - } - classFrom = characterClass(cPrev, cFrom); - classTo = characterClass(cPrev, cTo); - if( classFrom==classTo ){ - /* Same character class */ - return 40; - } - if( classFrom>=CCLASS_B && classFrom<=CCLASS_Y - && classTo>=CCLASS_B && classTo<=CCLASS_Y ){ - /* Convert from one consonant to another, but in a different class */ - return 75; - } - /* Any other subsitution */ - return 100; -} - -/* -** Given two strings zA and zB which are pure ASCII, return the cost -** of transforming zA into zB. If zA ends with '*' assume that it is -** a prefix of zB and give only minimal penalty for extra characters -** on the end of zB. -** -** Smaller numbers mean a closer match. -** -** Negative values indicate an error: -** -1 One of the inputs is NULL -** -2 Non-ASCII characters on input -** -3 Unable to allocate memory -** -** If pnMatch is not NULL, then *pnMatch is set to the number of bytes -** of zB that matched the pattern in zA. If zA does not end with a '*', -** then this value is always the number of bytes in zB (i.e. strlen(zB)). -** If zA does end in a '*', then it is the number of bytes in the prefix -** 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 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 */ - int *m; /* The cost matrix */ - char *cx; /* Corresponding character values */ - int *toFree = 0; /* Malloced space */ - int mStack[60+15]; /* Stack space to use if not too much is needed */ - int nMatch = 0; - - /* Early out if either input is NULL */ - if( zA==0 || zB==0 ) return -1; - - /* Skip any common prefix */ - while( zA[0] && zA[0]==zB[0] ){ dc = zA[0]; zA++; zB++; nMatch++; } - if( pnMatch ) *pnMatch = nMatch; - if( zA[0]==0 && zB[0]==0 ) return 0; - -#if 0 - printf("A=\"%s\" B=\"%s\" dc=%c\n", zA, zB, dc?dc:' '); -#endif - - /* Verify input strings and measure their lengths */ - for(nA=0; zA[nA]; nA++){ - if( zA[nA]&0x80 ) return -2; - } - for(nB=0; zB[nB]; nB++){ - if( zB[nB]&0x80 ) return -2; - } - - /* Special processing if either string is empty */ - if( nA==0 ){ - cBprev = dc; - for(xB=res=0; (cB = zB[xB])!=0; xB++){ - res += insertOrDeleteCost(cBprev, cB, zB[xB+1])/FINAL_INS_COST_DIV; - cBprev = cB; - } - return res; - } - if( nB==0 ){ - cAprev = dc; - for(xA=res=0; (cA = zA[xA])!=0; xA++){ - res += insertOrDeleteCost(cAprev, cA, zA[xA+1]); - cAprev = cA; - } - return res; - } - - /* A is a prefix of B */ - if( zA[0]=='*' && zA[1]==0 ) return 0; - - /* Allocate and initialize the Wagner matrix */ - if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ){ - m = mStack; - }else{ - m = toFree = sqlite3_malloc( (nB+1)*5*sizeof(m[0])/4 ); - if( m==0 ) return -3; - } - cx = (char*)&m[nB+1]; - - /* Compute the Wagner edit distance */ - m[0] = 0; - cx[0] = dc; - cBprev = dc; - for(xB=1; xB<=nB; xB++){ - cBnext = zB[xB]; - cB = zB[xB-1]; - cx[xB] = cB; - m[xB] = m[xB-1] + insertOrDeleteCost(cBprev, cB, cBnext); - cBprev = cB; - } - cAprev = dc; - for(xA=1; xA<=nA; xA++){ - int lastA = (xA==nA); - cA = zA[xA-1]; - cAnext = zA[xA]; - if( cA=='*' && lastA ) break; - d = m[0]; - dc = cx[0]; - m[0] = d + insertOrDeleteCost(cAprev, cA, cAnext); - cBprev = 0; - for(xB=1; xB<=nB; xB++){ - int totalCost, insCost, delCost, subCost, ncx; - cB = zB[xB-1]; - cBnext = zB[xB]; - - /* Cost to insert cB */ - insCost = insertOrDeleteCost(cx[xB-1], cB, cBnext); - if( lastA ) insCost /= FINAL_INS_COST_DIV; - - /* Cost to delete cA */ - delCost = insertOrDeleteCost(cx[xB], cA, cBnext); - - /* Cost to substitute cA->cB */ - subCost = substituteCost(cx[xB-1], cA, cB); - - /* Best cost */ - totalCost = insCost + m[xB-1]; - ncx = cB; - if( (delCost + m[xB])nLang; i++){ - EditDist3Cost *pCost, *pNext; - pCost = p->a[i].pCost; - while( pCost ){ - pNext = pCost->pNext; - sqlite3_free(pCost); - pCost = pNext; - } - } - sqlite3_free(p->a); - memset(p, 0, sizeof(*p)); -} -static void editDist3ConfigDelete(void *pIn){ - EditDist3Config *p = (EditDist3Config*)pIn; - editDist3ConfigClear(p); - sqlite3_free(p); -} - -/* -** Load all edit-distance weights from a table. -*/ -static int editDist3ConfigLoad( - EditDist3Config *p, /* The edit distance configuration to load */ - sqlite3 *db, /* Load from this database */ - const char *zTable /* Name of the table from which to load */ -){ - sqlite3_stmt *pStmt; - int rc, rc2; - char *zSql; - int iLangPrev = -9999; - EditDist3Lang *pLang = 0; - - zSql = sqlite3_mprintf("SELECT iLang, cFrom, cTo, iCost" - " FROM \"%w\" WHERE iLang>=0 ORDER BY iLang", zTable); - if( zSql==0 ) return SQLITE_NOMEM; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc ) return rc; - editDist3ConfigClear(p); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - int iLang = sqlite3_column_int(pStmt, 0); - const char *zFrom = (const char*)sqlite3_column_text(pStmt, 1); - int nFrom = zFrom ? sqlite3_column_bytes(pStmt, 1) : 0; - const char *zTo = (const char*)sqlite3_column_text(pStmt, 2); - int nTo = zTo ? sqlite3_column_bytes(pStmt, 2) : 0; - int iCost = sqlite3_column_int(pStmt, 3); - - assert( zFrom!=0 || nFrom==0 ); - assert( zTo!=0 || nTo==0 ); - if( nFrom>100 || nTo>100 ) continue; - if( iCost<0 ) continue; - if( pLang==0 || iLang!=iLangPrev ){ - EditDist3Lang *pNew; - pNew = sqlite3_realloc(p->a, (p->nLang+1)*sizeof(p->a[0])); - if( pNew==0 ){ rc = SQLITE_NOMEM; break; } - p->a = pNew; - pLang = &p->a[p->nLang]; - p->nLang++; - pLang->iLang = iLang; - pLang->iInsCost = 100; - pLang->iDelCost = 100; - pLang->iSubCost = 150; - pLang->pCost = 0; - iLangPrev = iLang; - } - if( nFrom==1 && zFrom[0]=='?' && nTo==0 ){ - pLang->iDelCost = iCost; - }else if( nFrom==0 && nTo==1 && zTo[0]=='?' ){ - pLang->iInsCost = iCost; - }else if( nFrom==1 && nTo==1 && zFrom[0]=='?' && zTo[0]=='?' ){ - pLang->iSubCost = iCost; - }else{ - EditDist3Cost *pCost; - int nExtra = nFrom + nTo - 4; - if( nExtra<0 ) nExtra = 0; - pCost = sqlite3_malloc( sizeof(*pCost) + nExtra ); - if( pCost==0 ){ rc = SQLITE_NOMEM; break; } - pCost->nFrom = nFrom; - pCost->nTo = nTo; - pCost->iCost = iCost; - memcpy(pCost->a, zFrom, nFrom); - memcpy(pCost->a + nFrom, zTo, nTo); - pCost->pNext = pLang->pCost; - pLang->pCost = pCost; - } - } - rc2 = sqlite3_finalize(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - return rc; -} - -/* -** Return the length (in bytes) of a utf-8 character. Or return a maximum -** of N. -*/ -static int utf8Len(unsigned char c, int N){ - int len = 1; - if( c>0x7f ){ - if( (c&0xe0)==0xc0 ){ - len = 2; - }else if( (c&0xf0)==0xe0 ){ - len = 3; - }else{ - len = 4; - } - } - if( len>N ) len = N; - return len; -} - -/* -** Return TRUE (non-zero) of the To side of the given cost matches -** the given string. -*/ -static int matchTo(EditDist3Cost *p, const char *z, int n){ - if( p->nTo>n ) return 0; - if( memcmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0; - return 1; -} - -/* -** Return TRUE (non-zero) of the To side of the given cost matches -** the given string. -*/ -static int matchFrom(EditDist3Cost *p, const char *z, int n){ - assert( p->nFrom<=n ); - if( memcmp(p->a, z, p->nFrom)!=0 ) return 0; - return 1; -} - -/* -** Return TRUE (non-zero) of the next FROM character and the next TO -** character are the same. -*/ -static int matchFromTo( - EditDist3FromString *pStr, /* Left hand string */ - int n1, /* Index of comparison character on the left */ - const char *z2, /* Right-handl comparison character */ - int n2 /* Bytes remaining in z2[] */ -){ - int b1 = pStr->a[n1].nByte; - if( b1>n2 ) return 0; - if( memcmp(pStr->z+n1, z2, b1)!=0 ) return 0; - return 1; -} - -/* -** Delete an EditDist3FromString objecct -*/ -static void editDist3FromStringDelete(EditDist3FromString *p){ - int i; - if( p ){ - for(i=0; in; i++){ - sqlite3_free(p->a[i].apDel); - sqlite3_free(p->a[i].apSubst); - } - sqlite3_free(p); - } -} - -/* -** Create a EditDist3FromString object. -*/ -static EditDist3FromString *editDist3FromStringNew( - const EditDist3Lang *pLang, - const char *z, - int n -){ - EditDist3FromString *pStr; - EditDist3Cost *p; - int i; - - if( z==0 ) return 0; - if( n<0 ) n = (int)strlen(z); - pStr = sqlite3_malloc( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 ); - if( pStr==0 ) return 0; - pStr->a = (EditDist3From*)&pStr[1]; - memset(pStr->a, 0, sizeof(pStr->a[0])*n); - pStr->n = n; - pStr->z = (char*)&pStr->a[n]; - memcpy(pStr->z, z, n+1); - if( n && z[n-1]=='*' ){ - pStr->isPrefix = 1; - n--; - pStr->n--; - pStr->z[n] = 0; - }else{ - pStr->isPrefix = 0; - } - - for(i=0; ia[i]; - memset(pFrom, 0, sizeof(*pFrom)); - pFrom->nByte = utf8Len((unsigned char)z[i], n-i); - for(p=pLang->pCost; p; p=p->pNext){ - EditDist3Cost **apNew; - if( i+p->nFrom>n ) continue; - if( matchFrom(p, z+i, n-i)==0 ) continue; - if( p->nTo==0 ){ - apNew = sqlite3_realloc(pFrom->apDel, - sizeof(*apNew)*(pFrom->nDel+1)); - if( apNew==0 ) break; - pFrom->apDel = apNew; - apNew[pFrom->nDel++] = p; - }else{ - apNew = sqlite3_realloc(pFrom->apSubst, - sizeof(*apNew)*(pFrom->nSubst+1)); - if( apNew==0 ) break; - pFrom->apSubst = apNew; - apNew[pFrom->nSubst++] = p; - } - } - if( p ){ - editDist3FromStringDelete(pStr); - pStr = 0; - break; - } - } - return pStr; -} - -/* -** Update entry m[i] such that it is the minimum of its current value -** and m[j]+iCost. -** -** If the iCost is 1,000,000 or greater, then consider the cost to be -** infinite and skip the update. -*/ -static void updateCost( - unsigned int *m, - int i, - int j, - int iCost -){ - assert( iCost>=0 ); - if( iCost<10000 ){ - unsigned int b = m[j] + iCost; - if( bpCost; p; p=p->pNext){ - EditDist3Cost **apNew; - if( p->nFrom>0 ) continue; - if( i2+p->nTo>n2 ) continue; - if( matchTo(p, z2+i2, n2-i2)==0 ) continue; - a2[i2].nIns++; - apNew = sqlite3_realloc(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns); - if( apNew==0 ){ - res = -1; /* Out of memory */ - goto editDist3Abort; - } - a2[i2].apIns = apNew; - a2[i2].apIns[a2[i2].nIns-1] = p; - } - } - - /* Prepare to compute the minimum edit distance */ - szRow = f.n+1; - memset(m, 0x01, (n2+1)*szRow*sizeof(m[0])); - m[0] = 0; - - /* First fill in the top-row of the matrix with FROM deletion costs */ - for(i1=0; i1iDelCost); - for(k=0; knFrom, i1, p->iCost); - } - } - - /* Fill in all subsequent rows, top-to-bottom, left-to-right */ - for(i2=0; i2iInsCost); - for(k=0; knTo), rxp, p->iCost); - } - for(i1=0; i1iDelCost); - for(k=0; knFrom, cxp, p->iCost); - } - updateCost(m, cx, cxu, pLang->iInsCost); - if( matchFromTo(&f, i1, z2+i2, n2-i2) ){ - updateCost(m, cx, cxd, 0); - } - updateCost(m, cx, cxd, pLang->iSubCost); - for(k=0; knFrom+szRow*p->nTo, cxd, p->iCost); - } - } - } - } - -#if 0 /* Enable for debugging */ - printf(" ^"); - for(i1=0; i19999 ) printf(" ****"); - else printf(" %4d", v); - } - printf("\n"); - for(i2=0; i29999 ) printf(" ****"); - else printf(" %4d", v); - } - printf("\n"); - } -#endif - - /* Free memory allocations and return the result */ - res = (int)m[szRow*(n2+1)-1]; - n = n2; - if( f.isPrefix ){ - for(i2=1; i2<=n2; i2++){ - int b = m[szRow*i2-1]; - if( b<=res ){ - res = b; - n = i2 - 1; - } - } - } - if( pnMatch ){ - int nExtra = 0; - for(k=0; knLang; i++){ - if( pConfig->a[i].iLang==iLang ) return &pConfig->a[i]; - } - return &editDist3Lang; -} - -/* -** Function: editdist3(A,B,iLang) -** editdist3(tablename) -** -** Return the cost of transforming string A into string B using edit -** weights for iLang. -** -** The second form loads edit weights into memory from a table. -*/ -static void editDist3SqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - EditDist3Config *pConfig = (EditDist3Config*)sqlite3_user_data(context); - sqlite3 *db = sqlite3_context_db_handle(context); - int rc; - if( argc==1 ){ - const char *zTable = (const char*)sqlite3_value_text(argv[0]); - rc = editDist3ConfigLoad(pConfig, db, zTable); - if( rc ) sqlite3_result_error_code(context, rc); - }else{ - const char *zA = (const char*)sqlite3_value_text(argv[0]); - const char *zB = (const char*)sqlite3_value_text(argv[1]); - int nA = sqlite3_value_bytes(argv[0]); - int nB = sqlite3_value_bytes(argv[1]); - int iLang = argc==3 ? sqlite3_value_int(argv[2]) : 0; - const EditDist3Lang *pLang = editDist3FindLang(pConfig, iLang); - EditDist3FromString *pFrom; - int dist; - - pFrom = editDist3FromStringNew(pLang, zA, nA); - if( pFrom==0 ){ - sqlite3_result_error_nomem(context); - return; - } - dist = editDist3Core(pFrom, zB, nB, pLang, 0); - editDist3FromStringDelete(pFrom); - if( dist==(-1) ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_int(context, dist); - } - } -} - -/* -** Register the editDist3 function with SQLite -*/ -static int editDist3Install(sqlite3 *db){ - int rc; - EditDist3Config *pConfig = sqlite3_malloc( sizeof(*pConfig) ); - if( pConfig==0 ) return SQLITE_NOMEM; - memset(pConfig, 0, sizeof(*pConfig)); - rc = sqlite3_create_function_v2(db, "editdist3", - 2, SQLITE_UTF8, pConfig, editDist3SqlFunc, 0, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function_v2(db, "editdist3", - 3, SQLITE_UTF8, pConfig, editDist3SqlFunc, 0, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function_v2(db, "editdist3", - 1, SQLITE_UTF8, pConfig, editDist3SqlFunc, 0, 0, - editDist3ConfigDelete); - }else{ - sqlite3_free(pConfig); - } - return rc; -} -/* End configurable cost unicode edit distance routines -****************************************************************************** -****************************************************************************** -** Begin transliterate unicode-to-ascii implementation -*/ - -#if !SQLITE_AMALGAMATION -/* -** This lookup table is used to help decode the first byte of -** a multi-byte UTF8 character. -*/ -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, -}; -#endif - -/* -** Return the value of the first UTF-8 character in the string. -*/ -static int utf8Read(const unsigned char *z, int n, int *pSize){ - int c, i; - - /* All callers to this routine (in the current implementation) - ** always have n>0. */ - if( NEVER(n==0) ){ - c = i = 0; - }else{ - c = z[0]; - i = 1; - if( c>=0xc0 ){ - c = sqlite3Utf8Trans1[c-0xc0]; - while( i0 ){ - c = utf8Read(zIn, nIn, &sz); - zIn += sz; - nIn -= sz; - if( c<=127 ){ - zOut[nOut++] = c; - }else{ - int xTop, xBtm, x; - xTop = sizeof(translit)/sizeof(translit[0]) - 1; - xBtm = 0; - while( xTop>=xBtm ){ - x = (xTop + xBtm)/2; - if( translit[x].cFrom==c ){ - zOut[nOut++] = translit[x].cTo0; - if( translit[x].cTo1 ){ - zOut[nOut++] = translit[x].cTo1; - /* Add an extra "ch" after the "sh" for Щ and щ */ - if( c==0x0429 || c== 0x0449 ){ - zOut[nOut++] = 'c'; - zOut[nOut++] = 'h'; - } - } - c = 0; - break; - }else if( translit[x].cFrom>c ){ - xTop = x-1; - }else{ - xBtm = x+1; - } - } - if( c ) zOut[nOut++] = '?'; - } - } - zOut[nOut] = 0; - return zOut; -} - -/* -** Return the number of characters in the shortest prefix of the input -** string that transliterates to an ASCII string nTrans bytes or longer. -** Or, if the transliteration of the input string is less than nTrans -** bytes in size, return the number of characters in the input string. -*/ -static int translen_to_charlen(const char *zIn, int nIn, int nTrans){ - int i, c, sz, nOut; - int nChar; - - i = nOut = 0; - for(nChar=0; i=128 ){ - int xTop, xBtm, x; - xTop = sizeof(translit)/sizeof(translit[0]) - 1; - xBtm = 0; - while( xTop>=xBtm ){ - x = (xTop + xBtm)/2; - if( translit[x].cFrom==c ){ - if( translit[x].cTo1 ) nOut++; - if( c==0x0429 || c== 0x0449 ) nOut += 2; - break; - }else if( translit[x].cFrom>c ){ - xTop = x-1; - }else{ - xBtm = x+1; - } - } - } - } - - return nChar; -} - - -/* -** spellfix1_translit(X) -** -** Convert a string that contains non-ASCII Roman characters into -** pure ASCII. -*/ -static void transliterateSqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn = sqlite3_value_text(argv[0]); - int nIn = sqlite3_value_bytes(argv[0]); - unsigned char *zOut = transliterate(zIn, nIn); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free); - } -} - -/* -** spellfix1_scriptcode(X) -** -** Try to determine the dominant script used by the word X and return -** its ISO 15924 numeric code. -** -** The current implementation only understands the following scripts: -** -** 215 (Latin) -** 220 (Cyrillic) -** 200 (Greek) -** -** This routine will return 998 if the input X contains characters from -** two or more of the above scripts or 999 if X contains no characters -** from any of the above scripts. -*/ -static void scriptCodeSqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn = sqlite3_value_text(argv[0]); - int nIn = sqlite3_value_bytes(argv[0]); - int c, sz; - int scriptMask = 0; - int res; -# define SCRIPT_LATIN 0x0001 -# define SCRIPT_CYRILLIC 0x0002 -# define SCRIPT_GREEK 0x0004 - - while( nIn>0 ){ - c = utf8Read(zIn, nIn, &sz); - zIn += sz; - nIn -= sz; - if( c<0x02af ){ - scriptMask |= SCRIPT_LATIN; - }else if( c>=0x0400 && c<=0x04ff ){ - scriptMask |= SCRIPT_CYRILLIC; - }else if( c>=0x0386 && c<=0x03ce ){ - scriptMask |= SCRIPT_GREEK; - } - } - switch( scriptMask ){ - case 0: res = 999; break; - case SCRIPT_LATIN: res = 215; break; - case SCRIPT_CYRILLIC: res = 220; break; - case SCRIPT_GREEK: res = 200; break; - default: res = 998; break; - } - sqlite3_result_int(context, res); -} - -/* End transliterate -****************************************************************************** -****************************************************************************** -** Begin spellfix1 virtual table. -*/ - -/* Maximum length of a phonehash used for querying the shadow table */ -#define SPELLFIX_MX_HASH 8 - -/* Maximum number of hash strings to examine per query */ -#define SPELLFIX_MX_RUN 1 - -typedef struct spellfix1_vtab spellfix1_vtab; -typedef struct spellfix1_cursor spellfix1_cursor; - -/* Fuzzy-search virtual table object */ -struct spellfix1_vtab { - sqlite3_vtab base; /* Base class - must be first */ - sqlite3 *db; /* Database connection */ - char *zDbName; /* Name of database holding this table */ - char *zTableName; /* Name of the virtual table */ - char *zCostTable; /* Table holding edit-distance cost numbers */ - EditDist3Config *pConfig3; /* Parsed edit distance costs */ -}; - -/* Fuzzy-search cursor object */ -struct spellfix1_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - spellfix1_vtab *pVTab; /* The table to which this cursor belongs */ - char *zPattern; /* rhs of MATCH clause */ - int nRow; /* Number of rows of content */ - int nAlloc; /* Number of allocated rows */ - int iRow; /* Current row of content */ - int iLang; /* Value of the langid= constraint */ - int iTop; /* Value of the top= constraint */ - int iScope; /* Value of the scope= constraint */ - int nSearch; /* Number of vocabulary items checked */ - sqlite3_stmt *pFullScan; /* Shadow query for a full table scan */ - struct spellfix1_row { /* For each row of content */ - sqlite3_int64 iRowid; /* Rowid for this row */ - char *zWord; /* Text for this row */ - int iRank; /* Rank for this row */ - int iDistance; /* Distance from pattern for this row */ - int iScore; /* Score for sorting */ - int iMatchlen; /* Value of matchlen column (or -1) */ - char zHash[SPELLFIX_MX_HASH]; /* the phonehash used for this match */ - } *a; -}; - -/* -** Construct one or more SQL statements from the format string given -** and then evaluate those statements. The success code is written -** into *pRc. -** -** If *pRc is initially non-zero then this routine is a no-op. -*/ -static void spellfix1DbExec( - int *pRc, /* Success code */ - sqlite3 *db, /* Database in which to run SQL */ - const char *zFormat, /* Format string for SQL */ - ... /* Arguments to the format string */ -){ - va_list ap; - char *zSql; - if( *pRc ) return; - va_start(ap, zFormat); - zSql = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - if( zSql==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - *pRc = sqlite3_exec(db, zSql, 0, 0, 0); - sqlite3_free(zSql); - } -} - -/* -** xDisconnect/xDestroy method for the fuzzy-search module. -*/ -static int spellfix1Uninit(int isDestroy, sqlite3_vtab *pVTab){ - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - int rc = SQLITE_OK; - if( isDestroy ){ - sqlite3 *db = p->db; - spellfix1DbExec(&rc, db, "DROP TABLE IF EXISTS \"%w\".\"%w_vocab\"", - p->zDbName, p->zTableName); - } - if( rc==SQLITE_OK ){ - sqlite3_free(p->zTableName); - editDist3ConfigDelete(p->pConfig3); - sqlite3_free(p->zCostTable); - sqlite3_free(p); - } - return rc; -} -static int spellfix1Disconnect(sqlite3_vtab *pVTab){ - return spellfix1Uninit(0, pVTab); -} -static int spellfix1Destroy(sqlite3_vtab *pVTab){ - return spellfix1Uninit(1, pVTab); -} - -/* -** Make a copy of a string. Remove leading and trailing whitespace -** and dequote it. -*/ -static char *spellfix1Dequote(const char *zIn){ - char *zOut; - int i, j; - char c; - while( isspace(zIn[0]) ) zIn++; - zOut = sqlite3_mprintf("%s", zIn); - if( zOut==0 ) return 0; - i = (int)strlen(zOut); -#if 0 /* The parser will never leave spaces at the end */ - while( i>0 && isspace(zOut[i-1]) ){ i--; } -#endif - zOut[i] = 0; - c = zOut[0]; - if( c=='\'' || c=='"' ){ - for(i=1, j=0; ALWAYS(zOut[i]); i++){ - zOut[j++] = zOut[i]; - if( zOut[i]==c ){ - if( zOut[i+1]==c ){ - i++; - }else{ - zOut[j-1] = 0; - break; - } - } - } - } - return zOut; -} - - -/* -** xConnect/xCreate method for the spellfix1 module. Arguments are: -** -** argv[0] -> module name ("spellfix1") -** argv[1] -> database name -** argv[2] -> table name -** argv[3].. -> optional arguments (i.e. "edit_cost_table" parameter) -*/ -static int spellfix1Init( - int isCreate, - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - spellfix1_vtab *pNew = 0; - const char *zModule = argv[0]; - const char *zDbName = argv[1]; - const char *zTableName = argv[2]; - int nDbName; - int rc = SQLITE_OK; - int i; - - nDbName = (int)strlen(zDbName); - pNew = sqlite3_malloc( sizeof(*pNew) + nDbName + 1); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(*pNew)); - pNew->zDbName = (char*)&pNew[1]; - memcpy(pNew->zDbName, zDbName, nDbName+1); - pNew->zTableName = sqlite3_mprintf("%s", zTableName); - pNew->db = db; - if( pNew->zTableName==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(word,rank,distance,langid, " - "score, matchlen, phonehash HIDDEN, " - "top HIDDEN, scope HIDDEN, srchcnt HIDDEN, " - "soundslike HIDDEN, command HIDDEN)" - ); -#define SPELLFIX_COL_WORD 0 -#define SPELLFIX_COL_RANK 1 -#define SPELLFIX_COL_DISTANCE 2 -#define SPELLFIX_COL_LANGID 3 -#define SPELLFIX_COL_SCORE 4 -#define SPELLFIX_COL_MATCHLEN 5 -#define SPELLFIX_COL_PHONEHASH 6 -#define SPELLFIX_COL_TOP 7 -#define SPELLFIX_COL_SCOPE 8 -#define SPELLFIX_COL_SRCHCNT 9 -#define SPELLFIX_COL_SOUNDSLIKE 10 -#define SPELLFIX_COL_COMMAND 11 - } - if( rc==SQLITE_OK && isCreate ){ - sqlite3_uint64 r; - spellfix1DbExec(&rc, db, - "CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n" - " id INTEGER PRIMARY KEY,\n" - " rank INT,\n" - " langid INT,\n" - " word TEXT,\n" - " k1 TEXT,\n" - " k2 TEXT\n" - ");\n", - zDbName, zTableName - ); - sqlite3_randomness(sizeof(r), &r); - spellfix1DbExec(&rc, db, - "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_index_%llx\" " - "ON \"%w_vocab\"(langid,k2);", - zDbName, zModule, r, zTableName - ); - } - for(i=3; rc==SQLITE_OK && ibase); - }else{ - *ppVTab = (sqlite3_vtab *)pNew; - } - return rc; -} - -/* -** The xConnect and xCreate methods -*/ -static int spellfix1Connect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - return spellfix1Init(0, db, pAux, argc, argv, ppVTab, pzErr); -} -static int spellfix1Create( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - return spellfix1Init(1, db, pAux, argc, argv, ppVTab, pzErr); -} - -/* -** Clear all of the content from a cursor. -*/ -static void spellfix1ResetCursor(spellfix1_cursor *pCur){ - int i; - for(i=0; inRow; i++){ - sqlite3_free(pCur->a[i].zWord); - } - pCur->nRow = 0; - pCur->iRow = 0; - pCur->nSearch = 0; - if( pCur->pFullScan ){ - sqlite3_finalize(pCur->pFullScan); - pCur->pFullScan = 0; - } -} - -/* -** Resize the cursor to hold up to N rows of content -*/ -static void spellfix1ResizeCursor(spellfix1_cursor *pCur, int N){ - struct spellfix1_row *aNew; - assert( N>=pCur->nRow ); - aNew = sqlite3_realloc(pCur->a, sizeof(pCur->a[0])*N); - if( aNew==0 && N>0 ){ - spellfix1ResetCursor(pCur); - sqlite3_free(pCur->a); - pCur->nAlloc = 0; - pCur->a = 0; - }else{ - pCur->nAlloc = N; - pCur->a = aNew; - } -} - - -/* -** Close a fuzzy-search cursor. -*/ -static int spellfix1Close(sqlite3_vtab_cursor *cur){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - spellfix1ResetCursor(pCur); - spellfix1ResizeCursor(pCur, 0); - sqlite3_free(pCur->zPattern); - sqlite3_free(pCur); - return SQLITE_OK; -} - -/* -** Search for terms of these forms: -** -** (A) word MATCH $str -** (B) langid == $langid -** (C) top = $top -** (D) scope = $scope -** (E) distance < $distance -** (F) distance <= $distance -** -** The plan number is a bit mask formed with these bits: -** -** 0x01 (A) is found -** 0x02 (B) is found -** 0x04 (C) is found -** 0x08 (D) is found -** 0x10 (E) is found -** 0x20 (F) is found -** -** filter.argv[*] values contains $str, $langid, $top, and $scope, -** if specified and in that order. -*/ -static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int iPlan = 0; - int iLangTerm = -1; - int iTopTerm = -1; - int iScopeTerm = -1; - int iDistTerm = -1; - int i; - const struct sqlite3_index_constraint *pConstraint; - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - - /* Terms of the form: word MATCH $str */ - if( (iPlan & 1)==0 - && pConstraint->iColumn==SPELLFIX_COL_WORD - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH - ){ - iPlan |= 1; - pIdxInfo->aConstraintUsage[i].argvIndex = 1; - pIdxInfo->aConstraintUsage[i].omit = 1; - } - - /* Terms of the form: langid = $langid */ - if( (iPlan & 2)==0 - && pConstraint->iColumn==SPELLFIX_COL_LANGID - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= 2; - iLangTerm = i; - } - - /* Terms of the form: top = $top */ - if( (iPlan & 4)==0 - && pConstraint->iColumn==SPELLFIX_COL_TOP - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= 4; - iTopTerm = i; - } - - /* Terms of the form: scope = $scope */ - if( (iPlan & 8)==0 - && pConstraint->iColumn==SPELLFIX_COL_SCOPE - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= 8; - iScopeTerm = i; - } - - /* Terms of the form: distance < $dist or distance <= $dist */ - if( (iPlan & (16|32))==0 - && pConstraint->iColumn==SPELLFIX_COL_DISTANCE - && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT - || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) - ){ - iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32; - iDistTerm = i; - } - } - if( iPlan&1 ){ - int idx = 2; - pIdxInfo->idxNum = iPlan; - if( pIdxInfo->nOrderBy==1 - && pIdxInfo->aOrderBy[0].iColumn==SPELLFIX_COL_SCORE - && pIdxInfo->aOrderBy[0].desc==0 - ){ - pIdxInfo->orderByConsumed = 1; /* Default order by iScore */ - } - if( iPlan&2 ){ - pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iLangTerm].omit = 1; - } - if( iPlan&4 ){ - pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iTopTerm].omit = 1; - } - if( iPlan&8 ){ - pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1; - } - if( iPlan&(16|32) ){ - pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iDistTerm].omit = 1; - } - pIdxInfo->estimatedCost = (double)10000; - }else{ - pIdxInfo->idxNum = 0; - pIdxInfo->estimatedCost = (double)10000000; - } - return SQLITE_OK; -} - -/* -** Open a new fuzzy-search cursor. -*/ -static int spellfix1Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - spellfix1_cursor *pCur; - pCur = sqlite3_malloc( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - pCur->pVTab = p; - *ppCursor = &pCur->base; - return SQLITE_OK; -} - -/* -** Adjust a distance measurement by the words rank in order to show -** preference to common words. -*/ -static int spellfix1Score(int iDistance, int iRank){ - int iLog2; - for(iLog2=0; iRank>0; iLog2++, iRank>>=1){} - return iDistance + 32 - iLog2; -} - -/* -** Compare two spellfix1_row objects for sorting purposes in qsort() such -** that they sort in order of increasing distance. -*/ -static int spellfix1RowCompare(const void *A, const void *B){ - const struct spellfix1_row *a = (const struct spellfix1_row*)A; - const struct spellfix1_row *b = (const struct spellfix1_row*)B; - return a->iScore - b->iScore; -} - -/* -** A structure used to pass information from spellfix1FilterForMatch() -** into spellfix1RunQuery(). -*/ -typedef struct MatchQuery { - spellfix1_cursor *pCur; /* The cursor being queried */ - sqlite3_stmt *pStmt; /* shadow table query statment */ - char zHash[SPELLFIX_MX_HASH]; /* The current phonehash for zPattern */ - const char *zPattern; /* Transliterated input string */ - int nPattern; /* Length of zPattern */ - EditDist3FromString *pMatchStr3; /* Original unicode string */ - EditDist3Config *pConfig3; /* Edit-distance cost coefficients */ - const EditDist3Lang *pLang; /* The selected language coefficients */ - int iLang; /* The language id */ - int iScope; /* Default scope */ - int iMaxDist; /* Maximum allowed edit distance, or -1 */ - int rc; /* Error code */ - int nRun; /* Number of prior runs for the same zPattern */ - char azPrior[SPELLFIX_MX_RUN][SPELLFIX_MX_HASH]; /* Prior hashes */ -} MatchQuery; - -/* -** Run a query looking for the best matches against zPattern using -** zHash as the character class seed hash. -*/ -static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){ - const char *zK1; - const char *zWord; - int iDist; - int iRank; - int iScore; - int iWorst = 0; - int idx; - int idxWorst = -1; - int i; - int iScope = p->iScope; - spellfix1_cursor *pCur = p->pCur; - sqlite3_stmt *pStmt = p->pStmt; - char zHash1[SPELLFIX_MX_HASH]; - char zHash2[SPELLFIX_MX_HASH]; - char *zClass; - int nClass; - int rc; - - if( pCur->a==0 || p->rc ) return; /* Prior memory allocation failure */ - zClass = (char*)phoneticHash((unsigned char*)zQuery, nQuery); - if( zClass==0 ){ - p->rc = SQLITE_NOMEM; - return; - } - nClass = (int)strlen(zClass); - if( nClass>SPELLFIX_MX_HASH-2 ){ - nClass = SPELLFIX_MX_HASH-2; - zClass[nClass] = 0; - } - if( nClass<=iScope ){ - if( nClass>2 ){ - iScope = nClass-1; - }else{ - iScope = nClass; - } - } - memcpy(zHash1, zClass, iScope); - sqlite3_free(zClass); - zHash1[iScope] = 0; - memcpy(zHash2, zHash1, iScope); - zHash2[iScope] = 'Z'; - zHash2[iScope+1] = 0; -#if SPELLFIX_MX_RUN>1 - for(i=0; inRun; i++){ - if( strcmp(p->azPrior[i], zHash1)==0 ) return; - } -#endif - assert( p->nRunazPrior[p->nRun++], zHash1, iScope+1); - if( sqlite3_bind_text(pStmt, 1, zHash1, -1, SQLITE_STATIC)==SQLITE_NOMEM - || sqlite3_bind_text(pStmt, 2, zHash2, -1, SQLITE_STATIC)==SQLITE_NOMEM - ){ - p->rc = SQLITE_NOMEM; - return; - } -#if SPELLFIX_MX_RUN>1 - for(i=0; inRow; i++){ - if( pCur->a[i].iScore>iWorst ){ - iWorst = pCur->a[i].iScore; - idxWorst = i; - } - } -#endif - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - int iMatchlen = -1; - iRank = sqlite3_column_int(pStmt, 2); - if( p->pMatchStr3 ){ - int nWord = sqlite3_column_bytes(pStmt, 1); - zWord = (const char*)sqlite3_column_text(pStmt, 1); - iDist = editDist3Core(p->pMatchStr3, zWord, nWord, p->pLang, &iMatchlen); - }else{ - zK1 = (const char*)sqlite3_column_text(pStmt, 3); - if( zK1==0 ) continue; - iDist = editdist1(p->zPattern, zK1, 0); - } - if( iDist<0 ){ - p->rc = SQLITE_NOMEM; - break; - } - pCur->nSearch++; - iScore = spellfix1Score(iDist,iRank); - if( p->iMaxDist>=0 ){ - if( iDist>p->iMaxDist ) continue; - if( pCur->nRow>=pCur->nAlloc-1 ){ - spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10); - if( pCur->a==0 ) break; - } - idx = pCur->nRow; - }else if( pCur->nRownAlloc ){ - idx = pCur->nRow; - }else if( iScorea[idx].zWord); - }else{ - continue; - } - pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); - if( pCur->a[idx].zWord==0 ){ - p->rc = SQLITE_NOMEM; - break; - } - pCur->a[idx].iRowid = sqlite3_column_int64(pStmt, 0); - pCur->a[idx].iRank = iRank; - pCur->a[idx].iDistance = iDist; - pCur->a[idx].iScore = iScore; - pCur->a[idx].iMatchlen = iMatchlen; - memcpy(pCur->a[idx].zHash, zHash1, iScope+1); - if( pCur->nRownAlloc ) pCur->nRow++; - if( pCur->nRow==pCur->nAlloc ){ - iWorst = pCur->a[0].iScore; - idxWorst = 0; - for(i=1; inRow; i++){ - iScore = pCur->a[i].iScore; - if( iWorstrc = rc; -} - -/* -** This version of the xFilter method work if the MATCH term is present -** and we are doing a scan. -*/ -static int spellfix1FilterForMatch( - spellfix1_cursor *pCur, - int idxNum, - int argc, - sqlite3_value **argv -){ - const unsigned char *zMatchThis; /* RHS of the MATCH operator */ - EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */ - char *zPattern; /* Transliteration of zMatchThis */ - int nPattern; /* Length of zPattern */ - int iLimit = 20; /* Max number of rows of output */ - int iScope = 3; /* Use this many characters of zClass */ - int iLang = 0; /* Language code */ - char *zSql; /* SQL of shadow table query */ - sqlite3_stmt *pStmt = 0; /* Shadow table query */ - int rc; /* Result code */ - int idx = 1; /* Next available filter parameter */ - spellfix1_vtab *p = pCur->pVTab; /* The virtual table that owns pCur */ - MatchQuery x; /* For passing info to RunQuery() */ - - /* Load the cost table if we have not already done so */ - if( p->zCostTable!=0 && p->pConfig3==0 ){ - p->pConfig3 = sqlite3_malloc( sizeof(p->pConfig3[0]) ); - if( p->pConfig3==0 ) return SQLITE_NOMEM; - memset(p->pConfig3, 0, sizeof(p->pConfig3[0])); - rc = editDist3ConfigLoad(p->pConfig3, p->db, p->zCostTable); - if( rc ) return rc; - } - memset(&x, 0, sizeof(x)); - x.iScope = 3; /* Default scope if none specified by "WHERE scope=N" */ - x.iMaxDist = -1; /* Maximum allowed edit distance */ - - if( idxNum&2 ){ - iLang = sqlite3_value_int(argv[idx++]); - } - if( idxNum&4 ){ - iLimit = sqlite3_value_int(argv[idx++]); - if( iLimit<1 ) iLimit = 1; - } - if( idxNum&8 ){ - x.iScope = sqlite3_value_int(argv[idx++]); - if( x.iScope<1 ) x.iScope = 1; - if( x.iScope>SPELLFIX_MX_HASH-2 ) x.iScope = SPELLFIX_MX_HASH-2; - } - if( idxNum&(16|32) ){ - x.iMaxDist = sqlite3_value_int(argv[idx++]); - if( idxNum&16 ) x.iMaxDist--; - if( x.iMaxDist<0 ) x.iMaxDist = 0; - } - spellfix1ResetCursor(pCur); - spellfix1ResizeCursor(pCur, iLimit); - zMatchThis = sqlite3_value_text(argv[0]); - if( zMatchThis==0 ) return SQLITE_OK; - if( p->pConfig3 ){ - x.pLang = editDist3FindLang(p->pConfig3, iLang); - pMatchStr3 = editDist3FromStringNew(x.pLang, (const char*)zMatchThis, -1); - if( pMatchStr3==0 ){ - x.rc = SQLITE_NOMEM; - goto filter_exit; - } - }else{ - x.pLang = 0; - } - zPattern = (char*)transliterate(zMatchThis, sqlite3_value_bytes(argv[0])); - sqlite3_free(pCur->zPattern); - pCur->zPattern = zPattern; - if( zPattern==0 ){ - x.rc = SQLITE_NOMEM; - goto filter_exit; - } - nPattern = (int)strlen(zPattern); - if( zPattern[nPattern-1]=='*' ) nPattern--; - zSql = sqlite3_mprintf( - "SELECT id, word, rank, k1" - " FROM \"%w\".\"%w_vocab\"" - " WHERE langid=%d AND k2>=?1 AND k2zDbName, p->zTableName, iLang - ); - if( zSql==0 ){ - x.rc = SQLITE_NOMEM; - pStmt = 0; - goto filter_exit; - } - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - pCur->iLang = iLang; - x.pCur = pCur; - x.pStmt = pStmt; - x.zPattern = zPattern; - x.nPattern = nPattern; - x.pMatchStr3 = pMatchStr3; - x.iLang = iLang; - x.rc = rc; - x.pConfig3 = p->pConfig3; - if( x.rc==SQLITE_OK ){ - spellfix1RunQuery(&x, zPattern, nPattern); - } - - if( pCur->a ){ - qsort(pCur->a, pCur->nRow, sizeof(pCur->a[0]), spellfix1RowCompare); - pCur->iTop = iLimit; - pCur->iScope = iScope; - }else{ - x.rc = SQLITE_NOMEM; - } - -filter_exit: - sqlite3_finalize(pStmt); - editDist3FromStringDelete(pMatchStr3); - return x.rc; -} - -/* -** This version of xFilter handles a full-table scan case -*/ -static int spellfix1FilterForFullScan( - spellfix1_cursor *pCur, - int idxNum, - int argc, - sqlite3_value **argv -){ - int rc; - char *zSql; - spellfix1_vtab *pVTab = pCur->pVTab; - spellfix1ResetCursor(pCur); - zSql = sqlite3_mprintf( - "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"", - pVTab->zDbName, pVTab->zTableName); - if( zSql==0 ) return SQLITE_NOMEM; - rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0); - sqlite3_free(zSql); - pCur->nRow = pCur->iRow = 0; - if( rc==SQLITE_OK ){ - rc = sqlite3_step(pCur->pFullScan); - if( rc==SQLITE_ROW ){ pCur->iRow = -1; rc = SQLITE_OK; } - if( rc==SQLITE_DONE ){ rc = SQLITE_OK; } - }else{ - pCur->iRow = 0; - } - return rc; -} - - -/* -** Called to "rewind" a cursor back to the beginning so that -** it starts its output over again. Always called at least once -** prior to any spellfix1Column, spellfix1Rowid, or spellfix1Eof call. -*/ -static int spellfix1Filter( - sqlite3_vtab_cursor *cur, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - int rc; - if( idxNum & 1 ){ - rc = spellfix1FilterForMatch(pCur, idxNum, argc, argv); - }else{ - rc = spellfix1FilterForFullScan(pCur, idxNum, argc, argv); - } - return rc; -} - - -/* -** Advance a cursor to its next row of output -*/ -static int spellfix1Next(sqlite3_vtab_cursor *cur){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - int rc = SQLITE_OK; - if( pCur->iRow < pCur->nRow ){ - if( pCur->pFullScan ){ - rc = sqlite3_step(pCur->pFullScan); - if( rc!=SQLITE_ROW ) pCur->iRow = pCur->nRow; - if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK; - }else{ - pCur->iRow++; - } - } - return rc; -} - -/* -** Return TRUE if we are at the end-of-file -*/ -static int spellfix1Eof(sqlite3_vtab_cursor *cur){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - return pCur->iRow>=pCur->nRow; -} - -/* -** Return columns from the current row. -*/ -static int spellfix1Column( - sqlite3_vtab_cursor *cur, - sqlite3_context *ctx, - int i -){ - spellfix1_cursor *pCur = (spellfix1_cursor*)cur; - if( pCur->pFullScan ){ - if( i<=SPELLFIX_COL_LANGID ){ - sqlite3_result_value(ctx, sqlite3_column_value(pCur->pFullScan, i)); - }else{ - sqlite3_result_null(ctx); - } - return SQLITE_OK; - } - switch( i ){ - case SPELLFIX_COL_WORD: { - sqlite3_result_text(ctx, pCur->a[pCur->iRow].zWord, -1, SQLITE_STATIC); - break; - } - case SPELLFIX_COL_RANK: { - sqlite3_result_int(ctx, pCur->a[pCur->iRow].iRank); - break; - } - case SPELLFIX_COL_DISTANCE: { - sqlite3_result_int(ctx, pCur->a[pCur->iRow].iDistance); - break; - } - case SPELLFIX_COL_LANGID: { - sqlite3_result_int(ctx, pCur->iLang); - break; - } - case SPELLFIX_COL_SCORE: { - sqlite3_result_int(ctx, pCur->a[pCur->iRow].iScore); - break; - } - case SPELLFIX_COL_MATCHLEN: { - int iMatchlen = pCur->a[pCur->iRow].iMatchlen; - if( iMatchlen<0 ){ - int nPattern = (int)strlen(pCur->zPattern); - char *zWord = pCur->a[pCur->iRow].zWord; - int nWord = (int)strlen(zWord); - - if( nPattern>0 && pCur->zPattern[nPattern-1]=='*' ){ - char *zTranslit; - int res; - zTranslit = (char *)transliterate((unsigned char *)zWord, nWord); - if( !zTranslit ) return SQLITE_NOMEM; - res = editdist1(pCur->zPattern, zTranslit, &iMatchlen); - sqlite3_free(zTranslit); - if( res<0 ) return SQLITE_NOMEM; - iMatchlen = translen_to_charlen(zWord, nWord, iMatchlen); - }else{ - iMatchlen = utf8Charlen(zWord, nWord); - } - } - - sqlite3_result_int(ctx, iMatchlen); - break; - } - case SPELLFIX_COL_PHONEHASH: { - sqlite3_result_text(ctx, pCur->a[pCur->iRow].zHash, -1, SQLITE_STATIC); - break; - } - case SPELLFIX_COL_TOP: { - sqlite3_result_int(ctx, pCur->iTop); - break; - } - case SPELLFIX_COL_SCOPE: { - sqlite3_result_int(ctx, pCur->iScope); - break; - } - case SPELLFIX_COL_SRCHCNT: { - sqlite3_result_int(ctx, pCur->nSearch); - break; - } - default: { - sqlite3_result_null(ctx); - break; - } - } - return SQLITE_OK; -} - -/* -** The rowid. -*/ -static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - spellfix1_cursor *pCur = (spellfix1_cursor*)cur; - if( pCur->pFullScan ){ - *pRowid = sqlite3_column_int64(pCur->pFullScan, 4); - }else{ - *pRowid = pCur->a[pCur->iRow].iRowid; - } - return SQLITE_OK; -} - -/* -** The xUpdate() method. -*/ -static int spellfix1Update( - sqlite3_vtab *pVTab, - int argc, - sqlite3_value **argv, - sqlite_int64 *pRowid -){ - int rc = SQLITE_OK; - sqlite3_int64 rowid, newRowid; - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - sqlite3 *db = p->db; - - if( argc==1 ){ - /* A delete operation on the rowid given by argv[0] */ - rowid = *pRowid = sqlite3_value_int64(argv[0]); - spellfix1DbExec(&rc, db, "DELETE FROM \"%w\".\"%w_vocab\" " - " WHERE id=%lld", - p->zDbName, p->zTableName, rowid); - }else{ - const unsigned char *zWord = sqlite3_value_text(argv[SPELLFIX_COL_WORD+2]); - int nWord = sqlite3_value_bytes(argv[SPELLFIX_COL_WORD+2]); - int iLang = sqlite3_value_int(argv[SPELLFIX_COL_LANGID+2]); - int iRank = sqlite3_value_int(argv[SPELLFIX_COL_RANK+2]); - const unsigned char *zSoundslike = - sqlite3_value_text(argv[SPELLFIX_COL_SOUNDSLIKE+2]); - int nSoundslike = sqlite3_value_bytes(argv[SPELLFIX_COL_SOUNDSLIKE+2]); - char *zK1, *zK2; - int i; - char c; - - if( zWord==0 ){ - /* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy'); - ** cause zWord to be NULL, so we look at the "command" column to see - ** what special actions to take */ - const char *zCmd = - (const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]); - if( zCmd==0 ){ - pVTab->zErrMsg = sqlite3_mprintf("%s.word may not be NULL", - p->zTableName); - return SQLITE_CONSTRAINT; - } - if( strcmp(zCmd,"reset")==0 ){ - /* Reset the edit cost table (if there is one). */ - editDist3ConfigDelete(p->pConfig3); - p->pConfig3 = 0; - return SQLITE_OK; - } - pVTab->zErrMsg = sqlite3_mprintf("unknown value for %s.command: \"%w\"", - p->zTableName, zCmd); - return SQLITE_ERROR; - } - if( iRank<1 ) iRank = 1; - if( zSoundslike ){ - zK1 = (char*)transliterate(zSoundslike, nSoundslike); - }else{ - zK1 = (char*)transliterate(zWord, nWord); - } - if( zK1==0 ) return SQLITE_NOMEM; - for(i=0; (c = zK1[i])!=0; i++){ - if( c>='A' && c<='Z' ) zK1[i] += 'a' - 'A'; - } - zK2 = (char*)phoneticHash((const unsigned char*)zK1, i); - if( zK2==0 ){ - sqlite3_free(zK1); - return SQLITE_NOMEM; - } - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ - spellfix1DbExec(&rc, db, - "INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) " - "VALUES(%d,%d,%Q,%Q,%Q)", - p->zDbName, p->zTableName, - iRank, iLang, zWord, zK1, zK2 - ); - *pRowid = sqlite3_last_insert_rowid(db); - }else{ - rowid = sqlite3_value_int64(argv[0]); - newRowid = *pRowid = sqlite3_value_int64(argv[1]); - spellfix1DbExec(&rc, db, - "UPDATE \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d," - " word=%Q, k1=%Q, k2=%Q WHERE id=%lld", - p->zDbName, p->zTableName, newRowid, iRank, iLang, - zWord, zK1, zK2, rowid - ); - } - sqlite3_free(zK1); - sqlite3_free(zK2); - } - return rc; -} - -/* -** Rename the spellfix1 table. -*/ -static int spellfix1Rename(sqlite3_vtab *pVTab, const char *zNew){ - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - sqlite3 *db = p->db; - int rc = SQLITE_OK; - char *zNewName = sqlite3_mprintf("%s", zNew); - if( zNewName==0 ){ - return SQLITE_NOMEM; - } - spellfix1DbExec(&rc, db, - "ALTER TABLE \"%w\".\"%w_vocab\" RENAME TO \"%w_vocab\"", - p->zDbName, p->zTableName, zNewName - ); - if( rc==SQLITE_OK ){ - sqlite3_free(p->zTableName); - p->zTableName = zNewName; - }else{ - sqlite3_free(zNewName); - } - return rc; -} - - -/* -** A virtual table module that provides fuzzy search. -*/ -static sqlite3_module spellfix1Module = { - 0, /* iVersion */ - spellfix1Create, /* xCreate - handle CREATE VIRTUAL TABLE */ - spellfix1Connect, /* xConnect - reconnected to an existing table */ - spellfix1BestIndex, /* xBestIndex - figure out how to do a query */ - spellfix1Disconnect, /* xDisconnect - close a connection */ - spellfix1Destroy, /* xDestroy - handle DROP TABLE */ - spellfix1Open, /* xOpen - open a cursor */ - spellfix1Close, /* xClose - close a cursor */ - spellfix1Filter, /* xFilter - configure scan constraints */ - spellfix1Next, /* xNext - advance a cursor */ - spellfix1Eof, /* xEof - check for end of scan */ - spellfix1Column, /* xColumn - read data */ - spellfix1Rowid, /* xRowid - read data */ - spellfix1Update, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - spellfix1Rename, /* xRename */ -}; - -/* -** Register the various functions and the virtual table. -*/ -static int spellfix1Register(sqlite3 *db){ - int rc = SQLITE_OK; - int i; - rc = sqlite3_create_function(db, "spellfix1_translit", 1, SQLITE_UTF8, 0, - transliterateSqlFunc, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "spellfix1_editdist", 2, SQLITE_UTF8, 0, - editdistSqlFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "spellfix1_phonehash", 1, SQLITE_UTF8, 0, - phoneticHashSqlFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "spellfix1_scriptcode", 1, SQLITE_UTF8, 0, - scriptCodeSqlFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module(db, "spellfix1", &spellfix1Module, 0); - } - if( rc==SQLITE_OK ){ - rc = editDist3Install(db); - } - - /* Verify sanity of the translit[] table */ - for(i=0; i #include #include -#include "sqliteInt.h" #if SQLITE_OS_UNIX -/* From test1.c */ -extern const char *sqlite3TestErrorName(int); +/* From main.c */ +extern const char *sqlite3ErrName(int); +#include #include #include @@ -106,7 +107,8 @@ static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off); static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off); static int ts_fchmod(int fd, mode_t mode); static int ts_fallocate(int fd, off_t off, off_t len); - +static void *ts_mmap(void *, size_t, int, int, int, off_t); +static void *ts_mremap(void*, size_t, size_t, int, ...); struct TestSyscallArray { const char *zName; @@ -131,6 +133,8 @@ struct TestSyscallArray { /* 13 */ { "pwrite64", (sqlite3_syscall_ptr)ts_pwrite64, 0, 0, 0 }, /* 14 */ { "fchmod", (sqlite3_syscall_ptr)ts_fchmod, 0, 0, 0 }, /* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 }, + /* 16 */ { "mmap", (sqlite3_syscall_ptr)ts_mmap, 0, 0, 0 }, + /* 17 */ { "mremap", (sqlite3_syscall_ptr)ts_mremap, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; @@ -152,6 +156,8 @@ struct TestSyscallArray { aSyscall[13].xOrig) #define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig) #define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig) +#define orig_mmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[16].xOrig) +#define orig_mremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[17].xOrig) /* ** This function is called exactly once from within each invocation of a @@ -377,6 +383,31 @@ static int ts_fallocate(int fd, off_t off, off_t len){ return orig_fallocate(fd, off, len); } +static void *ts_mmap( + void *pAddr, + size_t nByte, + int prot, + int flags, + int fd, + off_t iOff +){ + if( tsIsFailErrno("mmap") ){ + return MAP_FAILED; + } + return orig_mmap(pAddr, nByte, prot, flags, fd, iOff); +} + +static void *ts_mremap(void *a, size_t b, size_t c, int d, ...){ + va_list ap; + void *pArg; + if( tsIsFailErrno("mremap") ){ + return MAP_FAILED; + } + va_start(ap, d); + pArg = va_arg(ap, void *); + return orig_mremap(a, b, c, d, pArg); +} + static int test_syscall_install( void * clientData, Tcl_Interp *interp, @@ -467,7 +498,7 @@ static int test_syscall_reset( } } if( rc!=SQLITE_OK ){ - Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; } diff --git a/src/test_thread.c b/src/test_thread.c index ae62de8..2f9363b 100644 --- a/src/test_thread.c +++ b/src/test_thread.c @@ -60,12 +60,14 @@ static Tcl_ObjCmdProc blocking_prepare_v2_proc; int Sqlitetest1_Init(Tcl_Interp *); int Sqlite3_Init(Tcl_Interp *); +/* Functions from main.c */ +extern const char *sqlite3ErrName(int); + /* Functions from test1.c */ -void *sqlite3TestTextToPtr(const char *); -const char *sqlite3TestErrorName(int); -int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); -int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *); -int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int); +extern void *sqlite3TestTextToPtr(const char *); +extern int getDbPointer(Tcl_Interp *, const char *, sqlite3 **); +extern int sqlite3TestMakePointerStr(Tcl_Interp *, char *, void *); +extern int sqlite3TestErrCode(Tcl_Interp *, sqlite3 *, int); /* ** Handler for events of type EvalEvent. @@ -559,7 +561,7 @@ static int blocking_step_proc( pStmt = (sqlite3_stmt*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); rc = sqlite3_blocking_step(pStmt); - Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), 0); + Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), 0); return TCL_OK; } @@ -606,7 +608,7 @@ static int blocking_prepare_v2_proc( } if( rc!=SQLITE_OK ){ assert( pStmt==0 ); - sprintf(zBuf, "%s ", (char *)sqlite3TestErrorName(rc)); + sprintf(zBuf, "%s ", (char *)sqlite3ErrName(rc)); Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); return TCL_ERROR; } diff --git a/src/test_vfs.c b/src/test_vfs.c index 93c556b..fcd5774 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -125,8 +125,9 @@ struct Testvfs { #define TESTVFS_ACCESS_MASK 0x00004000 #define TESTVFS_FULLPATHNAME_MASK 0x00008000 #define TESTVFS_READ_MASK 0x00010000 +#define TESTVFS_UNLOCK_MASK 0x00020000 -#define TESTVFS_ALL_MASK 0x0001FFFF +#define TESTVFS_ALL_MASK 0x0003FFFF #define TESTVFS_MAX_PAGES 1024 @@ -265,7 +266,8 @@ static void tvfsExecTcl( const char *zMethod, Tcl_Obj *arg1, Tcl_Obj *arg2, - Tcl_Obj *arg3 + Tcl_Obj *arg3, + Tcl_Obj *arg4 ){ int rc; /* Return code from Tcl_EvalObj() */ Tcl_Obj *pEval; @@ -282,6 +284,7 @@ static void tvfsExecTcl( if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1); if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2); if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3); + if( arg4 ) Tcl_ListObjAppendElement(p->interp, pEval, arg4); rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ @@ -302,7 +305,7 @@ static int tvfsClose(sqlite3_file *pFile){ if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){ tvfsExecTcl(p, "xClose", - Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 + Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); } @@ -333,7 +336,7 @@ static int tvfsRead( Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_READ_MASK ){ tvfsExecTcl(p, "xRead", - Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 + Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } @@ -362,7 +365,7 @@ static int tvfsWrite( if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){ tvfsExecTcl(p, "xWrite", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, - Tcl_NewWideIntObj(iOfst) + Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt) ); tvfsResultCode(p, &rc); } @@ -390,7 +393,7 @@ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){ tvfsExecTcl(p, "xTruncate", - Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 + Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } @@ -431,7 +434,7 @@ static int tvfsSync(sqlite3_file *pFile, int flags){ tvfsExecTcl(p, "xSync", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, - Tcl_NewStringObj(zFlags, -1) + Tcl_NewStringObj(zFlags, -1), 0 ); tvfsResultCode(p, &rc); } @@ -465,8 +468,12 @@ static int tvfsLock(sqlite3_file *pFile, int eLock){ ** Unlock an tvfs-file. */ static int tvfsUnlock(sqlite3_file *pFile, int eLock){ - TestvfsFd *p = tvfsGetFd(pFile); - return sqlite3OsUnlock(p->pReal, eLock); + TestvfsFd *pFd = tvfsGetFd(pFile); + Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; + if( p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){ + return SQLITE_IOERR_UNLOCK; + } + return sqlite3OsUnlock(pFd->pReal, eLock); } /* @@ -578,7 +585,7 @@ static int tvfsOpen( z += strlen(z) + 1; } } - tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0); + tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0); Tcl_DecrRefCount(pArg); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; @@ -635,7 +642,7 @@ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ tvfsExecTcl(p, "xDelete", - Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0 + Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0 ); tvfsResultCode(p, &rc); } @@ -663,7 +670,7 @@ static int tvfsAccess( if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE"; if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ"; tvfsExecTcl(p, "xAccess", - Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0 + Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0, 0 ); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; @@ -691,7 +698,7 @@ static int tvfsFullPathname( Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){ int rc; - tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0); + tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; } @@ -771,7 +778,7 @@ static int tvfsShmOpen(sqlite3_file *pFile){ */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){ - tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); + tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; } @@ -841,7 +848,7 @@ static int tvfsShmMap( Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); tvfsExecTcl(p, "xShmMap", - Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg, 0 ); tvfsResultCode(p, &rc); Tcl_DecrRefCount(pArg); @@ -891,7 +898,7 @@ static int tvfsShmLock( } tvfsExecTcl(p, "xShmLock", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, - Tcl_NewStringObj(zLock, -1) + Tcl_NewStringObj(zLock, -1), 0 ); tvfsResultCode(p, &rc); } @@ -937,7 +944,7 @@ static void tvfsShmBarrier(sqlite3_file *pFile){ if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){ tvfsExecTcl(p, "xShmBarrier", - Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0 ); } } @@ -961,7 +968,7 @@ static int tvfsShmUnmap( if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){ tvfsExecTcl(p, "xShmUnmap", - Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0 ); tvfsResultCode(p, &rc); } @@ -1099,6 +1106,7 @@ static int testvfs_obj_cmd( { "xClose", TESTVFS_CLOSE_MASK }, { "xAccess", TESTVFS_ACCESS_MASK }, { "xFullPathname", TESTVFS_FULLPATHNAME_MASK }, + { "xUnlock", TESTVFS_UNLOCK_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; diff --git a/src/test_wholenumber.c b/src/test_wholenumber.c deleted file mode 100644 index 7c42d01..0000000 --- a/src/test_wholenumber.c +++ /dev/null @@ -1,311 +0,0 @@ -/* -** 2011 April 02 -** -** 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 a virtual table that returns the whole numbers -** between 1 and 4294967295, inclusive. -** -** Example: -** -** CREATE VIRTUAL TABLE nums USING wholenumber; -** SELECT value FROM nums WHERE value<10; -** -** Results in: -** -** 1 2 3 4 5 6 7 8 9 -*/ -#include "sqlite3.h" -#include -#include - -#ifndef SQLITE_OMIT_VIRTUALTABLE - - -/* A wholenumber cursor object */ -typedef struct wholenumber_cursor wholenumber_cursor; -struct wholenumber_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - sqlite3_int64 iValue; /* Current value */ - sqlite3_int64 mxValue; /* Maximum value */ -}; - -/* Methods for the wholenumber module */ -static int wholenumberConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - sqlite3_vtab *pNew; - pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); - if( pNew==0 ) return SQLITE_NOMEM; - sqlite3_declare_vtab(db, "CREATE TABLE x(value)"); - memset(pNew, 0, sizeof(*pNew)); - return SQLITE_OK; -} -/* Note that for this virtual table, the xCreate and xConnect -** methods are identical. */ - -static int wholenumberDisconnect(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); - return SQLITE_OK; -} -/* The xDisconnect and xDestroy methods are also the same */ - - -/* -** Open a new wholenumber cursor. -*/ -static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ - wholenumber_cursor *pCur; - pCur = sqlite3_malloc( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - *ppCursor = &pCur->base; - return SQLITE_OK; -} - -/* -** Close a wholenumber cursor. -*/ -static int wholenumberClose(sqlite3_vtab_cursor *cur){ - sqlite3_free(cur); - return SQLITE_OK; -} - - -/* -** Advance a cursor to its next row of output -*/ -static int wholenumberNext(sqlite3_vtab_cursor *cur){ - wholenumber_cursor *pCur = (wholenumber_cursor*)cur; - pCur->iValue++; - return SQLITE_OK; -} - -/* -** Return the value associated with a wholenumber. -*/ -static int wholenumberColumn( - sqlite3_vtab_cursor *cur, - sqlite3_context *ctx, - int i -){ - wholenumber_cursor *pCur = (wholenumber_cursor*)cur; - sqlite3_result_int64(ctx, pCur->iValue); - return SQLITE_OK; -} - -/* -** The rowid. -*/ -static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - wholenumber_cursor *pCur = (wholenumber_cursor*)cur; - *pRowid = pCur->iValue; - return SQLITE_OK; -} - -/* -** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal -** that the cursor has nothing more to output. -*/ -static int wholenumberEof(sqlite3_vtab_cursor *cur){ - wholenumber_cursor *pCur = (wholenumber_cursor*)cur; - return pCur->iValue>pCur->mxValue || pCur->iValue==0; -} - -/* -** Called to "rewind" a cursor back to the beginning so that -** it starts its output over again. Always called at least once -** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call. -** -** idxNum Constraints -** ------ --------------------- -** 0 (none) -** 1 value > $argv0 -** 2 value >= $argv0 -** 4 value < $argv0 -** 8 value <= $argv0 -** -** 5 value > $argv0 AND value < $argv1 -** 6 value >= $argv0 AND value < $argv1 -** 9 value > $argv0 AND value <= $argv1 -** 10 value >= $argv0 AND value <= $argv1 -*/ -static int wholenumberFilter( - sqlite3_vtab_cursor *pVtabCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor; - sqlite3_int64 v; - int i = 0; - pCur->iValue = 1; - pCur->mxValue = 0xffffffff; /* 4294967295 */ - if( idxNum & 3 ){ - v = sqlite3_value_int64(argv[0]) + (idxNum&1); - if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v; - i++; - } - if( idxNum & 12 ){ - v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1); - if( v>=pCur->iValue && vmxValue ) pCur->mxValue = v; - } - return SQLITE_OK; -} - -/* -** Search for terms of these forms: -** -** (1) value > $value -** (2) value >= $value -** (4) value < $value -** (8) value <= $value -** -** idxNum is an ORed combination of 1 or 2 with 4 or 8. -*/ -static int wholenumberBestIndex( - sqlite3_vtab *tab, - sqlite3_index_info *pIdxInfo -){ - int i; - int idxNum = 0; - int argvIdx = 1; - int ltIdx = -1; - int gtIdx = -1; - const struct sqlite3_index_constraint *pConstraint; - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){ - idxNum |= 1; - ltIdx = i; - } - if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){ - idxNum |= 2; - ltIdx = i; - } - if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ - idxNum |= 4; - gtIdx = i; - } - if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){ - idxNum |= 8; - gtIdx = i; - } - } - pIdxInfo->idxNum = idxNum; - if( ltIdx>=0 ){ - pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++; - pIdxInfo->aConstraintUsage[ltIdx].omit = 1; - } - if( gtIdx>=0 ){ - pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx; - pIdxInfo->aConstraintUsage[gtIdx].omit = 1; - } - if( pIdxInfo->nOrderBy==1 - && pIdxInfo->aOrderBy[0].desc==0 - ){ - pIdxInfo->orderByConsumed = 1; - } - pIdxInfo->estimatedCost = (double)1; - return SQLITE_OK; -} - -/* -** A virtual table module that provides read-only access to a -** Tcl global variable namespace. -*/ -static sqlite3_module wholenumberModule = { - 0, /* iVersion */ - wholenumberConnect, - wholenumberConnect, - wholenumberBestIndex, - wholenumberDisconnect, - wholenumberDisconnect, - wholenumberOpen, /* xOpen - open a cursor */ - wholenumberClose, /* xClose - close a cursor */ - wholenumberFilter, /* xFilter - configure scan constraints */ - wholenumberNext, /* xNext - advance a cursor */ - wholenumberEof, /* xEof - check for end of scan */ - wholenumberColumn, /* xColumn - read data */ - wholenumberRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ -}; - -#endif /* SQLITE_OMIT_VIRTUALTABLE */ - - -/* -** Register the wholenumber virtual table -*/ -int wholenumber_register(sqlite3 *db){ - int rc = SQLITE_OK; -#ifndef SQLITE_OMIT_VIRTUALTABLE - rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0); -#endif - return rc; -} - -#ifdef SQLITE_TEST -#include -/* -** Decode a pointer to an sqlite3 object. -*/ -extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); - -/* -** Register the echo virtual table module. -*/ -static int register_wholenumber_module( - ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ - Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ - int objc, /* Number of arguments */ - Tcl_Obj *CONST objv[] /* Command arguments */ -){ - sqlite3 *db; - if( objc!=2 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DB"); - return TCL_ERROR; - } - if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; - wholenumber_register(db); - return TCL_OK; -} - - -/* -** Register commands with the TCL interpreter. -*/ -int Sqlitetestwholenumber_Init(Tcl_Interp *interp){ - static struct { - char *zName; - Tcl_ObjCmdProc *xProc; - void *clientData; - } aObjCmd[] = { - { "register_wholenumber_module", register_wholenumber_module, 0 }, - }; - int i; - for(i=0; i=pTab->nCol ){ if( sqlite3IsRowid(pChanges->a[i].zName) ){ + j = -1; chngRowid = 1; pRowidExpr = pChanges->a[i].pExpr; }else{ @@ -220,7 +221,8 @@ void sqlite3Update( { int rc; rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName, - pTab->aCol[j].zName, db->aDb[iDb].zName); + j<0 ? "ROWID" : pTab->aCol[j].zName, + db->aDb[iDb].zName); if( rc==SQLITE_DENY ){ goto update_cleanup; }else if( rc==SQLITE_IGNORE ){ @@ -458,7 +460,7 @@ void sqlite3Update( /* The row-trigger may have deleted the row being updated. In this ** case, jump to the next row. No updates or AFTER triggers are - ** required. This behaviour - what happens when the row being updated + ** required. This behavior - what happens when the row being updated ** is deleted or renamed by a BEFORE trigger - is left undefined in the ** documentation. */ diff --git a/src/util.c b/src/util.c index 5cf8eba..d83a630 100644 --- a/src/util.c +++ b/src/util.c @@ -261,7 +261,7 @@ int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ */ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ #ifndef SQLITE_OMIT_FLOATING_POINT - int incr = (enc==SQLITE_UTF8?1:2); + int incr; const char *zEnd = z + length; /* sign * significand * (10 ^ (esign * exponent)) */ int sign = 1; /* sign of significand */ @@ -272,10 +272,22 @@ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ int eValid = 1; /* True exponent is either not used or is well-formed */ double result; int nDigits = 0; + int nonNum = 0; + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); *pResult = 0.0; /* Default return value, in case of an error */ - if( enc==SQLITE_UTF16BE ) z++; + if( enc==SQLITE_UTF8 ){ + incr = 1; + }else{ + int i; + incr = 2; + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); + for(i=3-enc; i=zEnd && nDigits>0 && eValid; + return z>=zEnd && nDigits>0 && eValid && nonNum==0; #else return !sqlite3Atoi64(z, pResult, length, enc); #endif /* SQLITE_OMIT_FLOATING_POINT */ @@ -457,21 +469,33 @@ static int compare2pow63(const char *zNum, int incr){ ** signed 64-bit integer, its negative -9223372036854665808 can be. ** ** If zNum is too big for a 64-bit integer and is not -** 9223372036854665808 then return 1. +** 9223372036854665808 or if zNum contains any non-numeric text, +** then return 1. ** ** length is the number of bytes in the string (bytes, not characters). ** The string is not necessarily zero-terminated. The encoding is ** given by enc. */ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ - int incr = (enc==SQLITE_UTF8?1:2); + int incr; u64 u = 0; int neg = 0; /* assume positive */ int i; int c = 0; + int nonNum = 0; const char *zStart; const char *zEnd = zNum + length; - if( enc==SQLITE_UTF16BE ) zNum++; + assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); + if( enc==SQLITE_UTF8 ){ + incr = 1; + }else{ + incr = 2; + assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); + for(i=3-enc; i19*incr ){ + if( (c!=0 && &zNum[i]19*incr || nonNum ){ /* zNum is empty or contains non-numeric text or is longer ** than 19 digits (thus guaranteeing that it is too large) */ return 1; diff --git a/src/vacuum.c b/src/vacuum.c index 7ed2478..4afb2cc 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -289,6 +289,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */ BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */ BTREE_USER_VERSION, 0, /* Preserve the user version */ + BTREE_APPLICATION_ID, 0, /* Preserve the application id */ }; assert( 1==sqlite3BtreeIsInTrans(pTemp) ); diff --git a/src/vdbe.c b/src/vdbe.c index a2ab31e..f343e13 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -152,11 +152,7 @@ int sqlite3_found_count = 0; && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} /* Return true if the cursor was opened using the OP_OpenSorter opcode. */ -#ifdef SQLITE_OMIT_MERGE_SORT -# define isSorter(x) 0 -#else # define isSorter(x) ((x)->pSorter!=0) -#endif /* ** Argument pMem points at a register that will be passed to a @@ -869,7 +865,7 @@ case OP_Halt: { if( rc==SQLITE_BUSY ){ p->rc = rc = SQLITE_BUSY; }else{ - assert( rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ); + assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ); assert( rc==SQLITE_OK || db->nDeferredCons>0 ); rc = p->rc ? SQLITE_ERROR : SQLITE_DONE; } @@ -3321,17 +3317,12 @@ case OP_OpenEphemeral: { case OP_SorterOpen: { VdbeCursor *pCx; -#ifndef SQLITE_OMIT_MERGE_SORT pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->pKeyInfo = pOp->p4.pKeyInfo; pCx->pKeyInfo->enc = ENC(p->db); pCx->isSorter = 1; rc = sqlite3VdbeSorterInit(db, pCx); -#else - pOp->opcode = OP_OpenEphemeral; - pc--; -#endif break; } @@ -3524,7 +3515,7 @@ case OP_SeekGt: { /* jump, in3 */ ** r.flags = 0; ** } */ - r.flags = (u16)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); + r.flags = (u8)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekGe || r.flags==0 ); @@ -4214,15 +4205,10 @@ case OP_SorterCompare: { case OP_SorterData: { VdbeCursor *pC; -#ifndef SQLITE_OMIT_MERGE_SORT pOut = &aMem[pOp->p2]; pC = p->apCsr[pOp->p1]; assert( pC->isSorter ); rc = sqlite3VdbeSorterRowkey(pC, pOut); -#else - pOp->opcode = OP_RowKey; - pc--; -#endif break; } @@ -4421,9 +4407,6 @@ case OP_Last: { /* jump */ ** correctly optimizing out sorts. */ case OP_SorterSort: /* jump */ -#ifdef SQLITE_OMIT_MERGE_SORT - pOp->opcode = OP_Sort; -#endif case OP_Sort: { /* jump */ #ifdef SQLITE_TEST sqlite3_sort_count++; @@ -4502,9 +4485,6 @@ case OP_Rewind: { /* jump */ ** number P5-1 in the prepared statement is incremented. */ case OP_SorterNext: /* jump */ -#ifdef SQLITE_OMIT_MERGE_SORT - pOp->opcode = OP_Next; -#endif case OP_Prev: /* jump */ case OP_Next: { /* jump */ VdbeCursor *pC; @@ -4555,9 +4535,6 @@ case OP_Next: { /* jump */ ** for tables is OP_Insert. */ case OP_SorterInsert: /* in2 */ -#ifdef SQLITE_OMIT_MERGE_SORT - pOp->opcode = OP_IdxInsert; -#endif case OP_IdxInsert: { /* in2 */ VdbeCursor *pC; BtCursor *pCrsr; @@ -5784,7 +5761,7 @@ case OP_VOpen: { /* Initialize sqlite3_vtab_cursor base class */ pVtabCursor->pVtab = pVtab; - /* Initialise vdbe cursor object */ + /* Initialize vdbe cursor object */ pCur = allocateCursor(p, pOp->p1, 0, -1, 0); if( pCur ){ pCur->pVtabCursor = pVtabCursor; @@ -6063,7 +6040,7 @@ case OP_VUpdate: { assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); db->lastRowid = lastRowid = rowid; } - if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ + if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ if( pOp->p5==OE_Ignore ){ rc = SQLITE_OK; }else{ diff --git a/src/vdbeInt.h b/src/vdbeInt.h index fac0b2f..3a5b402 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -18,6 +18,14 @@ #ifndef _VDBEINT_H_ #define _VDBEINT_H_ +/* +** The maximum number of times that a statement will try to reparse +** itself before giving up and returning SQLITE_SCHEMA. +*/ +#ifndef SQLITE_MAX_SCHEMA_RETRY +# define SQLITE_MAX_SCHEMA_RETRY 50 +#endif + /* ** SQL is translated into a sequence of instructions to be ** executed by a virtual machine. Each instruction is an instance @@ -123,7 +131,7 @@ struct VdbeFrame { VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ void *token; /* Copy of SubProgram.token */ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ - u16 nCursor; /* Number of entries in apCsr */ + int nCursor; /* Number of entries in apCsr */ int pc; /* Program Counter in parent (calling) frame */ int nOp; /* Size of aOp array */ int nMem; /* Number of entries in aMem */ @@ -309,7 +317,7 @@ struct Vdbe { int nLabel; /* Number of labels used */ int *aLabel; /* Space to hold the labels */ u16 nResColumn; /* Number of columns in one row of the result set */ - u16 nCursor; /* Number of slots in apCsr[] */ + int nCursor; /* Number of slots in apCsr[] */ u32 magic; /* Magic number for sanity checking */ char *zErrMsg; /* Error message written here */ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ @@ -429,15 +437,6 @@ int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); int sqlite3VdbeTransferError(Vdbe *p); -#ifdef SQLITE_OMIT_MERGE_SORT -# define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK -# define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK -# define sqlite3VdbeSorterClose(Y,Z) -# define sqlite3VdbeSorterRowkey(Y,Z) SQLITE_OK -# define sqlite3VdbeSorterRewind(X,Y,Z) SQLITE_OK -# define sqlite3VdbeSorterNext(X,Y,Z) SQLITE_OK -# define sqlite3VdbeSorterCompare(X,Y,Z) SQLITE_OK -#else int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *); void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *); int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *); @@ -445,7 +444,6 @@ int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *); int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *); int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *); int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *); -#endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 void sqlite3VdbeEnter(Vdbe*); diff --git a/src/vdbeapi.c b/src/vdbeapi.c index b488266..7c861e2 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -445,7 +445,7 @@ end_of_step: assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE ); if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ /* If this statement was prepared using sqlite3_prepare_v2(), and an - ** error has occured, then return the error code in p->rc to the + ** error has occurred, then return the error code in p->rc to the ** caller. Set the error code in the database handle to the same value. */ rc = sqlite3VdbeTransferError(p); @@ -453,14 +453,6 @@ end_of_step: return (rc&db->errMask); } -/* -** The maximum number of times that a statement will try to reparse -** itself before giving up and returning SQLITE_SCHEMA. -*/ -#ifndef SQLITE_MAX_SCHEMA_RETRY -# define SQLITE_MAX_SCHEMA_RETRY 5 -#endif - /* ** This is the top-level implementation of sqlite3_step(). Call ** sqlite3Step() to do most of the work. If a schema error occurs, @@ -1198,7 +1190,7 @@ int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){ if( zName ){ for(i=0; inzVar; i++){ const char *z = p->azVar[i]; - if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){ + if( z && strncmp(z,zName,nName)==0 && z[nName]==0 ){ return i+1; } } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 3bf8e47..2c4269a 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -17,18 +17,6 @@ #include "sqliteInt.h" #include "vdbeInt.h" - - -/* -** When debugging the code generator in a symbolic debugger, one can -** set the sqlite3VdbeAddopTrace to 1 and all opcodes will be printed -** as they are added to the instruction stream. -*/ -#ifdef SQLITE_DEBUG -int sqlite3VdbeAddopTrace = 0; -#endif - - /* ** Create a new virtual database engine. */ @@ -158,7 +146,9 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->p4type = P4_NOTUSED; #ifdef SQLITE_DEBUG pOp->zComment = 0; - if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + sqlite3VdbePrintOp(0, i, &p->aOp[i]); + } #endif #ifdef VDBE_PROFILE pOp->cycles = 0; @@ -377,7 +367,7 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) #endif || ((opcode==OP_Halt || opcode==OP_HaltIfNull) - && (pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) + && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) ){ hasAbort = 1; break; @@ -385,7 +375,7 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ } sqlite3DbFree(v->db, sIter.apSub); - /* Return true if hasAbort==mayAbort. Or if a malloc failure occured. + /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. ** If malloc failed, then the while() loop above may not have iterated ** through all opcodes and hasAbort may be set incorrectly. Return ** true for this case to prevent the assert() in the callers frame @@ -512,7 +502,7 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){ pOut->p5 = 0; #ifdef SQLITE_DEBUG pOut->zComment = 0; - if( sqlite3VdbeAddopTrace ){ + if( p->db->flags & SQLITE_VdbeAddopTrace ){ sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]); } #endif @@ -1538,7 +1528,7 @@ void sqlite3VdbeMakeReady( zEnd = &zCsr[nByte]; }while( nByte && !db->mallocFailed ); - p->nCursor = (u16)nCursor; + p->nCursor = nCursor; p->nOnceFlag = nOnce; if( p->aVar ){ p->nVar = (ynVar)nVar; @@ -1780,7 +1770,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ if( needXcommit && db->xCommitCallback ){ rc = db->xCommitCallback(db->pCommitArg); if( rc ){ - return SQLITE_CONSTRAINT; + return SQLITE_CONSTRAINT_COMMITHOOK; } } @@ -2017,7 +2007,7 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ /* If p->iStatement is greater than zero, then this Vdbe opened a ** statement transaction that should be closed here. The only exception - ** is that an IO error may have occured, causing an emergency rollback. + ** is that an IO error may have occurred, causing an emergency rollback. ** In this case (db->nStatement==0), and there is nothing to do. */ if( db->nStatement && p->iStatement ){ @@ -2072,14 +2062,14 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ ** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. ** ** If there are outstanding FK violations and this function returns -** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT and write -** an error message to it. Then return SQLITE_ERROR. +** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY +** and write an error message to it. Then return SQLITE_ERROR. */ #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ sqlite3 *db = p->db; if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){ - p->rc = SQLITE_CONSTRAINT; + p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; p->errorAction = OE_Abort; sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed"); return SQLITE_ERROR; @@ -2153,7 +2143,7 @@ int sqlite3VdbeHalt(Vdbe *p){ ** ** Even if the statement is read-only, it is important to perform ** a statement or transaction rollback operation. If the error - ** occured while writing to the journal, sub-journal or database + ** occurred while writing to the journal, sub-journal or database ** file as part of an effort to free up cache space (see function ** pagerStress() in pager.c), the rollback is required to restore ** the pager to a consistent state. @@ -2194,7 +2184,7 @@ int sqlite3VdbeHalt(Vdbe *p){ sqlite3VdbeLeave(p); return SQLITE_ERROR; } - rc = SQLITE_CONSTRAINT; + rc = SQLITE_CONSTRAINT_FOREIGNKEY; }else{ /* The auto-commit flag is true, the vdbe program was successful ** or hit an 'OR FAIL' constraint and there are no deferred foreign @@ -2237,7 +2227,7 @@ int sqlite3VdbeHalt(Vdbe *p){ if( eStatementOp ){ rc = sqlite3VdbeCloseStatement(p, eStatementOp); if( rc ){ - if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){ + if( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ){ p->rc = rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; @@ -2478,7 +2468,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ sqlite3DbFree(db, p->zSql); sqlite3DbFree(db, p->pFree); #if defined(SQLITE_ENABLE_TREE_EXPLAIN) - sqlite3_free(p->zExplain); + sqlite3DbFree(db, p->zExplain); sqlite3DbFree(db, p->pExplain); #endif } @@ -2567,7 +2557,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){ ** the blob of data that it corresponds to. In a table record, all serial ** types are stored at the start of the record, and the blobs of data at ** the end. Hence these functions allow the caller to handle the -** serial-type and data blob seperately. +** serial-type and data blob separately. ** ** The following table describes the various storage classes for data: ** diff --git a/src/vdbeblob.c b/src/vdbeblob.c index ae77a47..2e8fd8e 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -313,7 +313,7 @@ int sqlite3_blob_open( } sqlite3_bind_int64(pBlob->pStmt, 1, iRow); rc = blobSeekToRow(pBlob, iRow, &zErr); - } while( (++nAttempt)<5 && rc==SQLITE_SCHEMA ); + } while( (++nAttempt)mallocFailed==0 ){ diff --git a/src/vdbemem.c b/src/vdbemem.c index fd964de..8fc222e 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -32,7 +32,9 @@ ** between formats. */ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ +#ifndef SQLITE_OMIT_UTF16 int rc; +#endif assert( (pMem->flags&MEM_RowSet)==0 ); assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE || desiredEnc==SQLITE_UTF16BE ); diff --git a/src/vdbesort.c b/src/vdbesort.c index d51bbf5..fdfc4a7 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -18,7 +18,6 @@ #include "sqliteInt.h" #include "vdbeInt.h" -#ifndef SQLITE_OMIT_MERGE_SORT typedef struct VdbeSorterIter VdbeSorterIter; typedef struct SorterRecord SorterRecord; @@ -1037,5 +1036,3 @@ int sqlite3VdbeSorterCompare( vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes); return SQLITE_OK; } - -#endif /* #ifndef SQLITE_OMIT_MERGE_SORT */ diff --git a/src/vdbetrace.c b/src/vdbetrace.c index 35825c8..356277e 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -53,6 +53,11 @@ static int findNextHostParameter(const char *zSql, int *pnToken){ ** then the returned string holds a copy of zRawSql with "-- " prepended ** to each line of text. ** +** If the SQLITE_TRACE_SIZE_LIMIT macro is defined to an integer, then +** then long strings and blobs are truncated to that many bytes. This +** can be used to prevent unreasonably large trace strings when dealing +** with large (multi-megabyte) strings and blobs. +** ** The calling function is responsible for making sure the memory returned ** is eventually freed. ** @@ -123,30 +128,49 @@ char *sqlite3VdbeExpandSql( }else if( pVar->flags & MEM_Real ){ sqlite3XPrintf(&out, "%!.15g", pVar->r); }else if( pVar->flags & MEM_Str ){ + int nOut; /* Number of bytes of the string text to include in output */ #ifndef SQLITE_OMIT_UTF16 u8 enc = ENC(db); + Mem utf8; if( enc!=SQLITE_UTF8 ){ - Mem utf8; memset(&utf8, 0, sizeof(utf8)); utf8.db = db; sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8); - sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z); - sqlite3VdbeMemRelease(&utf8); - }else -#endif - { - sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z); + pVar = &utf8; } +#endif + nOut = pVar->n; +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOut>SQLITE_TRACE_SIZE_LIMIT ){ + nOut = SQLITE_TRACE_SIZE_LIMIT; + while( nOutn && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; } + } +#endif + sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z); +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOutn ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut); +#endif +#ifndef SQLITE_OMIT_UTF16 + if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8); +#endif }else if( pVar->flags & MEM_Zero ){ sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); }else{ + int nOut; /* Number of bytes of the blob to include in output */ assert( pVar->flags & MEM_Blob ); sqlite3StrAccumAppend(&out, "x'", 2); - for(i=0; in; i++){ + nOut = pVar->n; +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT; +#endif + for(i=0; iz[i]&0xff); } sqlite3StrAccumAppend(&out, "'", 1); +#ifdef SQLITE_TRACE_SIZE_LIMIT + if( nOutn ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut); +#endif } } } diff --git a/src/wal.c b/src/wal.c index 0d7271b..e642ea2 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1207,8 +1207,9 @@ finished: ** checkpointing the log file. */ if( pWal->hdr.nPage ){ - sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s", - pWal->hdr.nPage, pWal->zWalName + sqlite3_log(SQLITE_NOTICE_RECOVER_WAL, + "recovered %d frames from WAL file %s", + pWal->hdr.mxFrame, pWal->zWalName ); } } @@ -1722,8 +1723,8 @@ static int walCheckpoint( rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } - /* If the database file may grow as a result of this checkpoint, hint - ** about the eventual size of the db file to the VFS layer. + /* If the database may grow as a result of this checkpoint, hint + ** about the eventual size of the db file to the VFS layer. */ if( rc==SQLITE_OK ){ i64 nReq = ((i64)mxPage * szPage); @@ -1733,6 +1734,7 @@ static int walCheckpoint( } } + /* Iterate through the contents of the WAL, copying data to the db file. */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; @@ -2287,19 +2289,17 @@ void sqlite3WalEndReadTransaction(Wal *pWal){ } /* -** Read a page from the WAL, if it is present in the WAL and if the -** current read transaction is configured to use the WAL. +** Search the wal file for page pgno. If found, set *piRead to the frame that +** contains the page. Otherwise, if pgno is not in the wal file, set *piRead +** to zero. ** -** The *pInWal is set to 1 if the requested page is in the WAL and -** has been loaded. Or *pInWal is set to 0 if the page was not in -** the WAL and needs to be read out of the database. +** Return SQLITE_OK if successful, or an error code if an error occurs. If an +** error does occur, the final value of *piRead is undefined. */ -int sqlite3WalRead( +int sqlite3WalFindFrame( Wal *pWal, /* WAL handle */ Pgno pgno, /* Database page number to read data for */ - int *pInWal, /* OUT: True if data is read from WAL */ - int nOut, /* Size of buffer pOut in bytes */ - u8 *pOut /* Buffer to write page data to */ + u32 *piRead /* OUT: Frame number (or zero) */ ){ u32 iRead = 0; /* If !=0, WAL frame to return data from */ u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */ @@ -2315,7 +2315,7 @@ int sqlite3WalRead( ** WAL were empty. */ if( iLast==0 || pWal->readLock==0 ){ - *pInWal = 0; + *piRead = 0; return SQLITE_OK; } @@ -2386,26 +2386,31 @@ int sqlite3WalRead( } #endif - /* If iRead is non-zero, then it is the log frame number that contains the - ** required page. Read and return data from the log file. - */ - if( iRead ){ - int sz; - i64 iOffset; - sz = pWal->hdr.szPage; - sz = (sz&0xfe00) + ((sz&0x0001)<<16); - testcase( sz<=32768 ); - testcase( sz>=65536 ); - iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; - *pInWal = 1; - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ - return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); - } - - *pInWal = 0; + *piRead = iRead; return SQLITE_OK; } +/* +** Read the contents of frame iRead from the wal file into buffer pOut +** (which is nOut bytes in size). Return SQLITE_OK if successful, or an +** error code otherwise. +*/ +int sqlite3WalReadFrame( + Wal *pWal, /* WAL handle */ + u32 iRead, /* Frame to read */ + int nOut, /* Size of buffer pOut in bytes */ + u8 *pOut /* Buffer to write page data to */ +){ + int sz; + i64 iOffset; + sz = pWal->hdr.szPage; + sz = (sz&0xfe00) + ((sz&0x0001)<<16); + testcase( sz<=32768 ); + testcase( sz>=65536 ); + iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE; + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ + return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); +} /* ** Return the size of the database in pages (or zero, if unknown). @@ -2952,6 +2957,9 @@ int sqlite3WalCheckpoint( /* Read the wal-index header. */ if( rc==SQLITE_OK ){ rc = walIndexReadHdr(pWal, &isChanged); + if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ + sqlite3OsUnfetch(pWal->pDbFd, 0, 0); + } } /* Copy data from the log to the database file. */ diff --git a/src/wal.h b/src/wal.h index a848de1..0925463 100644 --- a/src/wal.h +++ b/src/wal.h @@ -31,7 +31,6 @@ # define sqlite3WalClose(w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) -# define sqlite3WalRead(v,w,x,y,z) 0 # define sqlite3WalDbsize(y) 0 # define sqlite3WalBeginWriteTransaction(y) 0 # define sqlite3WalEndWriteTransaction(x) 0 @@ -44,6 +43,7 @@ # define sqlite3WalExclusiveMode(y,z) 0 # define sqlite3WalHeapMemory(z) 0 # define sqlite3WalFramesize(z) 0 +# define sqlite3WalFindFrame(x,y,z) 0 #else #define WAL_SAVEPOINT_NDATA 4 @@ -71,7 +71,8 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *); void sqlite3WalEndReadTransaction(Wal *pWal); /* Read a page from the write-ahead log, if it is present. */ -int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut); +int sqlite3WalFindFrame(Wal *, Pgno, u32 *); +int sqlite3WalReadFrame(Wal *, u32, int, u8 *); /* If the WAL is not empty, return the size of the database. */ Pgno sqlite3WalDbsize(Wal *pWal); diff --git a/src/walker.c b/src/walker.c index eab96ea..e71ed2a 100644 --- a/src/walker.c +++ b/src/walker.c @@ -113,7 +113,9 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ /* ** Call sqlite3WalkExpr() for every expression in Select statement p. ** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and -** on the compound select chain, p->pPrior. +** on the compound select chain, p->pPrior. Invoke the xSelectCallback() +** either before or after the walk of expressions and FROM clause, depending +** on whether pWalker->bSelectDepthFirst is false or true, respectively. ** ** Return WRC_Continue under normal conditions. Return WRC_Abort if ** there is an abort request. @@ -127,14 +129,23 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){ rc = WRC_Continue; pWalker->walkerDepth++; while( p ){ - rc = pWalker->xSelectCallback(pWalker, p); - if( rc ) break; + if( !pWalker->bSelectDepthFirst ){ + rc = pWalker->xSelectCallback(pWalker, p); + if( rc ) break; + } if( sqlite3WalkSelectExpr(pWalker, p) || sqlite3WalkSelectFrom(pWalker, p) ){ pWalker->walkerDepth--; return WRC_Abort; } + if( pWalker->bSelectDepthFirst ){ + rc = pWalker->xSelectCallback(pWalker, p); + /* Depth-first search is currently only used for + ** selectAddSubqueryTypeInfo() and that routine always returns + ** WRC_Continue (0). So the following branch is never taken. */ + if( NEVER(rc) ) break; + } p = p->pPrior; } pWalker->walkerDepth--; diff --git a/src/where.c b/src/where.c index fb3bc5c..e614f4a 100644 --- a/src/where.c +++ b/src/where.c @@ -98,8 +98,8 @@ struct WhereTerm { int leftCursor; /* Cursor number of X in "X " */ union { int leftColumn; /* Column number of X in "X " */ - WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ - WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ + WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ + WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ } u; u16 eOperator; /* A WO_xx value describing */ u8 wtFlags; /* TERM_xxx bit flags. See below */ @@ -140,7 +140,6 @@ struct WhereTerm { struct WhereClause { Parse *pParse; /* The parser context */ WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */ - Bitmask vmask; /* Bitmask identifying virtual table cursors */ WhereClause *pOuter; /* Outer conjunction */ u8 op; /* Split operator. TK_AND or TK_OR */ u16 wctrlFlags; /* Might include WHERE_AND_ONLY */ @@ -227,6 +226,7 @@ struct WhereCost { #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ +#define WO_EQUIV 0x400 /* Of the form A==B, both columns */ #define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ @@ -253,7 +253,7 @@ struct WhereCost { #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ -#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ +#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* xEXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and xnTerm = 0; pWC->nSlot = ArraySize(pWC->aStatic); pWC->a = pWC->aStatic; - pWC->vmask = 0; pWC->wctrlFlags = wctrlFlags; } @@ -563,7 +564,7 @@ static int allowedOp(int op){ ** Commute a comparison operator. Expressions of the form "X op Y" ** are converted into "Y op X". ** -** If left/right precendence rules come into play when determining the +** If left/right precedence rules come into play when determining the ** collating ** side of the comparison, it remains associated with the same side after ** the commutation. So "Y collate NOCASE op X" becomes @@ -629,6 +630,23 @@ static u16 operatorMask(int op){ ** where X is a reference to the iColumn of table iCur and is one of ** the WO_xx operator codes specified by the op parameter. ** Return a pointer to the term. Return 0 if not found. +** +** The term returned might by Y= if there is another constraint in +** the WHERE clause that specifies that X=Y. Any such constraints will be +** identified by the WO_EQUIV bit in the pTerm->eOperator field. The +** aEquiv[] array holds X and all its equivalents, with each SQL variable +** taking up two slots in aEquiv[]. The first slot is for the cursor number +** and the second is for the column number. There are 22 slots in aEquiv[] +** so that means we can look for X plus up to 10 other equivalent values. +** Hence a search for X will return if X=A1 and A1=A2 and A2=A3 +** and ... and A9=A10 and A10=. +** +** If there are multiple terms in the WHERE clause of the form "X " +** then try for the one with no dependencies on - in other words where +** is a constant expression of some kind. Only return entries of +** the form "X Y" where Y is a column in another table if no terms of +** the form "X " exist. If no terms with a constant RHS +** exist, try to return a term that does not use WO_EQUIV. */ static WhereTerm *findTerm( WhereClause *pWC, /* The WHERE clause to be searched */ @@ -638,45 +656,85 @@ static WhereTerm *findTerm( u32 op, /* Mask of WO_xx values describing operator */ Index *pIdx /* Must be compatible with this index, if not NULL */ ){ - WhereTerm *pTerm; - int k; + WhereTerm *pTerm; /* Term being examined as possible result */ + WhereTerm *pResult = 0; /* The answer to return */ + WhereClause *pWCOrig = pWC; /* Original pWC value */ + int j, k; /* Loop counters */ + Expr *pX; /* Pointer to an expression */ + Parse *pParse; /* Parsing context */ + int iOrigCol = iColumn; /* Original value of iColumn */ + int nEquiv = 2; /* Number of entires in aEquiv[] */ + int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */ + int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */ + assert( iCur>=0 ); - op &= WO_ALL; - for(; pWC; pWC=pWC->pOuter){ - for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ - if( pTerm->leftCursor==iCur - && (pTerm->prereqRight & notReady)==0 - && pTerm->u.leftColumn==iColumn - && (pTerm->eOperator & op)!=0 - ){ - if( iColumn>=0 && pIdx && pTerm->eOperator!=WO_ISNULL ){ - Expr *pX = pTerm->pExpr; - CollSeq *pColl; - char idxaff; - int j; - Parse *pParse = pWC->pParse; - - idxaff = pIdx->pTable->aCol[iColumn].affinity; - if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue; - - /* Figure out the collation sequence required from an index for - ** it to be useful for optimising expression pX. Store this - ** value in variable pColl. - */ - assert(pX->pLeft); - pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - if( pColl==0 ) pColl = pParse->db->pDfltColl; - - for(j=0; pIdx->aiColumn[j]!=iColumn; j++){ - if( NEVER(j>=pIdx->nColumn) ) return 0; + aEquiv[0] = iCur; + aEquiv[1] = iColumn; + for(;;){ + for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){ + for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ + if( pTerm->leftCursor==iCur + && pTerm->u.leftColumn==iColumn + ){ + if( (pTerm->prereqRight & notReady)==0 + && (pTerm->eOperator & op & WO_ALL)!=0 + ){ + if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){ + CollSeq *pColl; + char idxaff; + + pX = pTerm->pExpr; + pParse = pWC->pParse; + idxaff = pIdx->pTable->aCol[iOrigCol].affinity; + if( !sqlite3IndexAffinityOk(pX, idxaff) ){ + continue; + } + + /* Figure out the collation sequence required from an index for + ** it to be useful for optimising expression pX. Store this + ** value in variable pColl. + */ + assert(pX->pLeft); + pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight); + if( pColl==0 ) pColl = pParse->db->pDfltColl; + + for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){ + if( NEVER(j>=pIdx->nColumn) ) return 0; + } + if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){ + continue; + } + } + if( pTerm->prereqRight==0 && (pTerm->eOperator&WO_EQ)!=0 ){ + pResult = pTerm; + goto findTerm_success; + }else if( pResult==0 ){ + pResult = pTerm; + } + } + if( (pTerm->eOperator & WO_EQUIV)!=0 + && nEquivpExpr->pRight); + assert( pX->op==TK_COLUMN ); + for(j=0; jiTable && aEquiv[j+1]==pX->iColumn ) break; + } + if( j==nEquiv ){ + aEquiv[j] = pX->iTable; + aEquiv[j+1] = pX->iColumn; + nEquiv += 2; + } } - if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue; } - return pTerm; } } + if( iEquiv>=nEquiv ) break; + iCur = aEquiv[iEquiv++]; + iColumn = aEquiv[iEquiv++]; } - return 0; +findTerm_success: + return pResult; } /* Forward reference */ @@ -862,7 +920,7 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ ** ** CASE 1: ** -** If all subterms are of the form T.C=expr for some single column of C +** If all subterms are of the form T.C=expr for some single column of C and ** a single table T (as shown in example B above) then create a new virtual ** term that is an equivalent IN expression. In other words, if the term ** being analyzed is: @@ -950,11 +1008,10 @@ static void exprAnalyzeOrTerm( ** Compute the set of tables that might satisfy cases 1 or 2. */ indexable = ~(Bitmask)0; - chngToIN = ~(pWC->vmask); + chngToIN = ~(Bitmask)0; for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){ if( (pOrTerm->eOperator & WO_SINGLE)==0 ){ WhereAndInfo *pAndInfo; - assert( pOrTerm->eOperator==0 ); assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 ); chngToIN = 0; pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo)); @@ -993,7 +1050,7 @@ static void exprAnalyzeOrTerm( b |= getMask(pMaskSet, pOther->leftCursor); } indexable &= b; - if( pOrTerm->eOperator!=WO_EQ ){ + if( (pOrTerm->eOperator & WO_EQ)==0 ){ chngToIN = 0; }else{ chngToIN &= b; @@ -1044,7 +1101,7 @@ static void exprAnalyzeOrTerm( for(j=0; j<2 && !okToChngToIN; j++){ pOrTerm = pOrWc->a; for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ - assert( pOrTerm->eOperator==WO_EQ ); + assert( pOrTerm->eOperator & WO_EQ ); pOrTerm->wtFlags &= ~TERM_OR_OK; if( pOrTerm->leftCursor==iCursor ){ /* This is the 2-bit case and we are on the second iteration and @@ -1070,7 +1127,7 @@ static void exprAnalyzeOrTerm( /* No candidate table+column was found. This can only occur ** on the second iteration */ assert( j==1 ); - assert( (chngToIN&(chngToIN-1))==0 ); + assert( IsPowerOfTwo(chngToIN) ); assert( chngToIN==getMask(pMaskSet, iCursor) ); break; } @@ -1080,7 +1137,7 @@ static void exprAnalyzeOrTerm( ** table and column is common to every term in the OR clause */ okToChngToIN = 1; for(; i>=0 && okToChngToIN; i--, pOrTerm++){ - assert( pOrTerm->eOperator==WO_EQ ); + assert( pOrTerm->eOperator & WO_EQ ); if( pOrTerm->leftCursor!=iCursor ){ pOrTerm->wtFlags &= ~TERM_OR_OK; }else if( pOrTerm->u.leftColumn!=iColumn ){ @@ -1116,7 +1173,7 @@ static void exprAnalyzeOrTerm( for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){ if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue; - assert( pOrTerm->eOperator==WO_EQ ); + assert( pOrTerm->eOperator & WO_EQ ); assert( pOrTerm->leftCursor==iCursor ); assert( pOrTerm->u.leftColumn==iColumn ); pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0); @@ -1146,7 +1203,6 @@ static void exprAnalyzeOrTerm( } #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ - /* ** The input to this routine is an WhereTerm structure with only the ** "pExpr" field filled in. The job of this routine is to analyze the @@ -1215,17 +1271,19 @@ static void exprAnalyze( pTerm->leftCursor = -1; pTerm->iParent = -1; pTerm->eOperator = 0; - if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){ + if( allowedOp(op) ){ Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); + u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV; if( pLeft->op==TK_COLUMN ){ pTerm->leftCursor = pLeft->iTable; pTerm->u.leftColumn = pLeft->iColumn; - pTerm->eOperator = operatorMask(op); + pTerm->eOperator = operatorMask(op) & opMask; } if( pRight && pRight->op==TK_COLUMN ){ WhereTerm *pNew; Expr *pDup; + u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */ if( pTerm->leftCursor>=0 ){ int idxNew; pDup = sqlite3ExprDup(db, pExpr, 0); @@ -1240,6 +1298,13 @@ static void exprAnalyze( pTerm = &pWC->a[idxTerm]; pTerm->nChild = 1; pTerm->wtFlags |= TERM_COPIED; + if( pExpr->op==TK_EQ + && !ExprHasProperty(pExpr, EP_FromJoin) + && OptimizationEnabled(db, SQLITE_Transitive) + ){ + pTerm->eOperator |= WO_EQUIV; + eExtraOp = WO_EQUIV; + } }else{ pDup = pExpr; pNew = pTerm; @@ -1251,7 +1316,7 @@ static void exprAnalyze( testcase( (prereqLeft | extraRight) != prereqLeft ); pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; - pNew->eOperator = operatorMask(pDup->op); + pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; } } @@ -1710,7 +1775,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){ /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTermeOperator==WO_OR + if( (pTerm->eOperator & WO_OR)!=0 && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0 && (pTerm->u.pOrInfo->indexable & maskSrc)!=0 ){ @@ -1731,7 +1796,7 @@ static void bestOrClauseIndex(WhereBestIdx *p){ WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", (pOrTerm - pOrWC->a), (pTerm - pWC->a) )); - if( pOrTerm->eOperator==WO_AND ){ + if( (pOrTerm->eOperator& WO_AND)!=0 ){ sBOI.pWC = &pOrTerm->u.pAndInfo->wc; bestIndex(&sBOI); }else if( pOrTerm->leftCursor==iCur ){ @@ -1792,7 +1857,7 @@ static int termCanDriveIndex( ){ char aff; if( pTerm->leftCursor!=pSrc->iCursor ) return 0; - if( pTerm->eOperator!=WO_EQ ) return 0; + if( (pTerm->eOperator & WO_EQ)==0 ) return 0; if( (pTerm->prereqRight & notReady)!=0 ) return 0; aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity; if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0; @@ -2054,10 +2119,10 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ ** to this virtual table */ for(i=nTerm=0, pTerm=pWC->a; inTerm; i++, pTerm++){ if( pTerm->leftCursor != pSrc->iCursor ) continue; - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); - testcase( pTerm->eOperator==WO_IN ); - testcase( pTerm->eOperator==WO_ISNULL ); - if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue; + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); + testcase( pTerm->eOperator & WO_IN ); + testcase( pTerm->eOperator & WO_ISNULL ); + if( pTerm->eOperator & (WO_ISNULL) ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; nTerm++; } @@ -2105,15 +2170,18 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ pUsage; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ + u8 op; if( pTerm->leftCursor != pSrc->iCursor ) continue; - assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 ); - testcase( pTerm->eOperator==WO_IN ); - testcase( pTerm->eOperator==WO_ISNULL ); - if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue; + assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) ); + testcase( pTerm->eOperator & WO_IN ); + testcase( pTerm->eOperator & WO_ISNULL ); + if( pTerm->eOperator & (WO_ISNULL) ) continue; if( pTerm->wtFlags & TERM_VNULL ) continue; pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; - pIdxCons[j].op = (u8)pTerm->eOperator; + op = (u8)pTerm->eOperator & WO_ALL; + if( op==WO_IN ) op = WO_EQ; + pIdxCons[j].op = op; /* The direct assignment in the previous line is possible only because ** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The ** following asserts verify this fact. */ @@ -2123,7 +2191,7 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){ assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT ); assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE ); assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH ); - assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); + assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) ); j++; } for(i=0; iazModuleArg && pTab->azModuleArg[0] ); assert( sqlite3GetVTable(pParse->db, pTab) ); - /* Set the aConstraint[].usable fields and initialize all - ** output variables to zero. - ** - ** aConstraint[].usable is true for constraints where the right-hand - ** side contains only references to tables to the left of the current - ** table. In other words, if the constraint is of the form: - ** - ** column = expr - ** - ** and we are evaluating a join, then the constraint on column is - ** only valid if all tables referenced in expr occur to the left - ** of the table containing column. - ** - ** The aConstraints[] array contains entries for all constraints - ** on the current table. That way we only have to compute it once - ** even though we might try to pick the best index multiple times. - ** For each attempt at picking an index, the order of tables in the - ** join might be different so we have to recompute the usable flag - ** each time. + /* Try once or twice. On the first attempt, allow IN optimizations. + ** If an IN optimization is accepted by the virtual table xBestIndex + ** method, but the pInfo->aConstrainUsage.omit flag is not set, then + ** the query will not work because it might allow duplicate rows in + ** output. In that case, run the xBestIndex method a second time + ** without the IN constraints. Usually this loop only runs once. + ** The loop will exit using a "break" statement. */ - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - pUsage = pIdxInfo->aConstraintUsage; - for(i=0; inConstraint; i++, pIdxCons++){ - j = pIdxCons->iTermOffset; - pTerm = &pWC->a[j]; - pIdxCons->usable = (pTerm->prereqRight&p->notReady) ? 0 : 1; - } - memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); - if( pIdxInfo->needToFreeIdxStr ){ - sqlite3_free(pIdxInfo->idxStr); - } - pIdxInfo->idxStr = 0; - pIdxInfo->idxNum = 0; - pIdxInfo->needToFreeIdxStr = 0; - pIdxInfo->orderByConsumed = 0; - /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ - pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); - nOrderBy = pIdxInfo->nOrderBy; - if( !p->pOrderBy ){ - pIdxInfo->nOrderBy = 0; + for(bAllowIN=1; 1; bAllowIN--){ + assert( bAllowIN==0 || bAllowIN==1 ); + + /* Set the aConstraint[].usable fields and initialize all + ** output variables to zero. + ** + ** aConstraint[].usable is true for constraints where the right-hand + ** side contains only references to tables to the left of the current + ** table. In other words, if the constraint is of the form: + ** + ** column = expr + ** + ** and we are evaluating a join, then the constraint on column is + ** only valid if all tables referenced in expr occur to the left + ** of the table containing column. + ** + ** The aConstraints[] array contains entries for all constraints + ** on the current table. That way we only have to compute it once + ** even though we might try to pick the best index multiple times. + ** For each attempt at picking an index, the order of tables in the + ** join might be different so we have to recompute the usable flag + ** each time. + */ + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + pUsage = pIdxInfo->aConstraintUsage; + for(i=0; inConstraint; i++, pIdxCons++){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + if( (pTerm->prereqRight&p->notReady)==0 + && (bAllowIN || (pTerm->eOperator & WO_IN)==0) + ){ + pIdxCons->usable = 1; + }else{ + pIdxCons->usable = 0; + } + } + memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); + if( pIdxInfo->needToFreeIdxStr ){ + sqlite3_free(pIdxInfo->idxStr); + } + pIdxInfo->idxStr = 0; + pIdxInfo->idxNum = 0; + pIdxInfo->needToFreeIdxStr = 0; + pIdxInfo->orderByConsumed = 0; + /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */ + pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2); + nOrderBy = pIdxInfo->nOrderBy; + if( !p->pOrderBy ){ + pIdxInfo->nOrderBy = 0; + } + + if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ + return; + } + + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pIdxCons++){ + if( pUsage[i].argvIndex>0 ){ + j = pIdxCons->iTermOffset; + pTerm = &pWC->a[j]; + p->cost.used |= pTerm->prereqRight; + if( (pTerm->eOperator & WO_IN)!=0 ){ + if( pUsage[i].omit==0 ){ + /* Do not attempt to use an IN constraint if the virtual table + ** says that the equivalent EQ constraint cannot be safely omitted. + ** If we do attempt to use such a constraint, some rows might be + ** repeated in the output. */ + break; + } + /* A virtual table that is constrained by an IN clause may not + ** consume the ORDER BY clause because (1) the order of IN terms + ** is not necessarily related to the order of output terms and + ** (2) Multiple outputs from a single IN value will not merge + ** together. */ + pIdxInfo->orderByConsumed = 0; + } + } + } + if( i>=pIdxInfo->nConstraint ) break; } - if( vtabBestIndex(pParse, pTab, pIdxInfo) ){ - return; - } - - pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; - for(i=0; inConstraint; i++){ - if( pUsage[i].argvIndex>0 ){ - p->cost.used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; + /* The orderByConsumed signal is only valid if all outer loops collectively + ** generate just a single row of output. + */ + if( pIdxInfo->orderByConsumed ){ + for(i=0; ii; i++){ + if( (p->aLevel[i].plan.wsFlags & WHERE_UNIQUE)==0 ){ + pIdxInfo->orderByConsumed = 0; + } } } - + /* If there is an ORDER BY clause, and the selected virtual table index ** does not satisfy it, increase the cost of the scan accordingly. This ** matches the processing for non-virtual tables in bestBtreeIndex(). @@ -2590,24 +2706,24 @@ static int whereRangeScanEst( if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); - assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); + assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); if( rc==SQLITE_OK && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK ){ iLower = a[0]; - if( pLower->eOperator==WO_GT ) iLower += a[1]; + if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1]; } sqlite3ValueFree(pRangeVal); } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); - assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); + assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); if( rc==SQLITE_OK && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK ){ iUpper = a[0]; - if( pUpper->eOperator==WO_LE ) iUpper += a[1]; + if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1]; } sqlite3ValueFree(pRangeVal); } @@ -2797,7 +2913,8 @@ static int isSortingIndex( WhereBestIdx *p, /* Best index search context */ Index *pIdx, /* The index we are testing */ int base, /* Cursor number for the table to be sorted */ - int *pbRev /* Set to 1 for reverse-order scan of pIdx */ + int *pbRev, /* Set to 1 for reverse-order scan of pIdx */ + int *pbObUnique /* ORDER BY column values will different in every row */ ){ int i; /* Number of pIdx terms used */ int j; /* Number of ORDER BY terms satisfied */ @@ -2811,12 +2928,16 @@ static int isSortingIndex( int nPriorSat; /* ORDER BY terms satisfied by outer loops */ int seenRowid = 0; /* True if an ORDER BY rowid term is seen */ int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */ + int outerObUnique; /* Outer loops generate different values in + ** every row for the ORDER BY columns */ if( p->i==0 ){ nPriorSat = 0; + outerObUnique = 1; }else{ + u32 wsFlags = p->aLevel[p->i-1].plan.wsFlags; nPriorSat = p->aLevel[p->i-1].plan.nOBSat; - if( (p->aLevel[p->i-1].plan.wsFlags & WHERE_ORDERED)==0 ){ + if( (wsFlags & WHERE_ORDERED)==0 ){ /* This loop cannot be ordered unless the next outer loop is ** also ordered */ return nPriorSat; @@ -2826,6 +2947,9 @@ static int isSortingIndex( ** optimization is disabled */ return nPriorSat; } + testcase( wsFlags & WHERE_OB_UNIQUE ); + testcase( wsFlags & WHERE_ALL_UNIQUE ); + outerObUnique = (wsFlags & (WHERE_OB_UNIQUE|WHERE_ALL_UNIQUE))!=0; } pOrderBy = p->pOrderBy; assert( pOrderBy!=0 ); @@ -2915,12 +3039,9 @@ static int isSortingIndex( WO_EQ|WO_ISNULL|WO_IN, pIdx); if( pConstraint==0 ){ isEq = 0; - }else if( pConstraint->eOperator==WO_IN ){ - /* Constraints of the form: "X IN ..." cannot be used with an ORDER BY - ** because we do not know in what order the values on the RHS of the IN - ** operator will occur. */ - break; - }else if( pConstraint->eOperator==WO_ISNULL ){ + }else if( (pConstraint->eOperator & WO_IN)!=0 ){ + isEq = 0; + }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){ uniqueNotNull = 0; isEq = 1; /* "X IS NULL" means X has only a single value */ }else if( pConstraint->prereqRight==0 ){ @@ -2970,11 +3091,26 @@ static int isSortingIndex( uniqueNotNull = 0; } } + if( seenRowid ){ + uniqueNotNull = 1; + }else if( uniqueNotNull==0 || inColumn ){ + uniqueNotNull = 0; + } /* If we have not found at least one ORDER BY term that matches the ** index, then show no progress. */ if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat; + /* Either the outer queries must generate rows where there are no two + ** rows with the same values in all ORDER BY columns, or else this + ** loop must generate just a single row of output. Example: Suppose + ** the outer loops generate A=1 and A=1, and this loop generates B=3 + ** and B=4. Then without the following test, ORDER BY A,B would + ** generate the wrong order output: 1,3 1,4 1,3 1,4 + */ + if( outerObUnique==0 && uniqueNotNull==0 ) return nPriorSat; + *pbObUnique = uniqueNotNull; + /* Return the necessary scan order back to the caller */ *pbRev = sortOrder & 1; @@ -2982,7 +3118,7 @@ static int isSortingIndex( ** possible for a single row from this table to match, then skip over ** any additional ORDER BY terms dealing with this table. */ - if( seenRowid || (uniqueNotNull && i>=pIdx->nColumn) ){ + if( uniqueNotNull ){ /* Advance j over additional ORDER BY terms associated with base */ WhereMaskSet *pMS = p->pWC->pMaskSet; Bitmask m = ~getMask(pMS, base); @@ -3223,8 +3359,8 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** indicate this to the caller. ** ** Otherwise, if the search may find more than one row, test to see if - ** there is a range constraint on indexed column (pc.plan.nEq+1) that can be - ** optimized using the index. + ** there is a range constraint on indexed column (pc.plan.nEq+1) that + ** can be optimized using the index. */ if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){ testcase( pc.plan.wsFlags & WHERE_COLUMN_IN ); @@ -3266,12 +3402,14 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** variable. */ if( bSort && (pSrc->jointype & JT_LEFT)==0 ){ int bRev = 2; - WHERETRACE((" --> before isSortingIndex: nPriorSat=%d\n",nPriorSat)); - pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev); - WHERETRACE((" --> after isSortingIndex: bRev=%d nOBSat=%d\n", - bRev, pc.plan.nOBSat)); + int bObUnique = 0; + WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat)); + pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique); + WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n", + bRev, bObUnique, pc.plan.nOBSat)); if( nPriorSat1 ){ assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 ); if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ - testcase( pFirstTerm->eOperator==WO_EQ ); - testcase( pFirstTerm->eOperator==WO_ISNULL ); + testcase( pFirstTerm->eOperator & WO_EQ ); + testcase( pFirstTerm->eOperator & WO_EQUIV ); + testcase( pFirstTerm->eOperator & WO_ISNULL ); whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &pc.plan.nRow); }else if( bInEst==0 ){ - assert( pFirstTerm->eOperator==WO_IN ); + assert( pFirstTerm->eOperator & WO_IN ); whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &pc.plan.nRow); } @@ -3364,7 +3503,8 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** So this computation assumes table records are about twice as big ** as index records */ - if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED))==WHERE_IDX_ONLY + if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED|WHERE_OB_UNIQUE)) + ==WHERE_IDX_ONLY && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 && sqlite3GlobalConfig.bUseCis && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan) @@ -3485,7 +3625,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ ** selective in practice, on average. */ pc.plan.nRow /= 3; } - }else if( pTerm->eOperator!=WO_NOOP ){ + }else if( (pTerm->eOperator & WO_NOOP)==0 ){ /* Any other expression lowers the output row count by half */ pc.plan.nRow /= 2; } @@ -3524,7 +3664,7 @@ static void bestBtreeIndex(WhereBestIdx *p){ /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag ** is set, then reverse the order that the index will be scanned ** in. This is used for application testing, to help find cases - ** where application behaviour depends on the (undefined) order that + ** where application behavior depends on the (undefined) order that ** SQLite outputs rows in in the absence of an ORDER BY clause. */ if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){ p->cost.plan.wsFlags |= WHERE_REVERSE; @@ -3537,8 +3677,9 @@ static void bestBtreeIndex(WhereBestIdx *p){ || p->cost.plan.u.pIdx==pSrc->pIndex ); - WHERETRACE((" best index is: %s\n", - p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk")); + WHERETRACE((" best index is %s cost=%.1f\n", + p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", + p->cost.rCost)); bestOrClauseIndex(p); bestAutomaticIndex(p); @@ -3563,7 +3704,8 @@ static void bestIndex(WhereBestIdx *p){ sqlite3_index_info *pIdxInfo = 0; p->ppIdxInfo = &pIdxInfo; bestVirtualIndex(p); - if( pIdxInfo->needToFreeIdxStr ){ + assert( pIdxInfo!=0 || p->pParse->db->mallocFailed ); + if( pIdxInfo && pIdxInfo->needToFreeIdxStr ){ sqlite3_free(pIdxInfo->idxStr); } sqlite3DbFree(p->pParse->db, pIdxInfo); @@ -3669,7 +3811,8 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ static int codeEqualityTerm( Parse *pParse, /* The parsing context */ WhereTerm *pTerm, /* The term of the WHERE clause to be coded */ - WhereLevel *pLevel, /* When level of the FROM clause we are working on */ + WhereLevel *pLevel, /* The level of the FROM clause we are working on */ + int iEq, /* Index of the equality term within this level */ int iTarget /* Attempt to leave results in this register */ ){ Expr *pX = pTerm->pExpr; @@ -3687,12 +3830,26 @@ static int codeEqualityTerm( int eType; int iTab; struct InLoop *pIn; + u8 bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; + if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 + && pLevel->plan.u.pIdx->aSortOrder[iEq] + ){ + testcase( iEq==0 ); + testcase( iEq==pLevel->plan.u.pIdx->nColumn-1 ); + testcase( iEq>0 && iEq+1plan.u.pIdx->nColumn ); + testcase( bRev ); + bRev = !bRev; + } assert( pX->op==TK_IN ); iReg = iTarget; eType = sqlite3FindInIndex(pParse, pX, 0); + if( eType==IN_INDEX_INDEX_DESC ){ + testcase( bRev ); + bRev = !bRev; + } iTab = pX->iTable; - sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); + sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); assert( pLevel->plan.wsFlags & WHERE_IN_ABLE ); if( pLevel->u.in.nIn==0 ){ pLevel->addrNxt = sqlite3VdbeMakeLabel(v); @@ -3710,6 +3867,7 @@ static int codeEqualityTerm( }else{ pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg); } + pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next; sqlite3VdbeAddOp1(v, OP_IsNull, iReg); }else{ pLevel->u.in.nIn = 0; @@ -3804,7 +3962,7 @@ static int codeAllEqualityTerms( ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */ testcase( (pTerm->wtFlags & TERM_CODED)!=0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - r1 = codeEqualityTerm(pParse, pTerm, pLevel, regBase+j); + r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, regBase+j); if( r1!=regBase+j ){ if( nReg==1 ){ sqlite3ReleaseTempReg(pParse, regBase); @@ -4014,6 +4172,7 @@ static Bitmask codeOneLoopStart( int addrCont; /* Jump here to continue with next cycle */ int iRowidReg = 0; /* Rowid is stored in this register, if not zero */ int iReleaseReg = 0; /* Temp register to free before returning */ + Bitmask newNotReady; /* Return value */ pParse = pWInfo->pParse; v = pParse->pVdbe; @@ -4024,6 +4183,7 @@ static Bitmask codeOneLoopStart( bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0; omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0 && (wctrlFlags & WHERE_FORCE_TABLE)==0; + VdbeNoopComment((v, "Begin Join Loop %d", iLevel)); /* Create labels for the "break" and "continue" instructions ** for the current loop. Jump to addrBrk to break out of a loop. @@ -4064,6 +4224,7 @@ static Bitmask codeOneLoopStart( ** to access the data. */ int iReg; /* P3 Value for OP_VFilter */ + int addrNotFound; sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; int nConstraint = pVtabIdx->nConstraint; struct sqlite3_index_constraint_usage *aUsage = @@ -4073,11 +4234,18 @@ static Bitmask codeOneLoopStart( sqlite3ExprCachePush(pParse); iReg = sqlite3GetTempRange(pParse, nConstraint+2); + addrNotFound = pLevel->addrBrk; for(j=1; j<=nConstraint; j++){ for(k=0; ka[iTerm].pExpr->pRight, iReg+j+1); + int iTarget = iReg+j+1; + pTerm = &pWC->a[aConstraint[k].iTermOffset]; + if( pTerm->eOperator & WO_IN ){ + codeEqualityTerm(pParse, pTerm, pLevel, k, iTarget); + addrNotFound = pLevel->addrNxt; + }else{ + sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget); + } break; } } @@ -4085,7 +4253,7 @@ static Bitmask codeOneLoopStart( } sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg); sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1); - sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr, + sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr, pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC); pVtabIdx->needToFreeIdxStr = 0; for(j=0; jpExpr!=0 ); - assert( pTerm->leftCursor==iCur ); assert( omitTable==0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */ - iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg); + iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, iReleaseReg); addrNxt = pLevel->addrNxt; sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); + sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); VdbeComment((v, "pk")); pLevel->op = OP_Noop; @@ -4503,7 +4671,7 @@ static Bitmask codeOneLoopStart( pTerm = pLevel->plan.u.pTerm; assert( pTerm!=0 ); - assert( pTerm->eOperator==WO_OR ); + assert( pTerm->eOperator & WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; pLevel->op = OP_Return; @@ -4558,6 +4726,10 @@ static Bitmask codeOneLoopStart( ** the "interesting" terms of z - terms that did not originate in the ** ON or USING clause of a LEFT JOIN, and terms that are usable as ** indices. + ** + ** This optimization also only applies if the (x1 OR x2 OR ...) term + ** is not contained in the ON clause of a LEFT JOIN. + ** See ticket http://www.sqlite.org/src/info/f2369304e4 */ if( pWC->nTerm>1 ){ int iTerm; @@ -4576,10 +4748,10 @@ static Bitmask codeOneLoopStart( for(ii=0; iinTerm; ii++){ WhereTerm *pOrTerm = &pOrWc->a[ii]; - if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ + if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; - if( pAndExpr ){ + if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){ pAndExpr->pLeft = pOrExpr; pOrExpr = pAndExpr; } @@ -4666,7 +4838,7 @@ static Bitmask codeOneLoopStart( pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } - notReady &= ~getMask(pWC->pMaskSet, iCur); + newNotReady = notReady & ~getMask(pWC->pMaskSet, iCur); /* Insert code to test every subexpression that can be completely ** computed using the current set of tables. @@ -4680,7 +4852,7 @@ static Bitmask codeOneLoopStart( testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ){ + if( (pTerm->prereqAll & newNotReady)!=0 ){ testcase( pWInfo->untestedTerms==0 && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); pWInfo->untestedTerms = 1; @@ -4695,6 +4867,33 @@ static Bitmask codeOneLoopStart( pTerm->wtFlags |= TERM_CODED; } + /* Insert code to test for implied constraints based on transitivity + ** of the "==" operator. + ** + ** Example: If the WHERE clause contains "t1.a=t2.b" and "t2.b=123" + ** and we are coding the t1 loop and the t2 loop has not yet coded, + ** then we cannot use the "t1.a=t2.b" constraint, but we can code + ** the implied "t1.a=123" constraint. + */ + for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ + Expr *pE; + WhereTerm *pAlt; + Expr sEq; + if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; + if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue; + if( pTerm->leftCursor!=iCur ) continue; + pE = pTerm->pExpr; + assert( !ExprHasProperty(pE, EP_FromJoin) ); + assert( (pTerm->prereqRight & newNotReady)!=0 ); + pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0); + if( pAlt==0 ) continue; + if( pAlt->wtFlags & (TERM_CODED) ) continue; + VdbeNoopComment((v, "begin transitive constraint")); + sEq = *pAlt->pExpr; + sEq.pLeft = pE->pLeft; + sqlite3ExprIfFalse(pParse, &sEq, addrCont, SQLITE_JUMPIFNULL); + } + /* For a LEFT OUTER JOIN, generate code that will record the fact that ** at least one row of the right table has matched the left table. */ @@ -4707,7 +4906,7 @@ static Bitmask codeOneLoopStart( testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ){ + if( (pTerm->prereqAll & newNotReady)!=0 ){ assert( pWInfo->untestedTerms ); continue; } @@ -4718,7 +4917,7 @@ static Bitmask codeOneLoopStart( } sqlite3ReleaseTempReg(pParse, iReleaseReg); - return notReady; + return newNotReady; } #if defined(SQLITE_TEST) @@ -4954,24 +5153,13 @@ WhereInfo *sqlite3WhereBegin( ** bitmask for all tables to the left of the join. Knowing the bitmask ** for all tables to the left of a left join is important. Ticket #3015. ** - ** Configure the WhereClause.vmask variable so that bits that correspond - ** to virtual table cursors are set. This is used to selectively disable - ** the OR-to-IN transformation in exprAnalyzeOrTerm(). It is not helpful - ** with virtual tables. - ** ** Note that bitmasks are created for all pTabList->nSrc tables in ** pTabList, not just the first nTabList tables. nTabList is normally ** equal to pTabList->nSrc but might be shortened to 1 if the ** WHERE_ONETABLE_ONLY flag is set. */ - assert( sWBI.pWC->vmask==0 && pMaskSet->n==0 ); for(ii=0; iinSrc; ii++){ createMask(pMaskSet, pTabList->a[ii].iCursor); -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( ALWAYS(pTabList->a[ii].pTab) && IsVirtual(pTabList->a[ii].pTab) ){ - sWBI.pWC->vmask |= ((Bitmask)1 << ii); - } -#endif } #ifndef NDEBUG { @@ -5031,6 +5219,7 @@ WhereInfo *sqlite3WhereBegin( int bestJ = -1; /* The value of j */ Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ + int ckOptimal; /* Do the optimal scan check */ int nUnconstrained; /* Number tables without INDEXED BY */ Bitmask notIndexed; /* Mask of tables that cannot use an index */ @@ -5065,10 +5254,8 @@ WhereInfo *sqlite3WhereBegin( ** strategies were found by the first iteration. This second iteration ** is used to search for the lowest cost scan overall. ** - ** Previous versions of SQLite performed only the second iteration - - ** the next outermost loop was always that with the lowest overall - ** cost. However, this meant that SQLite could select the wrong plan - ** for scripts such as the following: + ** Without the optimal scan step (the first iteration) a suboptimal + ** plan might be chosen for queries like this: ** ** CREATE TABLE t1(a, b); ** CREATE TABLE t2(c, d); @@ -5083,17 +5270,41 @@ WhereInfo *sqlite3WhereBegin( */ nUnconstrained = 0; notIndexed = 0; - for(isOptimal=(iFrom=0 && bestJ<0; isOptimal--){ + + /* The optimal scan check only occurs if there are two or more tables + ** available to be reordered */ + if( iFrom==nTabList-1 ){ + ckOptimal = 0; /* Common case of just one table in the FROM clause */ + }else{ + ckOptimal = -1; for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; jjointype & (JT_LEFT|JT_CROSS))!=0; - if( j!=iFrom && doNotReorder ) break; m = getMask(pMaskSet, sWBI.pSrc->iCursor); if( (m & sWBI.notValid)==0 ){ if( j==iFrom ) iFrom++; continue; } + if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break; + if( ++ckOptimal ) break; + if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break; + } + } + assert( ckOptimal==0 || ckOptimal==1 ); + + for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){ + for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; jiFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){ + /* This break and one like it in the ckOptimal computation loop + ** above prevent table reordering across LEFT and CROSS JOINs. + ** The LEFT JOIN case is necessary for correctness. The prohibition + ** against reordering across a CROSS JOIN is an SQLite feature that + ** allows the developer to control table reordering */ + break; + } + m = getMask(pMaskSet, sWBI.pSrc->iCursor); + if( (m & sWBI.notValid)==0 ){ + assert( j>iFrom ); + continue; + } sWBI.notReady = (isOptimal ? m : sWBI.notValid); if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; @@ -5122,8 +5333,8 @@ WhereInfo *sqlite3WhereBegin( } if( isOptimal ){ pWInfo->a[j].rOptCost = sWBI.cost.rCost; - }else if( iFromjointype & JT_LEFT)!=0 ) break; } } assert( bestJ>=0 ); assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); + assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 ); + testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); + testcase( bestJ>iFrom && bestJa[bestJ+1].jointype & JT_LEFT)!=0 ); WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", bestJ, pTabList->a[bestJ].pTab->zName, @@ -5424,7 +5643,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeResolveLabel(v, pLevel->addrNxt); for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ sqlite3VdbeJumpHere(v, pIn->addrInTop+1); - sqlite3VdbeAddOp2(v, OP_Next, pIn->iCur, pIn->addrInTop); + sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); sqlite3VdbeJumpHere(v, pIn->addrInTop-1); } sqlite3DbFree(db, pLevel->u.in.aInLoop); diff --git a/test/8_3_names.test b/test/8_3_names.test index b53e28a..1d63f5d 100644 --- a/test/8_3_names.test +++ b/test/8_3_names.test @@ -150,7 +150,7 @@ db close forcedelete test.db do_test 8_3_names-5.0 { sqlite3 db file:./test.db?8_3_names=1 - register_wholenumber_module db + load_static_extension db wholenumber db eval { PRAGMA journal_mode=WAL; CREATE TABLE t1(x); @@ -160,7 +160,7 @@ do_test 8_3_names-5.0 { UPDATE t1 SET x=x*2; } sqlite3 db2 file:./test.db?8_3_names=1 - register_wholenumber_module db2 + load_static_extension db2 wholenumber db2 eval { BEGIN; SELECT sum(x) FROM t1; diff --git a/test/analyze7.test b/test/analyze7.test index 5bdb04d..46ec39e 100644 --- a/test/analyze7.test +++ b/test/analyze7.test @@ -26,7 +26,7 @@ ifcapable {!analyze||!vtab} { # Generate some test data # do_test analyze7-1.0 { - register_wholenumber_module db + load_static_extension db wholenumber execsql { CREATE TABLE t1(a,b,c,d); CREATE INDEX t1a ON t1(a); diff --git a/test/auth.test b/test/auth.test index 211ae7e..fd402b1 100644 --- a/test/auth.test +++ b/test/auth.test @@ -2262,7 +2262,9 @@ do_test auth-4.3 { SQLITE_SELECT {} {} {} v1 \ SQLITE_READ t2 a main v1 \ SQLITE_READ t2 b main v1 \ - SQLITE_SELECT {} {} {} {} \ + SQLITE_READ v1 x main v1 \ + SQLITE_READ v1 x main v1 \ + SQLITE_SELECT {} {} {} v1 \ SQLITE_READ v1 x main v1 \ SQLITE_INSERT v1chng {} main r2 \ SQLITE_READ v1 x main r2 \ @@ -2288,7 +2290,9 @@ do_test auth-4.5 { SQLITE_SELECT {} {} {} v1 \ SQLITE_READ t2 a main v1 \ SQLITE_READ t2 b main v1 \ - SQLITE_SELECT {} {} {} {} \ + SQLITE_READ v1 x main v1 \ + SQLITE_READ v1 x main v1 \ + SQLITE_SELECT {} {} {} v1 \ SQLITE_READ v1 x main v1 \ SQLITE_INSERT v1chng {} main r3 \ SQLITE_READ v1 x main r3 \ @@ -2364,6 +2368,29 @@ ifcapable trigger { } {1} } +# Ticket [0eb70d77cb05bb22720]: Invalid pointer passsed to the authorizer +# callback when updating a ROWID. +# +do_test auth-6.1 { + execsql { + CREATE TABLE t6(a,b,c,d,e,f,g,h); + INSERT INTO t6 VALUES(1,2,3,4,5,6,7,8); + } +} {} +set ::authargs [list] +proc auth {args} { + eval lappend ::authargs $args + return SQLITE_OK +} +do_test auth-6.2 { + execsql {UPDATE t6 SET rowID=rowID+100} + set ::authargs +} [list SQLITE_READ t6 ROWID main {} \ + SQLITE_UPDATE t6 ROWID main {} \ +] +do_test auth-6.3 { + execsql {SELECT rowid, * FROM t6} +} {101 1 2 3 4 5 6 7 8} rename proc {} rename proc_real proc diff --git a/test/auth2.test b/test/auth2.test index f5dba14..9343fd6 100644 --- a/test/auth2.test +++ b/test/auth2.test @@ -131,12 +131,12 @@ do_test auth2-2.3 { } set ::authargs } {SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 a main {} -SQLITE_READ v2 b main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 a main {} +SQLITE_READ v2 b main {} SQLITE_SELECT {} {} {} v2 } do_test auth2-2.4 { @@ -149,20 +149,20 @@ do_test auth2-2.4 { } set ::authargs } {SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 b main {} -SQLITE_READ v2 a main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 b main {} +SQLITE_READ v2 a main {} SQLITE_SELECT {} {} {} v2 SQLITE_SELECT {} {} {} {} -SQLITE_READ v2 b main {} -SQLITE_READ v2 a main {} SQLITE_READ t2 x main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 y main v2 SQLITE_READ t2 z main v2 +SQLITE_READ v2 b main {} +SQLITE_READ v2 a main {} SQLITE_SELECT {} {} {} v2 } db2 close diff --git a/test/autoindex1.test b/test/autoindex1.test index 6c8d54d..54ff82a 100644 --- a/test/autoindex1.test +++ b/test/autoindex1.test @@ -257,5 +257,129 @@ do_execsql_test autoindex1-700 { 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } +# The following checks a performance issue reported on the sqlite-dev +# mailing list on 2013-01-10 +# +do_execsql_test autoindex1-800 { + CREATE TABLE accounts( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + account_name TEXT, + account_type TEXT, + data_set TEXT + ); + CREATE TABLE data( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + package_id INTEGER REFERENCES package(_id), + mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL, + raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL, + is_read_only INTEGER NOT NULL DEFAULT 0, + is_primary INTEGER NOT NULL DEFAULT 0, + is_super_primary INTEGER NOT NULL DEFAULT 0, + data_version INTEGER NOT NULL DEFAULT 0, + data1 TEXT, + data2 TEXT, + data3 TEXT, + data4 TEXT, + data5 TEXT, + data6 TEXT, + data7 TEXT, + data8 TEXT, + data9 TEXT, + data10 TEXT, + data11 TEXT, + data12 TEXT, + data13 TEXT, + data14 TEXT, + data15 TEXT, + data_sync1 TEXT, + data_sync2 TEXT, + data_sync3 TEXT, + data_sync4 TEXT + ); + CREATE TABLE mimetypes( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + mimetype TEXT NOT NULL + ); + CREATE TABLE raw_contacts( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + account_id INTEGER REFERENCES accounts(_id), + sourceid TEXT, + raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0, + version INTEGER NOT NULL DEFAULT 1, + dirty INTEGER NOT NULL DEFAULT 0, + deleted INTEGER NOT NULL DEFAULT 0, + contact_id INTEGER REFERENCES contacts(_id), + aggregation_mode INTEGER NOT NULL DEFAULT 0, + aggregation_needed INTEGER NOT NULL DEFAULT 1, + custom_ringtone TEXT, + send_to_voicemail INTEGER NOT NULL DEFAULT 0, + times_contacted INTEGER NOT NULL DEFAULT 0, + last_time_contacted INTEGER, + starred INTEGER NOT NULL DEFAULT 0, + display_name TEXT, + display_name_alt TEXT, + display_name_source INTEGER NOT NULL DEFAULT 0, + phonetic_name TEXT, + phonetic_name_style TEXT, + sort_key TEXT, + sort_key_alt TEXT, + name_verified INTEGER NOT NULL DEFAULT 0, + sync1 TEXT, + sync2 TEXT, + sync3 TEXT, + sync4 TEXT, + sync_uid TEXT, + sync_version INTEGER NOT NULL DEFAULT 1, + has_calendar_event INTEGER NOT NULL DEFAULT 0, + modified_time INTEGER, + is_restricted INTEGER DEFAULT 0, + yp_source TEXT, + method_selected INTEGER DEFAULT 0, + custom_vibration_type INTEGER DEFAULT 0, + custom_ringtone_path TEXT, + message_notification TEXT, + message_notification_path TEXT + ); + CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1); + CREATE INDEX data_raw_contact_id ON data (raw_contact_id); + CREATE UNIQUE INDEX mime_type ON mimetypes (mimetype); + CREATE INDEX raw_contact_sort_key1_index ON raw_contacts (sort_key); + CREATE INDEX raw_contact_sort_key2_index ON raw_contacts (sort_key_alt); + CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id); + CREATE INDEX raw_contacts_source_id_account_id_index + ON raw_contacts (sourceid, account_id); + ANALYZE sqlite_master; + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contact_sort_key2_index','1600 4'); + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contact_sort_key1_index','1600 4'); + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contacts_source_id_account_id_index', + '1600 1600 1600'); + INSERT INTO sqlite_stat1 + VALUES('raw_contacts','raw_contacts_contact_id_index','1600 1'); + INSERT INTO sqlite_stat1 VALUES('mimetypes','mime_type','12 1'); + INSERT INTO sqlite_stat1 + VALUES('data','data_mimetype_data1_index','9819 2455 3'); + INSERT INTO sqlite_stat1 VALUES('data','data_raw_contact_id','9819 7'); + INSERT INTO sqlite_stat1 VALUES('accounts',NULL,'1'); + DROP TABLE IF EXISTS sqlite_stat3; + ANALYZE sqlite_master; + + EXPLAIN QUERY PLAN + SELECT * FROM + data JOIN mimetypes ON (data.mimetype_id=mimetypes._id) + JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id) + JOIN accounts ON (raw_contacts.account_id=accounts._id) + WHERE mimetype_id=10 AND data14 IS NOT NULL; +} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/} +do_execsql_test autoindex1-801 { + EXPLAIN QUERY PLAN + SELECT * FROM + data JOIN mimetypes ON (data.mimetype_id=mimetypes._id) + JOIN raw_contacts ON (data.raw_contact_id=raw_contacts._id) + JOIN accounts ON (raw_contacts.account_id=accounts._id) + WHERE mimetypes._id=10 AND data14 IS NOT NULL; +} {/SEARCH TABLE data .*SEARCH TABLE raw_contacts/} finish_test diff --git a/test/backup_ioerr.test b/test/backup_ioerr.test index 313cff3..ca3fd32 100644 --- a/test/backup_ioerr.test +++ b/test/backup_ioerr.test @@ -115,7 +115,7 @@ proc clear_ioerr_simulation {} { # reported, then the backup process is concluded with a call to # backup_finish(). # -# Test that if an IO error occurs, or if one occured while updating +# Test that if an IO error occurs, or if one occurred while updating # the backup database during step 4, then the conditions listed # under step 3 are all true. # @@ -214,7 +214,7 @@ for {set iError 1} {$bStop == 0} {incr iError} { set rc [catchsql { UPDATE t1 SET b = randstr(1000,1000) WHERE a < 50 } sdb] if {[lindex $rc 0] && $::sqlite_io_error_persist==0} { - # The IO error occured while updating the source database. In this + # The IO error occurred while updating the source database. In this # case the backup should be able to continue. set rc [B step 5000] if { $rc != "SQLITE_IOERR_UNLOCK" } { diff --git a/test/cache.test b/test/cache.test index f81948b..ffc25c4 100644 --- a/test/cache.test +++ b/test/cache.test @@ -46,7 +46,7 @@ do_test cache-1.2 { # leaked, but would not be reused until the pager-cache was full (i.e. # 2000 pages by default). # -# This tests that once the pager-cache is initialised, it can be locked +# This tests that once the pager-cache is initialized, it can be locked # and unlocked repeatedly without internally allocating any new pages. # set cache_size [pager_cache_size db] diff --git a/test/capi2.test b/test/capi2.test index 8b36ac6..20ee340 100644 --- a/test/capi2.test +++ b/test/capi2.test @@ -235,8 +235,9 @@ do_test capi2-3.13 { do_test capi2-3.13b {db changes} {0} do_test capi2-3.14 { - list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] -} {SQLITE_CONSTRAINT {column a is not unique}} + list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \ + [sqlite3_extended_errcode $DB] +} {SQLITE_CONSTRAINT {column a is not unique} SQLITE_CONSTRAINT_UNIQUE} do_test capi2-3.15 { set VM [sqlite3_prepare $DB {CREATE TABLE t2(a NOT NULL, b)} -1 TAIL] set TAIL @@ -258,8 +259,9 @@ do_test capi2-3.18 { [get_column_names $VM] } {SQLITE_ERROR 0 {} {}} do_test capi2-3.19 { - list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] -} {SQLITE_CONSTRAINT {t2.a may not be NULL}} + list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \ + [sqlite3_extended_errcode $DB] +} {SQLITE_CONSTRAINT {t2.a may not be NULL} SQLITE_CONSTRAINT_NOTNULL} do_test capi2-3.20 { execsql { @@ -278,8 +280,8 @@ do_test capi2-3.23 { sqlite3_finalize $VM } {SQLITE_CONSTRAINT} do_test capi2-3.24 { - sqlite3_errcode $DB -} {SQLITE_CONSTRAINT} + list [sqlite3_errcode $DB] [sqlite3_extended_errcode $DB] +} {SQLITE_CONSTRAINT SQLITE_CONSTRAINT_UNIQUE} # Two or more virtual machines exists at the same time. # diff --git a/test/check.test b/test/check.test index bf0b770..99b72ac 100644 --- a/test/check.test +++ b/test/check.test @@ -15,6 +15,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix check # Only run these tests if the build includes support for CHECK constraints ifcapable !check { @@ -413,4 +414,42 @@ do_test check-6.15 { } +#-------------------------------------------------------------------------- +# If a connection opens a database that contains a CHECK constraint that +# uses an unknown UDF, the schema should not be considered malformed. +# Attempting to modify the table should fail (since the CHECK constraint +# cannot be tested). +# +reset_db +proc myfunc {x} {expr $x < 10} +db func myfunc myfunc + +do_execsql_test 7.1 { CREATE TABLE t6(a CHECK (myfunc(a))) } +do_execsql_test 7.2 { INSERT INTO t6 VALUES(9) } +do_catchsql_test 7.3 { INSERT INTO t6 VALUES(11) } {1 {constraint failed}} + +do_test 7.4 { + sqlite3 db2 test.db + execsql { SELECT * FROM t6 } db2 +} {9} + +do_test 7.5 { + catchsql { INSERT INTO t6 VALUES(8) } db2 +} {1 {unknown function: myfunc()}} + +do_test 7.6 { + catchsql { CREATE TABLE t7(a CHECK (myfunc(a))) } db2 +} {1 {no such function: myfunc}} + +do_test 7.7 { + db2 func myfunc myfunc + execsql { INSERT INTO t6 VALUES(8) } db2 +} {} + +do_test 7.8 { + db2 func myfunc myfunc + catchsql { INSERT INTO t6 VALUES(12) } db2 +} {1 {constraint failed}} + + finish_test diff --git a/test/collate3.test b/test/collate3.test index c4dbfbe..2c051cb 100644 --- a/test/collate3.test +++ b/test/collate3.test @@ -55,6 +55,104 @@ execsql { DROP TABLE collate3t1; } +proc caseless {a b} { string compare -nocase $a $b } +do_test collate3-1.4 { + db collate caseless caseless + execsql { + CREATE TABLE t1(a COLLATE caseless); + INSERT INTO t1 VALUES('Abc2'); + INSERT INTO t1 VALUES('abc1'); + INSERT INTO t1 VALUES('aBc3'); + } + execsql { SELECT * FROM t1 ORDER BY a } +} {abc1 Abc2 aBc3} + +do_test collate3-1.5 { + db close + sqlite3 db test.db + catchsql { SELECT * FROM t1 ORDER BY a } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.6.1 { + db collate caseless caseless + execsql { CREATE INDEX i1 ON t1(a) } + execsql { SELECT * FROM t1 ORDER BY a } +} {abc1 Abc2 aBc3} + +do_test collate3-1.6.2 { + db close + sqlite3 db test.db + catchsql { SELECT * FROM t1 ORDER BY a } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.6.3 { + db close + sqlite3 db test.db + catchsql { PRAGMA integrity_check } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.6.4 { + db close + sqlite3 db test.db + catchsql { REINDEX } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.1 { + db collate caseless caseless + execsql { + DROP TABLE t1; + CREATE TABLE t1(a); + CREATE INDEX i1 ON t1(a COLLATE caseless); + INSERT INTO t1 VALUES('Abc2'); + INSERT INTO t1 VALUES('abc1'); + INSERT INTO t1 VALUES('aBc3'); + SELECT * FROM t1 ORDER BY a COLLATE caseless; + } +} {abc1 Abc2 aBc3} + +do_test collate3-1.7.2 { + db close + sqlite3 db test.db + catchsql { SELECT * FROM t1 ORDER BY a COLLATE caseless} +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.4 { + db close + sqlite3 db test.db + catchsql { REINDEX } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.3 { + db close + sqlite3 db test.db + catchsql { PRAGMA integrity_check } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.4 { + db close + sqlite3 db test.db + catchsql { REINDEX } +} {1 {no such collation sequence: caseless}} + +do_test collate3-1.7.5 { + db close + sqlite3 db test.db + db collate caseless caseless + catchsql { PRAGMA integrity_check } +} {0 ok} + +proc needed {nm} { db collate caseless caseless } +do_test collate3-1.7.6 { + db close + sqlite3 db test.db + db collation_needed needed + catchsql { PRAGMA integrity_check } +} {0 ok} + +do_test collate3-1.8 { + execsql { DROP TABLE t1 } +} {} + # # Create a table with a default collation sequence, then close # and re-open the database without re-registering the collation diff --git a/test/collate4.test b/test/collate4.test index 6b3a1c7..5ae1e7b 100644 --- a/test/collate4.test +++ b/test/collate4.test @@ -60,7 +60,7 @@ proc cksort {sql} { # # Because these tests also exercise all the different ways indices # can be created, they also serve to verify that indices are correctly -# initialised with user-defined collation sequences when they are +# initialized with user-defined collation sequences when they are # created. # # Tests named collate4-1.1.* use indices with a single column. Tests diff --git a/test/conflict.test b/test/conflict.test index 17c7263..6576959 100644 --- a/test/conflict.test +++ b/test/conflict.test @@ -580,6 +580,7 @@ do_test conflict-9.19 { SELECT * FROM t2; } } {1 {column e is not unique}} +verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE do_test conflict-9.20 { catch {execsql {COMMIT}} execsql {SELECT * FROM t3} @@ -592,6 +593,7 @@ do_test conflict-9.21 { SELECT * FROM t2; } } {1 {column e is not unique}} +verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE do_test conflict-9.22 { catch {execsql {COMMIT}} execsql {SELECT * FROM t3} @@ -781,6 +783,7 @@ do_test conflict-12.3 { UPDATE t5 SET a=a+1 WHERE a=1; } } {1 {PRIMARY KEY must be unique}} +verify_ex_errcode conflict-12.3b SQLITE_CONSTRAINT_PRIMARYKEY do_test conflict-12.4 { execsql { UPDATE OR REPLACE t5 SET a=a+1 WHERE a=1; @@ -802,6 +805,7 @@ do_test conflict-13.1 { REPLACE INTO t13 VALUES(2); } } {1 {constraint failed}} +verify_ex_errcode conflict-13.1b SQLITE_CONSTRAINT_CHECK do_test conflict-13.2 { execsql { REPLACE INTO t13 VALUES(3); diff --git a/test/crash5.test b/test/crash5.test index a786712..83d1647 100644 --- a/test/crash5.test +++ b/test/crash5.test @@ -65,7 +65,7 @@ for {set ii 0} {$ii < 10} {incr ii} { # puts "$n $msg ac=[sqlite3_get_autocommit db]" # If the transaction is still active (it may not be if the malloc() - # failure occured in the OS layer), write to the database. Make sure + # failure occurred in the OS layer), write to the database. Make sure # page 4 is among those written. # if {![sqlite3_get_autocommit db]} { diff --git a/test/dbstatus2.test b/test/dbstatus2.test index 18bb087..2541a1a 100644 --- a/test/dbstatus2.test +++ b/test/dbstatus2.test @@ -40,6 +40,7 @@ proc db_write {db {reset 0}} { do_test 1.1 { db close sqlite3 db test.db + execsql { PRAGMA mmap_size = 0 } expr {[file size test.db] / 1024} } 6 diff --git a/test/descidx3.test b/test/descidx3.test index 3cc87af..c375acc 100644 --- a/test/descidx3.test +++ b/test/descidx3.test @@ -132,11 +132,11 @@ ifcapable subquery { # the IN(...) operator is not available. Hence these tests cannot be # run. do_test descidx3-4.1 { - execsql { + lsort [execsql { UPDATE t1 SET a=2 WHERE i<6; SELECT i FROM t1 WHERE a IN (1,2) AND b>0 AND b<'zzz'; - } - } {8 6 2 4 3} + }] + } {2 3 4 6 8} do_test descidx3-4.2 { execsql { UPDATE t1 SET a=1; diff --git a/test/e_createtable.test b/test/e_createtable.test index 35f7330..351a0f7 100644 --- a/test/e_createtable.test +++ b/test/e_createtable.test @@ -1257,7 +1257,7 @@ do_createtable_tests 4.4 { # SQLite allows NULL values in a PRIMARY KEY column. # # If the column is an integer primary key, attempting to insert a NULL -# into the column triggers the auto-increment behaviour. Attempting +# into the column triggers the auto-increment behavior. Attempting # to use UPDATE to set an ipk column to a NULL value is an error. # do_createtable_tests 4.5.1 { diff --git a/test/e_fkey.test b/test/e_fkey.test index 69e0868..001ba6c 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -627,7 +627,8 @@ proc test_efkey_57 {tn isError sql} { execsql $sql do_test e_fkey-18.$tn { catchsql { INSERT INTO t2 VALUES(NULL) } - } [lindex {{0 {}} {1 {foreign key mismatch}}} $isError] + } [lindex {{0 {}} {/1 {foreign key mismatch - ".*" referencing ".*"}/}} \ + $isError] } test_efkey_57 2 0 { CREATE TABLE t1(x PRIMARY KEY) } test_efkey_57 3 0 { CREATE TABLE t1(x UNIQUE) } @@ -698,16 +699,16 @@ do_test e_fkey-19.2 { } {} do_test e_fkey-19.2 { catchsql { INSERT INTO child4 VALUES('xxx', 5) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child4" referencing "parent"}} do_test e_fkey-19.3 { catchsql { INSERT INTO child5 VALUES('xxx', 6) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child5" referencing "parent"}} do_test e_fkey-19.4 { catchsql { INSERT INTO child6 VALUES(2, 3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child6" referencing "parent"}} do_test e_fkey-19.5 { catchsql { INSERT INTO child7 VALUES(3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child7" referencing "parent"}} #------------------------------------------------------------------------- # Test errors in the database schema that are detected while preparing @@ -765,12 +766,12 @@ do_test e_fkey-20.1 { foreach {tn tbl ptbl err} { 2 c1 {} "no such table: main.nosuchtable" - 3 c2 p2 "foreign key mismatch" - 4 c3 p3 "foreign key mismatch" - 5 c4 p4 "foreign key mismatch" - 6 c5 p5 "foreign key mismatch" - 7 c6 p6 "foreign key mismatch" - 8 c7 p7 "foreign key mismatch" + 3 c2 p2 "foreign key mismatch - \"c2\" referencing \"p2\"" + 4 c3 p3 "foreign key mismatch - \"c3\" referencing \"p3\"" + 5 c4 p4 "foreign key mismatch - \"c4\" referencing \"p4\"" + 6 c5 p5 "foreign key mismatch - \"c5\" referencing \"p5\"" + 7 c6 p6 "foreign key mismatch - \"c6\" referencing \"p6\"" + 8 c7 p7 "foreign key mismatch - \"c7\" referencing \"p7\"" } { do_test e_fkey-20.$tn.1 { catchsql "INSERT INTO $tbl VALUES('a', 'b')" @@ -820,22 +821,22 @@ do_test e_fkey-21.2 { } {} do_test e_fkey-21.3 { catchsql { INSERT INTO child9 VALUES('I') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.4 { catchsql { INSERT INTO child9 VALUES('II') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.5 { catchsql { INSERT INTO child9 VALUES(NULL) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child9" referencing "parent2"}} do_test e_fkey-21.6 { catchsql { INSERT INTO child10 VALUES('I', 'II', 'III') } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} do_test e_fkey-21.7 { catchsql { INSERT INTO child10 VALUES(1, 2, 3) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} do_test e_fkey-21.8 { catchsql { INSERT INTO child10 VALUES(NULL, NULL, NULL) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "child10" referencing "parent2"}} #------------------------------------------------------------------------- # Test errors that are reported when creating the child table. @@ -1151,7 +1152,7 @@ do_test e_fkey-28.8 { CREATE TABLE c(a, b, FOREIGN KEY(a,b) REFERENCES p); } catchsql {DELETE FROM p} -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c" referencing "p"}} do_test e_fkey-28.9 { drop_all_tables execsql { @@ -1159,7 +1160,7 @@ do_test e_fkey-28.9 { CREATE TABLE c(a REFERENCES p); } catchsql {DELETE FROM p} -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c" referencing "p"}} #------------------------------------------------------------------------- @@ -2729,19 +2730,19 @@ do_test e_fkey-60.3 { do_test e_fkey-60.4 { execsql { CREATE TABLE nosuchtable(x PRIMARY KEY) } catchsql { DELETE FROM p } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c2" referencing "p"}} do_test e_fkey-60.5 { execsql { DROP TABLE c1 } catchsql { DELETE FROM p } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "c2" referencing "p"}} do_test e_fkey-60.6 { execsql { DROP TABLE c2 } execsql { DELETE FROM p } } {} #------------------------------------------------------------------------- -# Test that the special behaviours of ALTER and DROP TABLE are only -# activated when foreign keys are enabled. Special behaviours are: +# Test that the special behaviors of ALTER and DROP TABLE are only +# activated when foreign keys are enabled. Special behaviors are: # # 1. ADD COLUMN not allowing a REFERENCES clause with a non-NULL # default value. @@ -2836,7 +2837,7 @@ foreach zMatch [list SIMPLE PARTIAL FULL Simple parTIAL FuLL ] { do_test e_fkey-62.$zMatch.2 { execsql { INSERT INTO p VALUES(1, 2, 3) } - # MATCH SIMPLE behaviour: Allow any child key that contains one or more + # MATCH SIMPLE behavior: Allow any child key that contains one or more # NULL value to be inserted. Non-NULL values do not have to map to any # parent key values, so long as at least one field of the child key is # NULL. diff --git a/test/e_select.test b/test/e_select.test index fb63d05..ea44aed 100644 --- a/test/e_select.test +++ b/test/e_select.test @@ -1227,7 +1227,7 @@ do_select_tests e_select-5.1 { # the entire set of result rows are returned by the SELECT. # # EVIDENCE-OF: R-47911-02086 If neither ALL or DISTINCT are present, -# then the behaviour is as if ALL were specified. +# then the behavior is as if ALL were specified. # # EVIDENCE-OF: R-14442-41305 If the simple SELECT is a SELECT DISTINCT, # then duplicate rows are removed from the set of result rows before it diff --git a/test/e_uri.test b/test/e_uri.test index f16c0cb..ac34ed4 100644 --- a/test/e_uri.test +++ b/test/e_uri.test @@ -361,7 +361,7 @@ foreach {tn uri error} " # # EVIDENCE-OF: R-19510-48080 If sqlite3_open_v2() is used and the # "cache" parameter is present in a URI filename, its value overrides -# any behaviour requested by setting SQLITE_OPEN_PRIVATECACHE or +# any behavior requested by setting SQLITE_OPEN_PRIVATECACHE or # SQLITE_OPEN_SHAREDCACHE flag. # set orig [sqlite3_enable_shared_cache] diff --git a/test/enc2.test b/test/enc2.test index 415bc0f..3eb3aa2 100644 --- a/test/enc2.test +++ b/test/enc2.test @@ -32,7 +32,7 @@ ifcapable {!utf16} { # enc2.3.*: Simple tests with a UTF-16BE db. # enc2.4.*: Test that attached databases must have the same text encoding # as the main database. -# enc2.5.*: Test the behaviour of the library when a collation sequence is +# enc2.5.*: Test the behavior of the library when a collation sequence is # not available for the most desirable text encoding. # enc2.6.*: Similar test for user functions. # enc2.7.*: Test that the VerifyCookie opcode protects against assuming the diff --git a/test/errmsg.test b/test/errmsg.test index 9f8409b..6b3f3b7 100644 --- a/test/errmsg.test +++ b/test/errmsg.test @@ -80,12 +80,14 @@ do_test 2.2 { SQLITE_ERROR {SQL logic error or missing database} SQLITE_CONSTRAINT {column b is not unique} }] +verify_ex_errcode 2.2b SQLITE_CONSTRAINT_UNIQUE do_test 2.3 { error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')" } [list {*}{ SQLITE_CONSTRAINT {column b is not unique} SQLITE_CONSTRAINT {column b is not unique} }] +verify_ex_errcode 2.3b SQLITE_CONSTRAINT_UNIQUE #------------------------------------------------------------------------- # Test SQLITE_SCHEMA errors. And, for _v2(), test that if the schema diff --git a/test/exclusive2.test b/test/exclusive2.test index 2208da5..54203e3 100644 --- a/test/exclusive2.test +++ b/test/exclusive2.test @@ -25,6 +25,14 @@ ifcapable {!pager_pragmas} { return } +# Tests in this file verify that locking_mode=exclusive causes SQLite to +# use cached pages even if the database is changed on disk. This doesn't +# work with mmap. +if {[permutation]=="mmap"} { + finish_test + return +} + # This module does not work right if the cache spills at unexpected # moments. So disable the soft-heap-limit. # diff --git a/test/filefmt.test b/test/filefmt.test index 1165cd6..bc1af18 100644 --- a/test/filefmt.test +++ b/test/filefmt.test @@ -213,4 +213,39 @@ do_execsql_test filefmt-3.3 { PRAGMA integrity_check; } {ok} +reset_db +do_execsql_test filefmt-4.1 { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x, y); + CREATE TABLE t2(x, y); + + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + INSERT INTO t1 VALUES(randomblob(100), randomblob(100)); + + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; + INSERT INTO t2 SELECT randomblob(100), randomblob(100) FROM t1; +} + +do_test filefmt-4.2 { + sql36231 { INSERT INTO t2 SELECT * FROM t1 } +} {} + +do_test filefmt-4.3 { + forcedelete bak.db + db backup bak.db +} {} + +do_test filefmt-4.4 { + sqlite3 db2 bak.db + db2 eval { PRAGMA integrity_check } +} {ok} +db2 close + finish_test + diff --git a/test/fkey2.test b/test/fkey2.test index f0cc4d2..3e5b27c 100644 --- a/test/fkey2.test +++ b/test/fkey2.test @@ -139,14 +139,21 @@ set FkeySimpleTests { 4.17 "UPDATE t7 SET a = 10" {0 {}} 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}} - 5.2 "INSERT INTO t10 VALUES(1, 3)" {1 {foreign key mismatch}} + 5.2 "INSERT INTO t10 VALUES(1, 3)" + {1 {foreign key mismatch - "t10" referencing "t9"}} } do_test fkey2-1.1.0 { execsql [string map {/D/ {}} $FkeySimpleSchema] } {} foreach {tn zSql res} $FkeySimpleTests { - do_test fkey2-1.1.$tn { catchsql $zSql } $res + do_test fkey2-1.1.$tn.1 { catchsql $zSql } $res + do_test fkey2-1.1.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.1.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.1.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.1.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.1.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.1.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } drop_all_tables @@ -155,6 +162,12 @@ do_test fkey2-1.2.0 { } {} foreach {tn zSql res} $FkeySimpleTests { do_test fkey2-1.2.$tn { catchsql $zSql } $res + do_test fkey2-1.2.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.2.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.2.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.2.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.2.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.2.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } drop_all_tables @@ -165,6 +178,12 @@ do_test fkey2-1.3.0 { foreach {tn zSql res} $FkeySimpleTests { if {$res == "0 {}"} { set res {0 1} } do_test fkey2-1.3.$tn { catchsql $zSql } $res + do_test fkey2-1.3.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {} + do_test fkey2-1.3.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {} + do_test fkey2-1.3.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {} + do_test fkey2-1.3.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {} + do_test fkey2-1.3.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {} + do_test fkey2-1.3.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {} } execsql { PRAGMA count_changes = 0 } drop_all_tables @@ -681,7 +700,7 @@ foreach zSql [list { do_test fkey2-10.1.[incr tn] { execsql $zSql catchsql { INSERT INTO c DEFAULT VALUES } - } {1 {foreign key mismatch}} + } {/1 {foreign key mismatch - "c" referencing "."}/} } # "rowid" cannot be used as part of a child or parent key definition @@ -709,7 +728,7 @@ do_test fkey2-10.2.1 { INSERT INTO t1(rowid, a, b) VALUES(1, 1, 1); INSERT INTO t2 VALUES(1, 1); } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "t2" referencing "t1"}} do_test fkey2-10.2.2 { drop_all_tables catchsql { @@ -1223,7 +1242,7 @@ do_test fkey-2.14.3.8 { CREATE TABLE cc(a, b, FOREIGN KEY(a, b) REFERENCES pp(x, z)); } catchsql { INSERT INTO cc VALUES(1, 2) } -} {1 {foreign key mismatch}} +} {1 {foreign key mismatch - "cc" referencing "pp"}} do_test fkey-2.14.3.9 { execsql { DROP TABLE cc } } {} @@ -1414,10 +1433,12 @@ do_test fkey2-17.1.2 { set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy] sqlite3_step $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.2b SQLITE_CONSTRAINT_FOREIGNKEY ifcapable autoreset { do_test fkey2-17.1.3 { sqlite3_step $STMT } {SQLITE_CONSTRAINT} + verify_ex_errcode fkey2-17.1.3b SQLITE_CONSTRAINT_FOREIGNKEY } else { do_test fkey2-17.1.3 { sqlite3_step $STMT @@ -1426,6 +1447,7 @@ ifcapable autoreset { do_test fkey2-17.1.4 { sqlite3_finalize $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.4b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-17.1.5 { execsql { INSERT INTO one VALUES(2, 3, 4); @@ -1469,9 +1491,11 @@ do_test fkey2-17.1.12 { do_test fkey2-17.1.13 { sqlite3_step $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.13b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-17.1.14 { sqlite3_finalize $STMT } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-17.1.14b SQLITE_CONSTRAINT_FOREIGNKEY drop_all_tables do_test fkey2-17.2.1 { @@ -1625,9 +1649,11 @@ do_test fkey2-19.2 { sqlite3_bind_int $S 1 2 sqlite3_step $S } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-19.2b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-19.3 { sqlite3_reset $S } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey2-19.3b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey2-19.4 { sqlite3_bind_int $S 1 1 sqlite3_step $S diff --git a/test/fkey4.test b/test/fkey4.test index d6dd2fc..79cf663 100644 --- a/test/fkey4.test +++ b/test/fkey4.test @@ -42,10 +42,12 @@ do_test fkey4-1.2 { set ::STMT1 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] sqlite3_step $::STMT1 } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey4-1.2b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey4-1.3 { set ::STMT2 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] sqlite3_step $::STMT2 } {SQLITE_CONSTRAINT} +verify_ex_errcode fkey4-1.3b SQLITE_CONSTRAINT_FOREIGNKEY do_test fkey4-1.4 { db eval {SELECT * FROM t2} } {1 3} diff --git a/test/fkey_malloc.test b/test/fkey_malloc.test index 4a36f5f..b4b5b4e 100644 --- a/test/fkey_malloc.test +++ b/test/fkey_malloc.test @@ -29,6 +29,7 @@ do_malloc_test fkey_malloc-1 -sqlprep { INSERT INTO t2 VALUES('aaa'); UPDATE t1 SET a = 'bbb'; DELETE FROM t1; + PRAGMA foreign_key_check; } do_malloc_test fkey_malloc-2 -sqlprep { @@ -128,5 +129,3 @@ do_malloc_test fkey_malloc-7 -sqlprep { } finish_test - - diff --git a/test/fts3ai.test b/test/fts3ai.test index 144b4c3..b17b5bd 100644 --- a/test/fts3ai.test +++ b/test/fts3ai.test @@ -19,6 +19,11 @@ ifcapable !fts3 { return } +ifcapable !utf16 { + finish_test + return +} + # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. # NOTE(shess) Copied from capi3.test. diff --git a/test/fts3aux1.test b/test/fts3aux1.test index adda586..ef17949 100644 --- a/test/fts3aux1.test +++ b/test/fts3aux1.test @@ -354,10 +354,10 @@ do_execsql_test 3.1.1 { do_catchsql_test 3.1.2 { CREATE VIRTUAL TABLE terms2 USING fts4aux; -} {1 {wrong number of arguments to fts4aux constructor}} +} {1 {invalid arguments to fts4aux constructor}} do_catchsql_test 3.1.3 { CREATE VIRTUAL TABLE terms2 USING fts4aux(t2, t2); -} {1 {wrong number of arguments to fts4aux constructor}} +} {1 {invalid arguments to fts4aux constructor}} do_execsql_test 3.2.1 { CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) @@ -444,7 +444,6 @@ do_plansql_test 4.5 { # odd name (one that requires quoting for use in SQL statements). And that # the argument to the fts4aux constructor is properly dequoted before use. # -# do_execsql_test 5.1 { CREATE VIRTUAL TABLE "abc '!' def" USING fts4(x, y); INSERT INTO "abc '!' def" VALUES('XX', 'YY'); @@ -458,5 +457,66 @@ do_execsql_test 5.2 { SELECT * FROM "%%^^%%"; } {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} +#------------------------------------------------------------------------- +# Test that we can create an fts4aux table in the temp database. +# +forcedelete test.db2 +do_execsql_test 6.1 { + CREATE VIRTUAL TABLE ft1 USING fts4(x, y); + INSERT INTO ft1 VALUES('a b', 'c d'); + INSERT INTO ft1 VALUES('e e', 'c d'); + INSERT INTO ft1 VALUES('a a', 'b b'); + CREATE VIRTUAL TABLE temp.aux1 USING fts4aux(main, ft1); + SELECT * FROM aux1; +} { + a * 2 3 a 0 2 3 + b * 2 3 b 0 1 1 b 1 1 2 + c * 2 2 c 1 2 2 + d * 2 2 d 1 2 2 + e * 1 2 e 0 1 2 +} + +do_execsql_test 6.2 { + ATTACH 'test.db2' AS att; + CREATE VIRTUAL TABLE att.ft1 USING fts4(x, y); + INSERT INTO att.ft1 VALUES('v w', 'x y'); + INSERT INTO att.ft1 VALUES('z z', 'x y'); + INSERT INTO att.ft1 VALUES('v v', 'w w'); + CREATE VIRTUAL TABLE temp.aux2 USING fts4aux(att, ft1); + SELECT * FROM aux2; +} { + v * 2 3 v 0 2 3 + w * 2 3 w 0 1 1 w 1 1 2 + x * 2 2 x 1 2 2 + y * 2 2 y 1 2 2 + z * 1 2 z 0 1 2 +} + +foreach {tn q res1 res2} { + 1 { SELECT * FROM %%% WHERE term = 'a' } {a * 2 3 a 0 2 3} {} + 2 { SELECT * FROM %%% WHERE term = 'x' } {} {x * 2 2 x 1 2 2} + + 3 { SELECT * FROM %%% WHERE term >= 'y' } + {} {y * 2 2 y 1 2 2 z * 1 2 z 0 1 2} + + 4 { SELECT * FROM %%% WHERE term <= 'c' } + {a * 2 3 a 0 2 3 b * 2 3 b 0 1 1 b 1 1 2 c * 2 2 c 1 2 2} {} +} { + set sql1 [string map {%%% aux1} $q] + set sql2 [string map {%%% aux2} $q] + + do_execsql_test 7.$tn.1 $sql1 $res1 + do_execsql_test 7.$tn.2 $sql2 $res2 +} + +do_test 8.1 { + catchsql { CREATE VIRTUAL TABLE att.aux3 USING fts4aux(main, ft1) } +} {1 {invalid arguments to fts4aux constructor}} + +do_test 8.2 { + execsql {DETACH att} + catchsql { SELECT * FROM aux2 } +} {1 {SQL logic error or missing database}} finish_test + diff --git a/test/fts3near.test b/test/fts3near.test index 9c4409e..9276fa3 100644 --- a/test/fts3near.test +++ b/test/fts3near.test @@ -580,5 +580,19 @@ do_test fts3near-6.5 { } } {3} +# Ticket 38b1ae018f. +# +do_execsql_test fts3near-7.1 { + CREATE VIRTUAL TABLE x USING fts4(y,z); + INSERT INTO x VALUES('aaa bbb ccc ddd', 'bbb ddd aaa ccc'); + SELECT * FROM x where y MATCH 'bbb NEAR/6 aaa'; +} {{aaa bbb ccc ddd} {bbb ddd aaa ccc}} + +do_execsql_test fts3near-7.2 { + CREATE VIRTUAL TABLE t2 USING fts4(a, b); + INSERT INTO t2 VALUES('A B C', 'A D E'); + SELECT * FROM t2 where t2 MATCH 'a:A NEAR E' +} {} + finish_test diff --git a/test/fts4content.test b/test/fts4content.test index 59c4199..302f14e 100644 --- a/test/fts4content.test +++ b/test/fts4content.test @@ -46,6 +46,8 @@ ifcapable !fts3 { # 8.* - Test that if the content=xxx and prefix options are used together, # the 'rebuild' command still works. # +# 9.* - Test using content=xxx where xxx is a virtual table. +# do_execsql_test 1.1.1 { CREATE TABLE t1(a, b, c); @@ -522,4 +524,103 @@ do_execsql_test 8.4 { SELECT rowid FROM ft10 WHERE a MATCH 'ab*'; } {1 2 3} do_execsql_test 8.5 { SELECT rowid FROM ft10 WHERE b MATCH 'abav*'; } {3} do_execsql_test 8.6 { SELECT rowid FROM ft10 WHERE ft10 MATCH 'abas*'; } {1} +#------------------------------------------------------------------------- +# Test cases 9.* +# +reset_db +register_echo_module [sqlite3_connection_pointer db] + +do_execsql_test 9.1 { + CREATE TABLE tbl1(a, b); + INSERT INTO tbl1 VALUES('a b', 'c d'); + INSERT INTO tbl1 VALUES('e f', 'a b'); + CREATE VIRTUAL TABLE e1 USING echo(tbl1); + CREATE VIRTUAL TABLE ft1 USING fts4(content=e1); + INSERT INTO ft1(ft1) VALUES('rebuild'); +} + +do_execsql_test 9.2 { + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'e' +} {2 {e f} {a b}} + +do_execsql_test 9.3 { + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a' +} {1 {a b} {c d} 2 {e f} {a b}} + +do_execsql_test 9.4 { + DELETE FROM ft1 WHERE docid=1; +} + +do_execsql_test 9.5 { + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a' +} {2 {e f} {a b}} + +do_execsql_test 9.6 { + INSERT INTO ft1(ft1) VALUES('rebuild'); + SELECT rowid, * FROM ft1 WHERE ft1 MATCH 'a' +} {1 {a b} {c d} 2 {e f} {a b}} + + +#------------------------------------------------------------------------- +# Test cases 10.* +# +reset_db +register_fs_module [sqlite3_connection_pointer db] + +proc write_file {path text} { + set fd [open $path w] + puts -nonewline $fd $text + close $fd +} + +write_file t1.txt {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} +write_file t2.txt {a b c d e f g h i j k l m a b c d e f g h i j k l m} +write_file t3.txt {n o p q r s t u v w x y z n o p q r s t u v w x y z} + +do_execsql_test 10.1 { + CREATE TABLE idx(id INTEGER PRIMARY KEY, path TEXT); + INSERT INTO idx VALUES (1, 't1.txt'); + INSERT INTO idx VALUES (2, 't2.txt'); + INSERT INTO idx VALUES (3, 't3.txt'); + + CREATE VIRTUAL TABLE vt USING fs(idx); + SELECT * FROM vt; +} { + 1 {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} + 2 {a b c d e f g h i j k l m a b c d e f g h i j k l m} + 3 {n o p q r s t u v w x y z n o p q r s t u v w x y z} +} + +do_execsql_test 10.2 { + SELECT * FROM vt WHERE rowid = 2; +} { + 2 {a b c d e f g h i j k l m a b c d e f g h i j k l m} +} + +do_execsql_test 10.3 { + CREATE VIRTUAL TABLE ft USING fts4(content=vt); + INSERT INTO ft(ft) VALUES('rebuild'); +} + +do_execsql_test 10.4 { + SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 'e' +} { + {...c d [e] f g...} {...c d [e] f g...} +} + +do_execsql_test 10.5 { + SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 't' +} { + {...r s [t] u v...} {...r s [t] u v...} +} + +do_execsql_test 10.6 { DELETE FROM ft WHERE docid=2 } + +do_execsql_test 10.7 { + SELECT snippet(ft, '[', ']', '...', -1, 5) FROM ft WHERE ft MATCH 'e' +} { + {...c d [e] f g...} +} + finish_test + diff --git a/test/fts4unicode.test b/test/fts4unicode.test index 0ac60a6..8bd83f6 100644 --- a/test/fts4unicode.test +++ b/test/fts4unicode.test @@ -44,12 +44,12 @@ proc do_unicode_token_test3 {tn args} { } do_unicode_token_test 1.0 {a B c D} {0 a a 1 b B 2 c c 3 d D} -do_unicode_token_test 1.1 {Ä Ö Ü} {0 ä Ä 1 ö Ö 2 ü Ü} -do_unicode_token_test 1.2 {xÄx xÖx xÜx} {0 xäx xÄx 1 xöx xÖx 2 xüx xÜx} +do_unicode_token_test 1.1 {Ä Ö Ãœ} {0 ä Ä 1 ö Ö 2 ü Ãœ} +do_unicode_token_test 1.2 {xÄx xÖx xÃœx} {0 xäx xÄx 1 xöx xÖx 2 xüx xÃœx} # 0x00DF is a small "sharp s". 0x1E9E is a capital sharp s. do_unicode_token_test 1.3 "\uDF" "0 \uDF \uDF" -do_unicode_token_test 1.4 "\u1E9E" "0 ß \u1E9E" +do_unicode_token_test 1.4 "\u1E9E" "0 ß \u1E9E" do_unicode_token_test 1.5 "\u1E9E" "0 \uDF \u1E9E" do_unicode_token_test 1.6 "The quick brown fox" { @@ -60,12 +60,15 @@ do_unicode_token_test 1.7 "The\u00bfquick\u224ebrown\u2263fox" { } do_unicode_token_test2 1.8 {a B c D} {0 a a 1 b B 2 c c 3 d D} -do_unicode_token_test2 1.9 {Ä Ö Ü} {0 a Ä 1 o Ö 2 u Ü} -do_unicode_token_test2 1.10 {xÄx xÖx xÜx} {0 xax xÄx 1 xox xÖx 2 xux xÜx} +do_unicode_token_test2 1.9 {Ä Ö Ãœ} {0 a Ä 1 o Ö 2 u Ãœ} +do_unicode_token_test2 1.10 {xÄx xÖx xÃœx} {0 xax xÄx 1 xox xÖx 2 xux xÃœx} # Check that diacritics are removed if remove_diacritics=1 is specified. # And that they do not break tokens. -do_unicode_token_test2 1.10 "xx\u0301xx" "0 xxxx xx\u301xx" +do_unicode_token_test2 1.11 "xx\u0301xx" "0 xxxx xx\u301xx" + +# Title-case mappings work +do_unicode_token_test 1.12 "\u01c5" "0 \u01c6 \u01c5" #------------------------------------------------------------------------- # @@ -383,5 +386,3 @@ foreach T $tokenizers { finish_test - - diff --git a/test/func.test b/test/func.test index e44c44b..4ab7688 100644 --- a/test/func.test +++ b/test/func.test @@ -1273,11 +1273,13 @@ do_test func-29.3 { sqlite3_db_status db CACHE_MISS 1 db eval {SELECT typeof(+x) FROM t29 ORDER BY id} } {integer null real blob text} -do_test func-29.4 { - set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] - if {$x>100} {set x many} - set x -} {many} +if {[permutation] != "mmap"} { + do_test func-29.4 { + set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1] + if {$x>100} {set x many} + set x + } {many} +} do_test func-29.5 { db close sqlite3 db test.db @@ -1289,6 +1291,21 @@ do_test func-29.6 { if {$x<5} {set x 1} set x } {1} - + +do_execsql_test func-30.1 {SELECT unicode('$');} 36 +do_execsql_test func-30.2 [subst {SELECT unicode('\u00A2');}] 162 +do_execsql_test func-30.3 [subst {SELECT unicode('\u20AC');}] 8364 +do_execsql_test func-30.4 {SELECT char(36,162,8364);} [subst {$\u00A2\u20AC}] + +for {set i 1} {$i<0xd800} {incr i 13} { + do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i +} +for {set i 57344} {$i<=0xfffd} {incr i 17} { + if {$i==0xfeff} continue + do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i +} +for {set i 65536} {$i<=0x10ffff} {incr i 139} { + do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i +} finish_test diff --git a/test/fuzzer1.test b/test/fuzzer1.test index 827d6a0..473d0e1 100644 --- a/test/fuzzer1.test +++ b/test/fuzzer1.test @@ -24,12 +24,7 @@ ifcapable !vtab { set ::testprefix fuzzer1 -# Test of test code. Only here to make the coverage metric better. -do_test 0.1 { - list [catch { register_fuzzer_module a b c } msg] $msg -} {1 {wrong # args: should be "register_fuzzer_module DB"}} - -register_fuzzer_module db +load_static_extension db fuzzer # Check configuration errors. # diff --git a/test/fuzzerfault.test b/test/fuzzerfault.test index 067da7f..6449612 100644 --- a/test/fuzzerfault.test +++ b/test/fuzzerfault.test @@ -17,7 +17,7 @@ source $testdir/tester.tcl ifcapable !vtab { finish_test ; return } set ::testprefix fuzzerfault -register_fuzzer_module db +load_static_extension db fuzzer do_test 1-pre1 { execsql { @@ -30,7 +30,7 @@ do_test 1-pre1 { } {} do_faultsim_test 1 -prep { faultsim_restore_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer } -body { execsql { CREATE VIRTUAL TABLE x1 USING fuzzer(x1_rules); @@ -43,7 +43,7 @@ do_faultsim_test 1 -prep { do_test 2-pre1 { faultsim_delete_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer execsql { CREATE TABLE x2_rules(ruleset, cFrom, cTo, cost); INSERT INTO x2_rules VALUES(0, 'a', 'x', 1); @@ -56,7 +56,7 @@ do_test 2-pre1 { do_faultsim_test 2 -prep { faultsim_restore_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer } -body { execsql { SELECT count(*) FROM x2 WHERE word MATCH 'abc'; @@ -78,7 +78,7 @@ do_test 3-pre1 { do_faultsim_test 3 -prep { faultsim_restore_and_reopen - register_fuzzer_module db + load_static_extension db fuzzer } -body { execsql { CREATE VIRTUAL TABLE x1 USING fuzzer(x1_rules); diff --git a/test/hook.test b/test/hook.test index a195275..6346cc7 100644 --- a/test/hook.test +++ b/test/hook.test @@ -74,6 +74,7 @@ do_test hook-3.6 { INSERT INTO t2 VALUES(6,7); } } {1 {constraint failed}} +verify_ex_errcode hook-3.6b SQLITE_CONSTRAINT_COMMITHOOK do_test hook-3.7 { set ::commit_cnt } {1 2 2 3 3 4 4 5 5 6 6 7} diff --git a/test/incrblob.test b/test/incrblob.test index 7cc99dd..4277e5c 100644 --- a/test/incrblob.test +++ b/test/incrblob.test @@ -123,6 +123,7 @@ foreach AutoVacuumMode [list 0 1] { forcedelete test.db test.db-journal sqlite3 db test.db + execsql "PRAGMA mmap_size = 0" execsql "PRAGMA auto_vacuum = $AutoVacuumMode" do_test incrblob-2.$AutoVacuumMode.1 { @@ -149,6 +150,7 @@ foreach AutoVacuumMode [list 0 1] { # Open and close the db to make sure the page cache is empty. db close sqlite3 db test.db + execsql "PRAGMA mmap_size = 0" # Read the last 20 bytes of the blob via a blob handle. set ::blob [db incrblob blobs v 1] @@ -171,6 +173,7 @@ foreach AutoVacuumMode [list 0 1] { # Open and close the db to make sure the page cache is empty. db close sqlite3 db test.db + execsql "PRAGMA mmap_size = 0" # Write the second-to-last 20 bytes of the blob via a blob handle. # @@ -200,6 +203,7 @@ foreach AutoVacuumMode [list 0 1] { # Open and close the db to make sure the page cache is empty. db close sqlite3 db test.db + execsql { PRAGMA mmap_size = 0 } execsql { SELECT i FROM blobs } } {45} @@ -505,7 +509,7 @@ if {[permutation] != "memsubsys1"} { sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) #----------------------------------------------------------------------- -# The following tests verify the behaviour of the incremental IO +# The following tests verify the behavior of the incremental IO # APIs in the following cases: # # 7.1 A row that containing an open blob is modified. @@ -516,7 +520,7 @@ sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) # 7.3 An INCREMENTAL VACUUM moves an overflow page that is part # of an open blob. # -# In the first case above, correct behaviour is for all subsequent +# In the first case above, correct behavior is for all subsequent # read/write operations on the blob-handle to return SQLITE_ABORT. # More accurately, blob-handles are invalidated whenever the table # they belong to is written to. diff --git a/test/incrvacuum_ioerr.test b/test/incrvacuum_ioerr.test index 946925d..50f7fa2 100644 --- a/test/incrvacuum_ioerr.test +++ b/test/incrvacuum_ioerr.test @@ -139,8 +139,9 @@ ifcapable shared_cache { # Figure out how big the database is and how many free pages it # has before running incremental-vacuum. # - set nPage [expr {[file size test.db]/1024}] set nFree [execsql {pragma freelist_count} db1] + set nPage [execsql {pragma page_count} db1] + puts "nFree=$nFree nPage=$nPage" # Now run incremental-vacuum to vacuum 5 pages from the db file. # The iTest'th I/O call is set to fail. @@ -158,11 +159,11 @@ ifcapable shared_cache { set ::sqlite_io_error_hardhit 0 set nFree2 [execsql {pragma freelist_count} db1] - set nPage2 [expr {[file size test.db]/1024}] + set nPage2 [execsql {pragma page_count} db1] do_test incrvacuum-ioerr-4.$iTest.2 { set shrink [expr {$nPage-$nPage2}] - expr {$shrink==0 || $shrink==5} + expr {$shrink==0 || $shrink==5 || ($nFree<5 && $shrink==$nFree)} } {1} do_test incrvacuum-ioerr-4.$iTest.3 { diff --git a/test/index5.test b/test/index5.test index c8e94b3..7895391 100644 --- a/test/index5.test +++ b/test/index5.test @@ -36,11 +36,10 @@ db close testvfs tvfs tvfs filter xWrite tvfs script write_cb -proc write_cb {xCall file handle iOfst} { +proc write_cb {xCall file handle iOfst args} { if {[file tail $file]=="test.db"} { lappend ::write_list [expr $iOfst/1024] } - puts "$xCall $file $args" } do_test 1.2 { @@ -65,11 +64,12 @@ do_test 1.3 { } set iPrev $iNext } + puts -nonewline \ + " (forward=$nForward, back=$nBackward, noncontiguous=$nNoncont)" - expr {$nForward > $nBackward} + expr {$nForward > 2*($nBackward + $nNoncont)} } {1} db close tvfs delete finish_test - diff --git a/test/io.test b/test/io.test index 9363b0c..11f9cc8 100644 --- a/test/io.test +++ b/test/io.test @@ -16,6 +16,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix io db close sqlite3_simulate_device @@ -38,6 +39,10 @@ sqlite3 db test.db -vfs devsym # # io-5.* - Test that the default page size is selected and used # correctly. +# +# io-6.* - Test that the pager-cache is not being flushed unnecessarily +# after a transaction that uses the special atomic-write path +# is committed. # set ::nWrite 0 @@ -207,7 +212,7 @@ do_test io-2.5.3 { # Changed 2010-03-27: The size of the database is now stored in # bytes 28..31 and so when a page is added to the database, page 1 # is immediately modified and the journal file immediately comes into -# existance. To fix this test, the BEGIN is changed into a a +# existence. To fix this test, the BEGIN is changed into a a # BEGIN IMMEDIATE and the INSERT is omitted. # do_test io-2.6.1 { @@ -565,5 +570,75 @@ foreach {char sectorsize pgsize} { } $pgsize } +#---------------------------------------------------------------------- +# +do_test io-6.1 { + db close + sqlite3_simulate_device -char atomic + forcedelete test.db + sqlite3 db test.db -vfs devsym + execsql { + PRAGMA mmap_size = 0; + PRAGMA page_size = 1024; + PRAGMA cache_size = 2000; + CREATE TABLE t1(x); + CREATE TABLE t2(x); + CREATE TABLE t3(x); + CREATE INDEX i3 ON t3(x); + INSERT INTO t3 VALUES(randomblob(100)); + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + INSERT INTO t3 SELECT randomblob(100) FROM t3; + } + + db_save_and_close +} {} + +foreach {tn sql} { + 1 { BEGIN; + INSERT INTO t1 VALUES('123'); + INSERT INTO t2 VALUES('456'); + COMMIT; + } + 2 { BEGIN; + INSERT INTO t1 VALUES('123'); + COMMIT; + } +} { + + # These tests don't work with memsubsys1, as it causes the effective page + # cache size to become too small to hold the entire db in memory. + if {[permutation] == "memsubsys1"} continue + + db_restore + sqlite3 db test.db -vfs devsym + execsql { + PRAGMA cache_size = 2000; + PRAGMA mmap_size = 0; + SELECT x FROM t3 ORDER BY rowid; + SELECT x FROM t3 ORDER BY x; + } + do_execsql_test 6.2.$tn.1 { PRAGMA integrity_check } {ok} + do_execsql_test 6.2.$tn.2 $sql + + # Corrupt the database file on disk. This should not matter for the + # purposes of the following "PRAGMA integrity_check", as the entire + # database should be cached in the pager-cache. If corruption is + # reported, it indicates that executing $sql caused the pager cache + # to be flushed. Which is a bug. + hexio_write test.db [expr 1024 * 5] [string repeat 00 2048] + do_execsql_test 6.2.$tn.3 { PRAGMA integrity_check } {ok} + db close +} + sqlite3_simulate_device -char {} -sectorsize 0 finish_test + diff --git a/test/limit.test b/test/limit.test index e5aac70..c5b75c2 100644 --- a/test/limit.test +++ b/test/limit.test @@ -468,5 +468,152 @@ do_test limit-12.4 { } } {1 {no such column: x}} +# Ticket [db4d96798da8b] +# LIMIT does not work with nested views containing UNION ALL +# +do_test limit-13.1 { + db eval { + CREATE TABLE t13(x); + INSERT INTO t13 VALUES(1),(2); + CREATE VIEW v13a AS SELECT x AS y FROM t13; + CREATE VIEW v13b AS SELECT y AS z FROM v13a UNION ALL SELECT y+10 FROM v13a; + CREATE VIEW v13c AS SELECT z FROM v13b UNION ALL SELECT z+20 FROM v13b; + } +} {} +do_test limit-13.2 { + db eval {SELECT z FROM v13c LIMIT 1} +} {1} +do_test limit-13.3 { + db eval {SELECT z FROM v13c LIMIT 2} +} {1 2} +do_test limit-13.4 { + db eval {SELECT z FROM v13c LIMIT 3} +} {1 2 11} +do_test limit-13.5 { + db eval {SELECT z FROM v13c LIMIT 4} +} {1 2 11 12} +do_test limit-13.6 { + db eval {SELECT z FROM v13c LIMIT 5} +} {1 2 11 12 21} +do_test limit-13.7 { + db eval {SELECT z FROM v13c LIMIT 6} +} {1 2 11 12 21 22} +do_test limit-13.8 { + db eval {SELECT z FROM v13c LIMIT 7} +} {1 2 11 12 21 22 31} +do_test limit-13.9 { + db eval {SELECT z FROM v13c LIMIT 8} +} {1 2 11 12 21 22 31 32} +do_test limit-13.10 { + db eval {SELECT z FROM v13c LIMIT 9} +} {1 2 11 12 21 22 31 32} +do_test limit-13.11 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 1} +} {2} +do_test limit-13.12 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 1} +} {2 11} +do_test limit-13.13 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 1} +} {2 11 12} +do_test limit-13.14 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 1} +} {2 11 12 21} +do_test limit-13.15 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 1} +} {2 11 12 21 22} +do_test limit-13.16 { + db eval {SELECT z FROM v13c LIMIT 6 OFFSET 1} +} {2 11 12 21 22 31} +do_test limit-13.17 { + db eval {SELECT z FROM v13c LIMIT 7 OFFSET 1} +} {2 11 12 21 22 31 32} +do_test limit-13.18 { + db eval {SELECT z FROM v13c LIMIT 8 OFFSET 1} +} {2 11 12 21 22 31 32} +do_test limit-13.21 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 2} +} {11} +do_test limit-13.22 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 2} +} {11 12} +do_test limit-13.23 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 2} +} {11 12 21} +do_test limit-13.24 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 2} +} {11 12 21 22} +do_test limit-13.25 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 2} +} {11 12 21 22 31} +do_test limit-13.26 { + db eval {SELECT z FROM v13c LIMIT 6 OFFSET 2} +} {11 12 21 22 31 32} +do_test limit-13.27 { + db eval {SELECT z FROM v13c LIMIT 7 OFFSET 2} +} {11 12 21 22 31 32} +do_test limit-13.31 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 3} +} {12} +do_test limit-13.32 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 3} +} {12 21} +do_test limit-13.33 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 3} +} {12 21 22} +do_test limit-13.34 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 3} +} {12 21 22 31} +do_test limit-13.35 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 3} +} {12 21 22 31 32} +do_test limit-13.36 { + db eval {SELECT z FROM v13c LIMIT 6 OFFSET 3} +} {12 21 22 31 32} +do_test limit-13.41 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 4} +} {21} +do_test limit-13.42 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 4} +} {21 22} +do_test limit-13.43 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 4} +} {21 22 31} +do_test limit-13.44 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 4} +} {21 22 31 32} +do_test limit-13.45 { + db eval {SELECT z FROM v13c LIMIT 5 OFFSET 4} +} {21 22 31 32} +do_test limit-13.51 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 5} +} {22} +do_test limit-13.52 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 5} +} {22 31} +do_test limit-13.53 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 5} +} {22 31 32} +do_test limit-13.54 { + db eval {SELECT z FROM v13c LIMIT 4 OFFSET 5} +} {22 31 32} +do_test limit-13.61 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 6} +} {31} +do_test limit-13.62 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 6} +} {31 32} +do_test limit-13.63 { + db eval {SELECT z FROM v13c LIMIT 3 OFFSET 6} +} {31 32} +do_test limit-13.71 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 7} +} {32} +do_test limit-13.72 { + db eval {SELECT z FROM v13c LIMIT 2 OFFSET 7} +} {32} +do_test limit-13.81 { + db eval {SELECT z FROM v13c LIMIT 1 OFFSET 8} +} {} finish_test diff --git a/test/loadext.test b/test/loadext.test index 72eff12..0d5b841 100644 --- a/test/loadext.test +++ b/test/loadext.test @@ -139,20 +139,22 @@ do_test loadext-2.1 { sqlite3_load_extension db "${testextension}xx" } msg] list $rc $msg -} [list 1 [format $dlerror_nosuchfile ${testextension}xx]] +} /[list 1 [format $dlerror_nosuchfile ${testextension}xx.*]]/ # Try to load an extension for which the file is not a shared object # do_test loadext-2.2 { - set fd [open "${testextension}xx" w] + set fd [open "./notasharedlib.so" w] + puts $fd blah + close $fd + set fd [open "./notasharedlib.dll" w] puts $fd blah close $fd set rc [catch { - sqlite3_load_extension db "${testextension}xx" + sqlite3_load_extension db "./notasharedlib" } msg] - set expected_error_pattern [format $dlerror_notadll ${testextension}xx] - list $rc [string match $expected_error_pattern $msg] -} [list 1 1] + list $rc $msg +} /[list 1 [format $dlerror_notadll ./notasharedlib.*]]/ # Try to load an extension for which the file is present but the # entry point is not. @@ -196,7 +198,7 @@ do_test loadext-3.2 { regsub {0x[1234567890abcdefABCDEF]*} $res XXX res } set res -} [list 1 [format $dlerror_nosymbol $testextension sqlite3_extension_init]] +} /[list 1 [format $dlerror_nosymbol $testextension sqlite3_.*_init]]/ do_test loadext-3.3 { catchsql { SELECT load_extension($::testextension,'testloadext_init') diff --git a/test/malloc.test b/test/malloc.test index 0d213d7..5d03aa8 100644 --- a/test/malloc.test +++ b/test/malloc.test @@ -842,7 +842,7 @@ do_malloc_test 36 -sqlprep { SELECT test_agg_errmsg16(), group_concat(a) FROM t1 } -# At one point, if an OOM occured immediately after obtaining a shared lock +# At one point, if an OOM occurred immediately after obtaining a shared lock # on the database file, the file remained locked. This test case ensures # that bug has been fixed.i if {[db eval {PRAGMA locking_mode}]!="exclusive"} { diff --git a/test/malloc3.test b/test/malloc3.test index 04f7d32..f4a6c3b 100644 --- a/test/malloc3.test +++ b/test/malloc3.test @@ -595,8 +595,8 @@ proc run_test {arglist iRepeat {pcstart 0} {iFailStart 1}} { if {$rc != 0 && $nac && !$ac} { # Before [db eval] the auto-commit flag was clear. Now it - # is set. Since an error occured we assume this was not a - # commit - therefore a rollback occured. Check that the + # is set. Since an error occurred we assume this was not a + # commit - therefore a rollback occurred. Check that the # rollback-hook was invoked. do_test malloc3-rollback_hook_count.$iterid { set ::rollback_hook_count diff --git a/test/mallocG.test b/test/mallocG.test index eab533b..4f2395d 100644 --- a/test/mallocG.test +++ b/test/mallocG.test @@ -53,6 +53,11 @@ do_malloc_test mallocG-3 -sqlprep { AND x BETWEEN 'i' AND 'm' } +ifcapable !utf16 { + finish_test + return +} + proc utf16 {utf8} { set utf16 [encoding convertto unicode $utf8] append utf16 "\x00\x00" diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl index 5937b95..2ac619b 100644 --- a/test/malloc_common.tcl +++ b/test/malloc_common.tcl @@ -264,7 +264,7 @@ proc faultsim_test_result_int {args} { set t [list $testrc $testresult] set r $args if { ($testnfail==0 && $t != [lindex $r 0]) || [lsearch $r $t]<0 } { - error "nfail=$testnfail rc=$testrc result=$testresult" + error "nfail=$testnfail rc=$testrc result=$testresult list=$r" } } diff --git a/test/memdb.test b/test/memdb.test index 1da3d7c..802fb1a 100644 --- a/test/memdb.test +++ b/test/memdb.test @@ -365,7 +365,7 @@ do_test memdb-6.15 { ifcapable subquery&&vtab { do_test memdb-7.1 { - register_wholenumber_module db + load_static_extension db wholenumber execsql { CREATE TABLE t6(x); CREATE VIRTUAL TABLE nums USING wholenumber; diff --git a/test/minmax.test b/test/minmax.test index 3621375..fb9bbb3 100644 --- a/test/minmax.test +++ b/test/minmax.test @@ -17,6 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl +set ::testprefix minmax do_test minmax-1.0 { execsql { @@ -536,7 +537,96 @@ do_test minmax-12.17 { } } {5} +#------------------------------------------------------------------------- +reset_db +proc do_test_13 {op name sql1 sql2 res} { + set ::sqlite_search_count 0 + uplevel [list do_execsql_test $name.1 $sql1 $res] + set a $::sqlite_search_count + + set ::sqlite_search_count 0 + uplevel [list do_execsql_test $name.2 $sql2 $res] + set b $::sqlite_search_count + + uplevel [list do_test $name.3 [list expr "$a $op $b"] 1] +} + +# Run a test named $name. Check that SQL statements $sql1 and $sql2 both +# return the same result, but that $sql2 increments the $sqlite_search_count +# variable more often (indicating that it is visiting more rows to determine +# the result). +# +proc do_test_13_opt {name sql1 sql2 res} { + uplevel [list do_test_13 < $name $sql1 $sql2 $res] +} + +# Like [do_test_13_noopt], except this time check that the $sqlite_search_count +# variable is incremented the same number of times by both SQL statements. +# +proc do_test_13_noopt {name sql1 sql2 res} { + uplevel [list do_test_13 == $name $sql1 $sql2 $res] +} + +do_execsql_test 13.1 { + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES('a', 1, 1); + INSERT INTO t1 VALUES('b', 6, 6); + INSERT INTO t1 VALUES('c', 5, 5); + INSERT INTO t1 VALUES('a', 4, 4); + INSERT INTO t1 VALUES('a', 5, 5); + INSERT INTO t1 VALUES('c', 6, 6); + INSERT INTO t1 VALUES('b', 4, 4); + INSERT INTO t1 VALUES('c', 7, 7); + INSERT INTO t1 VALUES('b', 2, 2); + INSERT INTO t1 VALUES('b', 3, 3); + INSERT INTO t1 VALUES('a', 3, 3); + INSERT INTO t1 VALUES('b', 5, 5); + INSERT INTO t1 VALUES('c', 4, 4); + INSERT INTO t1 VALUES('c', 3, 3); + INSERT INTO t1 VALUES('a', 2, 2); + SELECT * FROM t1 ORDER BY a, b, c; +} {a 1 1 a 2 2 a 3 3 a 4 4 a 5 5 + b 2 2 b 3 3 b 4 4 b 5 5 b 6 6 + c 3 3 c 4 4 c 5 5 c 6 6 c 7 7 +} +do_execsql_test 13.2 { CREATE INDEX i1 ON t1(a, b, c) } + +do_test_13_opt 13.3 { + SELECT min(b) FROM t1 WHERE a='b' +} { + SELECT min(c) FROM t1 WHERE a='b' +} {2} + +do_test_13_opt 13.4 { + SELECT a, min(b) FROM t1 WHERE a='b' +} { + SELECT a, min(c) FROM t1 WHERE a='b' +} {b 2} + +do_test_13_opt 13.4 { + SELECT a||c, max(b)+4 FROM t1 WHERE a='c' +} { + SELECT a||c, max(c)+4 FROM t1 WHERE a='c' +} {c7 11} + +do_test_13_noopt 13.5 { + SELECT a||c, max(b+1) FROM t1 WHERE a='c' +} { + SELECT a||c, max(c+1) FROM t1 WHERE a='c' +} {c7 8} + +do_test_13_noopt 13.6 { + SELECT count(b) FROM t1 WHERE a='c' +} { + SELECT count(c) FROM t1 WHERE a='c' +} {5} + +do_test_13_noopt 13.7 { + SELECT min(b), count(b) FROM t1 WHERE a='a'; +} { + SELECT min(c), count(c) FROM t1 WHERE a='a'; +} {1 5} finish_test diff --git a/test/misc7.test b/test/misc7.test index 4868c12..72c1cd6 100644 --- a/test/misc7.test +++ b/test/misc7.test @@ -488,6 +488,34 @@ do_test misc7-21.1 { list $rc $msg } {1 {unable to open database file}} +# Try to do hot-journal rollback with a read-only connection. The +# error code should be SQLITE_READONLY_ROLLBACK. +# +do_test misc7-22.1 { + db close + forcedelete test.db copy.db-journal + sqlite3 db test.db + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + } + db close + sqlite3 db test.db -readonly 1 + catchsql { + INSERT INTO t1 VALUES(5, 6); + } +} {1 {attempt to write a readonly database}} +do_test misc7-22.2 { execsql { SELECT * FROM t1 } } {1 2 3 4} +do_test misc7-22.3 { + set fd [open test.db-journal w] + puts $fd [string repeat abc 1000] + close $fd + catchsql { SELECT * FROM t1 } +} {1 {attempt to write a readonly database}} +do_test misc7-22.4 { + sqlite3_extended_errcode db +} SQLITE_READONLY_ROLLBACK db close forcedelete test.db diff --git a/test/notify2.test b/test/notify2.test index 4016b6d..9e40ed6 100644 --- a/test/notify2.test +++ b/test/notify2.test @@ -150,9 +150,9 @@ set sql $zSql # Hit some other kind of error. This is a malfunction. error $msg } else { - # No error occured. Check that any SELECT statements in the transaction + # No error occurred. Check that any SELECT statements in the transaction # returned "1". Otherwise, the invariant was false, indicating that - # some malfunction has occured. + # some malfunction has occurred. foreach r $msg { if {$r != 1} { puts "Invariant check failed: $msg" } } } } diff --git a/test/notnull.test b/test/notnull.test index 240aaba..01738a4 100644 --- a/test/notnull.test +++ b/test/notnull.test @@ -48,6 +48,7 @@ do_test notnull-1.2 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-1.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.3 { catchsql { DELETE FROM t1; @@ -62,6 +63,7 @@ do_test notnull-1.4 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-1.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.5 { catchsql { DELETE FROM t1; @@ -69,6 +71,7 @@ do_test notnull-1.5 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-1.5b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.6 { catchsql { DELETE FROM t1; @@ -104,6 +107,7 @@ do_test notnull-1.10 { SELECT * FROM t1 order by a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-1.10b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.11 { catchsql { DELETE FROM t1; @@ -146,6 +150,7 @@ do_test notnull-1.16 { SELECT * FROM t1 order by a; } } {1 {t1.c may not be NULL}} +verify_ex_errcode notnull-1.16b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.17 { catchsql { DELETE FROM t1; @@ -153,6 +158,7 @@ do_test notnull-1.17 { SELECT * FROM t1 order by a; } } {1 {t1.d may not be NULL}} +verify_ex_errcode notnull-1.17b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.18 { catchsql { DELETE FROM t1; @@ -174,6 +180,7 @@ do_test notnull-1.20 { SELECT * FROM t1 order by a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-1.20b SQLITE_CONSTRAINT_NOTNULL do_test notnull-1.21 { catchsql { DELETE FROM t1; @@ -190,6 +197,7 @@ do_test notnull-2.1 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-2.1b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.2 { catchsql { DELETE FROM t1; @@ -198,6 +206,7 @@ do_test notnull-2.2 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-2.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.3 { catchsql { DELETE FROM t1; @@ -214,6 +223,7 @@ do_test notnull-2.4 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-2.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.5 { catchsql { DELETE FROM t1; @@ -222,6 +232,7 @@ do_test notnull-2.5 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-2.6b SQLITE_CONSTRAINT_NOTNULL do_test notnull-2.6 { catchsql { DELETE FROM t1; @@ -262,6 +273,7 @@ do_test notnull-2.10 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-2.10b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.0 { execsql { @@ -287,6 +299,7 @@ do_test notnull-3.2 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-3.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.3 { catchsql { DELETE FROM t1; @@ -301,6 +314,7 @@ do_test notnull-3.4 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-3.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.5 { catchsql { DELETE FROM t1; @@ -308,6 +322,7 @@ do_test notnull-3.5 { SELECT * FROM t1 order by a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-3.5b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.6 { catchsql { DELETE FROM t1; @@ -343,6 +358,7 @@ do_test notnull-3.10 { SELECT * FROM t1 order by a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-3.10b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.11 { catchsql { DELETE FROM t1; @@ -385,6 +401,7 @@ do_test notnull-3.16 { SELECT * FROM t1 order by a; } } {1 {t1.c may not be NULL}} +verify_ex_errcode notnull-3.16b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.17 { catchsql { DELETE FROM t1; @@ -392,6 +409,7 @@ do_test notnull-3.17 { SELECT * FROM t1 order by a; } } {1 {t1.d may not be NULL}} +verify_ex_errcode notnull-3.17b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.18 { catchsql { DELETE FROM t1; @@ -413,6 +431,7 @@ do_test notnull-3.20 { SELECT * FROM t1 order by a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-3.20b SQLITE_CONSTRAINT_NOTNULL do_test notnull-3.21 { catchsql { DELETE FROM t1; @@ -429,6 +448,7 @@ do_test notnull-4.1 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-4.1b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.2 { catchsql { DELETE FROM t1; @@ -437,6 +457,7 @@ do_test notnull-4.2 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-4.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.3 { catchsql { DELETE FROM t1; @@ -453,6 +474,7 @@ do_test notnull-4.4 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.a may not be NULL}} +verify_ex_errcode notnull-4.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.5 { catchsql { DELETE FROM t1; @@ -461,6 +483,7 @@ do_test notnull-4.5 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-4.5b SQLITE_CONSTRAINT_NOTNULL do_test notnull-4.6 { catchsql { DELETE FROM t1; @@ -501,6 +524,7 @@ do_test notnull-4.10 { SELECT * FROM t1 ORDER BY a; } } {1 {t1.e may not be NULL}} +verify_ex_errcode notnull-4.10b SQLITE_CONSTRAINT_NOTNULL # Test that bug 29ab7be99f is fixed. # @@ -519,6 +543,7 @@ do_test notnull-5.2 { INSERT INTO t1 SELECT * FROM t2; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-5.2b SQLITE_CONSTRAINT_NOTNULL do_test notnull-5.3 { execsql { SELECT * FROM t1 } } {1 2} @@ -531,9 +556,9 @@ do_test notnull-5.4 { COMMIT; } } {1 {t1.b may not be NULL}} +verify_ex_errcode notnull-5.4b SQLITE_CONSTRAINT_NOTNULL do_test notnull-5.5 { execsql { SELECT * FROM t1 } } {1 2} finish_test - diff --git a/test/pager1.test b/test/pager1.test index 6849e55..72e4780 100644 --- a/test/pager1.test +++ b/test/pager1.test @@ -15,6 +15,7 @@ source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl +set testprefix pager1 # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). @@ -752,7 +753,7 @@ sqlite3 db test.db -readonly 1 do_catchsql_test pager1.4.5.6 { SELECT * FROM t1; SELECT * FROM t2; -} {1 {disk I/O error}} +} {1 {attempt to write a readonly database}} db close # Snapshot the file-system just before multi-file commit. Save the name @@ -883,12 +884,20 @@ do_execsql_test pager1.4.7.1 { tv filter {} db close tv delete +catch { + test_syscall install fchmod + test_syscall fault 1 1 +} do_test pager1.4.7.2 { faultsim_restore_and_reopen catch {file attributes test.db-journal -permissions r--------} catch {file attributes test.db-journal -readonly 1} catchsql { SELECT * FROM t1 } } {1 {unable to open database file}} +catch { + test_syscall reset + test_syscall fault 0 0 +} do_test pager1.4.7.3 { db close catch {file attributes test.db-journal -permissions rw-rw-rw-} @@ -1374,6 +1383,7 @@ db2 close # testvfs tv -default 1 foreach sectorsize { + 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 } { @@ -1396,7 +1406,7 @@ foreach sectorsize { COMMIT; } file size test.db-journal - } [expr $sectorsize > 65536 ? 65536 : $sectorsize] + } [expr $sectorsize > 65536 ? 65536 : ($sectorsize<32 ? 512 : $sectorsize)] do_test pager1-10.$sectorsize.2 { execsql { @@ -2235,7 +2245,6 @@ do_test pager1-25-1 { } db close } {} -breakpoint do_test pager1-25-2 { faultsim_delete_and_reopen execsql { @@ -2515,4 +2524,295 @@ if {$::tcl_platform(platform)=="unix"} { } {one two} } +#------------------------------------------------------------------------- +# Test that appending pages to the database file then moving those pages +# to the free-list before the transaction is committed does not cause +# an error. +# +foreach {tn pragma strsize} { + 1 { PRAGMA mmap_size = 0 } 2400 + 2 { } 2400 + 3 { PRAGMA mmap_size = 0 } 4400 + 4 { } 4400 +} { + reset_db + db func a_string a_string + db eval $pragma + do_execsql_test 34.$tn.1 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + do_execsql_test 34.$tn.2 { + BEGIN; + INSERT INTO t1 VALUES(2, a_string($strsize)); + DELETE FROM t1 WHERE oid=2; + COMMIT; + PRAGMA integrity_check; + } {ok} +} + +#------------------------------------------------------------------------- +# +reset_db +do_test 35 { + sqlite3 db test.db + + execsql { + CREATE TABLE t1(x, y); + PRAGMA journal_mode = WAL; + INSERT INTO t1 VALUES(1, 2); + } + + execsql { + BEGIN; + CREATE TABLE t2(a, b); + } + + hexio_write test.db-shm [expr 16*1024] [string repeat 0055 8192] + catchsql ROLLBACK +} {0 {}} + +do_multiclient_test tn { + sql1 { + PRAGMA auto_vacuum = 0; + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + } + + do_test 36.$tn.1 { + sql2 { PRAGMA max_page_count = 2 } + list [catch { sql2 { CREATE TABLE t2(x) } } msg] $msg + } {1 {database or disk is full}} + + sql1 { PRAGMA checkpoint_fullfsync = 1 } + sql1 { CREATE TABLE t2(x) } + + do_test 36.$tn.2 { + sql2 { INSERT INTO t2 VALUES('xyz') } + list [catch { sql2 { CREATE TABLE t3(x) } } msg] $msg + } {1 {database or disk is full}} +} + +forcedelete test1 test2 +foreach {tn uri} { + 1 {file:?mode=memory&cache=shared} + 2 {file:one?mode=memory&cache=shared} + 3 {file:test1?cache=shared} + 4 {file:test2?another=parameter&yet=anotherone} +} { + do_test 37.$tn { + catch { db close } + sqlite3_shutdown + sqlite3_config_uri 1 + sqlite3 db $uri + + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + SELECT * FROM t1; + } + } {1} + + do_execsql_test 37.$tn.2 { + VACUUM; + SELECT * FROM t1; + } {1} + + db close + sqlite3_shutdown + sqlite3_config_uri 0 +} + +do_test 38.1 { + catch { db close } + forcedelete test.db + set fd [open test.db w] + puts $fd "hello world" + close $fd + sqlite3 db test.db + catchsql { CREATE TABLE t1(x) } +} {1 {file is encrypted or is not a database}} +do_test 38.2 { + catch { db close } + forcedelete test.db +} {} + +do_test 39.1 { + sqlite3 db test.db + execsql { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('xxx'); + INSERT INTO t1 VALUES('two'); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 VALUES(randomblob(400)); + BEGIN; + UPDATE t1 SET x = 'one' WHERE rowid=1; + } + set ::stmt [sqlite3_prepare db "SELECT * FROM t1 ORDER BY rowid" -1 dummy] + sqlite3_step $::stmt + sqlite3_column_text $::stmt 0 +} {one} +do_test 39.2 { + execsql { CREATE TABLE t2(x) } + sqlite3_step $::stmt + sqlite3_column_text $::stmt 0 +} {two} +do_test 39.3 { + sqlite3_finalize $::stmt + execsql COMMIT +} {} + +do_execsql_test 39.4 { + PRAGMA auto_vacuum = 2; + CREATE TABLE t3(x); + CREATE TABLE t4(x); + + DROP TABLE t2; + DROP TABLE t3; + DROP TABLE t4; +} +do_test 39.5 { + db close + sqlite3 db test.db + execsql { + PRAGMA cache_size = 1; + PRAGMA incremental_vacuum; + PRAGMA integrity_check; + } +} {ok} + +do_test 40.1 { + reset_db + execsql { + PRAGMA auto_vacuum = 1; + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES(randomblob(1200)); + PRAGMA page_count; + } +} {6} +do_test 40.2 { + execsql { + INSERT INTO t1 VALUES(randomblob(1200)); + INSERT INTO t1 VALUES(randomblob(1200)); + INSERT INTO t1 VALUES(randomblob(1200)); + } +} {} +do_test 40.3 { + db close + sqlite3 db test.db + execsql { + PRAGMA cache_size = 1; + CREATE TABLE t2(x); + PRAGMA integrity_check; + } +} {ok} + +do_test 41.1 { + reset_db + execsql { + CREATE TABLE t1(x PRIMARY KEY); + INSERT INTO t1 VALUES(randomblob(200)); + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200) FROM t1; + } +} {} +do_test 41.2 { + testvfs tv -default 1 + tv sectorsize 16384; + tv devchar [list] + db close + sqlite3 db test.db + execsql { + PRAGMA cache_size = 1; + DELETE FROM t1 WHERE rowid%4; + PRAGMA integrity_check; + } +} {ok} +db close +tv delete + +set pending_prev [sqlite3_test_control_pending_byte 0x1000000] +do_test 42.1 { + reset_db + execsql { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + db close + sqlite3_test_control_pending_byte 0x0010000 + sqlite3 db test.db + db eval { PRAGMA mmap_size = 0 } + catchsql { SELECT sum(length(y)) FROM t1 } +} {1 {database disk image is malformed}} +do_test 42.2 { + reset_db + execsql { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + db close + + testvfs tv -default 1 + tv sectorsize 16384; + tv devchar [list] + sqlite3 db test.db -vfs tv + execsql { UPDATE t1 SET x = randomblob(200) } +} {} +db close +tv delete +sqlite3_test_control_pending_byte $pending_prev + +do_test 43.1 { + reset_db + execsql { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 2); + CREATE TABLE t3(x, y); + INSERT INTO t3 VALUES(1, 2); + } + db close + sqlite3 db test.db + + db eval { PRAGMA mmap_size = 0 } + db eval { SELECT * FROM t1 } + sqlite3_db_status db CACHE_MISS 0 +} {0 2 0} + +do_test 43.2 { + db eval { SELECT * FROM t2 } + sqlite3_db_status db CACHE_MISS 1 +} {0 3 0} + +do_test 43.3 { + db eval { SELECT * FROM t3 } + sqlite3_db_status db CACHE_MISS 0 +} {0 1 0} + finish_test + diff --git a/test/pager2.test b/test/pager2.test index fa5f7b9..0e2b33b 100644 --- a/test/pager2.test +++ b/test/pager2.test @@ -118,7 +118,6 @@ tv delete #------------------------------------------------------------------------- -# # pager2-2.1: Test a ROLLBACK with journal_mode=off. # pager2-2.2: Test shrinking the database (auto-vacuum) with # journal_mode=off @@ -148,4 +147,22 @@ do_test pager2-2.2 { file size test.db } {3072} +#------------------------------------------------------------------------- +# Test that shared in-memory databases seem to work. +# +db close +do_test pager2-3.1 { + forcedelete test.db + sqlite3_shutdown + sqlite3_config_uri 1 + + sqlite3 db1 {file:test.db?mode=memory&cache=shared} + sqlite3 db2 {file:test.db?mode=memory&cache=shared} + sqlite3 db3 test.db + + db1 eval { CREATE TABLE t1(a, b) } + db2 eval { INSERT INTO t1 VALUES(1, 2) } + list [catch { db3 eval { INSERT INTO t1 VALUES(3, 4) } } msg] $msg +} {1 {no such table: t1}} + finish_test diff --git a/test/pagerfault.test b/test/pagerfault.test index e04e97e..23754fa 100644 --- a/test/pagerfault.test +++ b/test/pagerfault.test @@ -1246,5 +1246,304 @@ do_faultsim_test pagerfault-27 -faults ioerr-persistent -prep { faultsim_integrity_check } + +#------------------------------------------------------------------------- +# +do_test pagerfault-28-pre { + faultsim_delete_and_reopen + db func a_string a_string + execsql { + PRAGMA page_size = 512; + + PRAGMA journal_mode = wal; + PRAGMA wal_autocheckpoint = 0; + PRAGMA cache_size = 100000; + + BEGIN; + CREATE TABLE t2(a UNIQUE, b UNIQUE); + INSERT INTO t2 VALUES( a_string(800), a_string(800) ); + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + COMMIT; + CREATE TABLE t1(a PRIMARY KEY, b); + } + expr {[file size test.db-shm] >= 96*1024} +} {1} +faultsim_save_and_close + +do_faultsim_test pagerfault-28a -faults oom* -prep { + faultsim_restore_and_reopen + execsql { PRAGMA mmap_size=0 } + + sqlite3 db2 test.db + db2 eval { SELECT count(*) FROM t2 } + + db func a_string a_string + execsql { + BEGIN; + INSERT INTO t1 VALUES(a_string(2000), a_string(2000)); + INSERT INTO t1 VALUES(a_string(2000), a_string(2000)); + } + set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY a" -1 DUMMY] + sqlite3_step $::STMT +} -body { + execsql { ROLLBACK } +} -test { + db2 close + sqlite3_finalize $::STMT + catchsql { ROLLBACK } + faultsim_integrity_check +} + +faultsim_restore_and_reopen +sqlite3 db2 test.db +db2 eval {SELECT count(*) FROM t2} +db close + +do_faultsim_test pagerfault-28b -faults oom* -prep { + sqlite3 db test.db +} -body { + execsql { SELECT count(*) FROM t2 } +} -test { + faultsim_test_result {0 2048} + db close +} + +db2 close + +#------------------------------------------------------------------------- +# Try this: +# +# 1) Put the pager in ERROR state (error during rollback) +# +# 2) Next time the connection is used inject errors into all xWrite() and +# xUnlock() calls. This causes the hot-journal rollback to fail and +# the pager to declare its locking state UNKNOWN. +# +# 3) Same again. +# +# 4a) Stop injecting errors. Allow the rollback to succeed. Check that +# the database is Ok. Or, +# +# 4b) Close and reopen the db. Check that the db is Ok. +# +proc custom_injectinstall {} { + testvfs custom -default true + custom filter {xWrite xUnlock} +} +proc custom_injectuninstall {} { + catch {db close} + catch {db2 close} + custom delete +} +proc custom_injectstart {iFail} { + custom ioerr $iFail 1 +} +proc custom_injectstop {} { + custom ioerr +} +set ::FAULTSIM(custom) [list \ + -injectinstall custom_injectinstall \ + -injectstart custom_injectstart \ + -injectstop custom_injectstop \ + -injecterrlist {{1 {disk I/O error}}} \ + -injectuninstall custom_injectuninstall \ +] + +do_test pagerfault-29-pre { + faultsim_delete_and_reopen + db func a_string a_string + execsql { + PRAGMA page_size = 1024; + PRAGMA cache_size = 5; + + BEGIN; + CREATE TABLE t2(a UNIQUE, b UNIQUE); + INSERT INTO t2 VALUES( a_string(800), a_string(800) ); + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + INSERT INTO t2 SELECT a_string(800), a_string(800) FROM t2; + COMMIT; + } + expr {[file size test.db] >= 50*1024} +} {1} +faultsim_save_and_close +foreach {tn tt} { + 29 { catchsql ROLLBACK } + 30 { db close ; sqlite3 db test.db } +} { + do_faultsim_test pagerfault-$tn -faults custom -prep { + faultsim_restore_and_reopen + db func a_string a_string + execsql { + PRAGMA cache_size = 5; + BEGIN; + UPDATE t2 SET a = a_string(799); + } + } -body { + catchsql ROLLBACK + catchsql ROLLBACK + catchsql ROLLBACK + } -test { + eval $::tt + if {"ok" != [db one {PRAGMA integrity_check}]} { + error "integrity check failed" + } + } +} + +do_test pagerfault-31-pre { + sqlite3_shutdown + sqlite3_config_uri 1 +} {SQLITE_OK} +do_faultsim_test pagerfault-31 -faults oom* -body { + sqlite3 db {file:one?mode=memory&cache=shared} + db eval { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + SELECT * FROM t1; + } +} -test { + faultsim_test_result {0 1} {1 {}} + catch { db close } +} +sqlite3_shutdown +sqlite3_config_uri 0 + +do_test pagerfault-32-pre { + reset_db + execsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('one'); + } +} {} +faultsim_save_and_close + +do_faultsim_test pagerfault-32 -prep { + faultsim_restore_and_reopen + db eval { SELECT * FROM t1; } +} -body { + execsql { SELECT * FROM t1; } +} -test { + faultsim_test_result {0 one} +} +sqlite3_shutdown +sqlite3_config_uri 0 + +do_faultsim_test pagerfault-33a -prep { + sqlite3 db :memory: + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } +} -body { + execsql { VACUUM } +} -test { + faultsim_test_result {0 {}} +} +do_faultsim_test pagerfault-33b -prep { + sqlite3 db "" + execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } +} -body { + execsql { VACUUM } +} -test { + faultsim_test_result {0 {}} +} + +do_test pagerfault-34-pre { + reset_db + execsql { + CREATE TABLE t1(x PRIMARY KEY); + } +} {} +faultsim_save_and_close +do_faultsim_test pagerfault-34 -prep { + faultsim_restore_and_reopen + execsql { + BEGIN; + INSERT INTO t1 VALUES( randomblob(4000) ); + DELETE FROM t1; + } +} -body { + execsql COMMIT +} -test { + faultsim_test_result {0 {}} +} + +do_test pagerfault-35-pre { + faultsim_delete_and_reopen + execsql { + CREATE TABLE t1(x PRIMARY KEY, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + faultsim_save_and_close +} {} +testvfs tv -default 1 +tv sectorsize 8192; +tv devchar [list] +do_faultsim_test pagerfault-35 -prep { + faultsim_restore_and_reopen +} -body { + execsql { UPDATE t1 SET x=randomblob(200) } +} -test { + faultsim_test_result {0 {}} +} +catch {db close} +tv delete + +sqlite3_shutdown +sqlite3_config_uri 1 +do_test pagerfault-36-pre { + faultsim_delete_and_reopen + execsql { + CREATE TABLE t1(x PRIMARY KEY, y); + INSERT INTO t1 VALUES(randomblob(200), randomblob(200)); + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + INSERT INTO t1 SELECT randomblob(200), randomblob(200) FROM t1; + } + faultsim_save_and_close +} {} +do_faultsim_test pagerfault-36 -prep { + faultsim_restore + sqlite3 db file:test.db?cache=shared + sqlite3 db2 file:test.db?cache=shared + db2 eval { + BEGIN; + SELECT count(*) FROM sqlite_master; + } + db eval { + PRAGMA cache_size = 1; + BEGIN; + UPDATE t1 SET x = randomblob(200); + } +} -body { + execsql ROLLBACK db +} -test { + catch { db eval {UPDATE t1 SET x = randomblob(200)} } + faultsim_test_result {0 {}} + catch { db close } + catch { db2 close } +} + +sqlite3_shutdown +sqlite3_config_uri 0 + finish_test diff --git a/test/pageropt.test b/test/pageropt.test index 8231196..7191661 100644 --- a/test/pageropt.test +++ b/test/pageropt.test @@ -87,12 +87,17 @@ do_test pageropt-1.4 { # But if the other thread modifies the database, then the cache # must refill. # +ifcapable mmap { + set x [expr {[permutation]=="mmap" ? 1 : 6}] +} else { + set x 6 +} do_test pageropt-1.5 { db2 eval {CREATE TABLE t2(y)} pagercount_sql { SELECT hex(x) FROM t1 } -} [list 6 0 0 $blobcontent] +} [list $x 0 0 $blobcontent] do_test pageropt-1.6 { pagercount_sql { SELECT hex(x) FROM t1 diff --git a/test/permutations.test b/test/permutations.test index 711d4e5..bc3ceb8 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -138,6 +138,14 @@ test_suite "veryquick" -prefix "" -description { test_set $allquicktests -exclude *malloc* *ioerr* *fault* ] +test_suite "mmap" -prefix "mm-" -description { + Similar to veryquick. Except with memory mapping disabled. +} -presql { + pragma mmap_size = 268435456; +} -files [ + test_set $allquicktests -exclude *malloc* *ioerr* *fault* -include malloc.test +] + test_suite "valgrind" -prefix "" -description { Run the "veryquick" test suite with a couple of multi-process tests (that fail under valgrind) omitted. @@ -178,6 +186,7 @@ test_suite "fts3" -prefix "" -description { fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3atoken.test fts3b.test fts3c.test fts3cov.test fts3d.test fts3defer.test fts3defer2.test fts3e.test fts3expr.test fts3expr2.test + fts3expr3.test fts3near.test fts3query.test fts3shared.test fts3snippet.test fts3sort.test fts3fault.test fts3malloc.test fts3matchinfo.test @@ -234,10 +243,14 @@ lappend ::testsuitelist xxx # Run some tests using pre-allocated page and scratch blocks. # +# mmap1.test is excluded because a good number of its tests depend on +# the page-cache being larger than the database. But this permutation +# causes the effective limit on the page-cache to be just 24 pages. +# test_suite "memsubsys1" -description { Tests using pre-allocated page and scratch blocks } -files [ - test_set $::allquicktests -exclude ioerr5.test malloc5.test + test_set $::allquicktests -exclude ioerr5.test malloc5.test mmap1.test ] -initialize { catch {db close} sqlite3_shutdown diff --git a/test/pragma.test b/test/pragma.test index 3c8d23a..a6d198e 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -534,12 +534,20 @@ do_test pragma-6.2.2 { b DEFAULT (5+3), c TEXT, d INTEGER DEFAULT NULL, - e TEXT DEFAULT '' + e TEXT DEFAULT '', + UNIQUE(b,c,d), + PRIMARY KEY(e,b,c) ); PRAGMA table_info(t5); } -} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 0 2 c TEXT 0 <> 0 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 0} +} {0 a TEXT 0 CURRENT_TIMESTAMP 0 1 b {} 0 5+3 2 2 c TEXT 0 <> 3 3 d INTEGER 0 NULL 0 4 e TEXT 0 '' 1} db nullvalue {} +do_test pragma-6.2.3 { + execsql { + CREATE TABLE t2_3(a,b INTEGER PRIMARY KEY,c); + pragma table_info(t2_3) + } +} {0 a {} 0 {} 0 1 b INTEGER 0 {} 1 2 c {} 0 {} 0} ifcapable {foreignkey} { do_test pragma-6.3.1 { execsql { @@ -928,6 +936,16 @@ proc check_temp_store {} { return "unknown" } +# Application_ID +# +do_test pragma-8.3.1 { + execsql { + PRAGMA application_id; + } +} {0} +do_test pragma-8.3.2 { + execsql {PRAGMA Application_ID(12345); PRAGMA application_id;} +} {12345} # Test temp_store and temp_store_directory pragmas # @@ -1618,6 +1636,48 @@ do_test 22.4.3 { execsql { PRAGMA aux.integrity_check; } } {ok} +db close +forcedelete test.db test.db-wal test.db-journal +sqlite3 db test.db +sqlite3 db2 test.db +do_test 23.1 { + db eval { + CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d); + CREATE INDEX i1 ON t1(b,c); + CREATE INDEX i2 ON t1(c,d); + CREATE TABLE t2(x INTEGER REFERENCES t1); + } + db2 eval {SELECT name FROM sqlite_master} +} {t1 i1 i2 t2} +do_test 23.2 { + db eval { + DROP INDEX i2; + CREATE INDEX i2 ON t1(c,d,b); + } + db2 eval {PRAGMA index_info(i2)} +} {0 2 c 1 3 d 2 1 b} +do_test 23.3 { + db eval { + CREATE INDEX i3 ON t1(d,b,c); + } + db2 eval {PRAGMA index_list(t1)} +} {0 i3 0 1 i2 0 2 i1 0} +do_test 23.4 { + db eval { + ALTER TABLE t1 ADD COLUMN e; + } + db2 eval { + PRAGMA table_info(t1); + } +} {/4 e {} 0 {} 0/} +do_test 23.5 { + db eval { + DROP TABLE t2; + CREATE TABLE t2(x, y INTEGER REFERENCES t1); + } + db2 eval { + PRAGMA foreign_key_list(t2); + } +} {0 0 t1 y {} {NO ACTION} {NO ACTION} NONE} + finish_test - - diff --git a/test/selectA.test b/test/selectA.test index 5b86377..5fd2288 100644 --- a/test/selectA.test +++ b/test/selectA.test @@ -281,13 +281,13 @@ do_test selectA-2.34 { do_test selectA-2.35 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-2.36 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE DESC,a,c + ORDER BY y COLLATE NOCASE DESC,x,z } } {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a} do_test selectA-2.37 { @@ -311,7 +311,7 @@ do_test selectA-2.39 { do_test selectA-2.40 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-2.41 { @@ -602,7 +602,7 @@ do_test selectA-2.85 { do_test selectA-2.86 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-2.87 { @@ -632,7 +632,7 @@ do_test selectA-2.90 { do_test selectA-2.91 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-2.92 { @@ -893,13 +893,13 @@ do_test selectA-3.34 { do_test selectA-3.35 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-3.36 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY b COLLATE NOCASE DESC,a,c + ORDER BY y COLLATE NOCASE DESC,x,z } } {mad Z z -23 Y y 5200000.0 X x {} U u hare m M abc e e hello d D {} C c 9.9 b B 1 a a} do_test selectA-3.37 { @@ -923,7 +923,7 @@ do_test selectA-3.39 { do_test selectA-3.40 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t1 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-3.41 { @@ -1214,7 +1214,7 @@ do_test selectA-3.85 { do_test selectA-3.86 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY b COLLATE NOCASE,a,c + ORDER BY y COLLATE NOCASE,x,z } } {1 a a 9.9 b B {} C c hello d D abc e e hare m M {} U u 5200000.0 X x -23 Y y mad Z z} do_test selectA-3.87 { @@ -1244,7 +1244,7 @@ do_test selectA-3.90 { do_test selectA-3.91 { execsql { SELECT x,y,z FROM t2 UNION SELECT a,b,c FROM t3 - ORDER BY c COLLATE BINARY DESC,a,b + ORDER BY z COLLATE BINARY DESC,x,y } } {mad Z z -23 Y y 5200000.0 X x {} U u abc e e {} C c 1 a a hare m M hello d D 9.9 b B} do_test selectA-3.92 { diff --git a/test/shell1.test b/test/shell1.test index 50e6c71..c60f3af 100644 --- a/test/shell1.test +++ b/test/shell1.test @@ -220,7 +220,7 @@ do_test shell1-2.3.2 { } {0 {}} do_test shell1-2.3.3 { catchcmd "test.db" ".explain \"1 2 3\"" -} {0 {}} +} {1 {ERROR: Not a boolean value: "1 2 3". Assuming "no".}} do_test shell1-2.3.4 { catchcmd "test.db" ".explain \"OFF\"" } {0 {}} @@ -253,7 +253,7 @@ do_test shell1-2.4.2 { # .backup ?DB? FILE Backup DB (default "main") to FILE do_test shell1-3.1.1 { catchcmd "test.db" ".backup" -} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}} +} {1 {missing FILENAME argument on .backup}} do_test shell1-3.1.2 { catchcmd "test.db" ".backup FOO" } {0 {}} @@ -263,7 +263,7 @@ do_test shell1-3.1.3 { do_test shell1-3.1.4 { # too many arguments catchcmd "test.db" ".backup FOO BAR BAD" -} {1 {Error: unknown command or invalid arguments: "backup". Enter ".help" for help}} +} {1 {too many arguments to .backup}} # .bail ON|OFF Stop after hitting an error. Default OFF do_test shell1-3.2.1 { @@ -326,10 +326,6 @@ do_test shell1-3.5.4 { do_test shell1-3.6.1 { catchcmd "test.db" ".exit" } {0 {}} -do_test shell1-3.6.2 { - # too many arguments - catchcmd "test.db" ".exit BAD" -} {1 {Error: unknown command or invalid arguments: "exit". Enter ".help" for help}} # .explain ON|OFF Turn output mode suitable for EXPLAIN on or off. do_test shell1-3.7.1 { diff --git a/test/speed1p.test b/test/speed1p.test index 915f165..6bf7b10 100644 --- a/test/speed1p.test +++ b/test/speed1p.test @@ -24,6 +24,8 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl speed_trial_init speed1 +sqlite3_memdebug_vfs_oom_test 0 + # Set a uniform random seed expr srand(0) @@ -78,7 +80,6 @@ do_test speed1p-1.0 { } } {i2a i2b t1 t2} - # 50000 INSERTs on an unindexed table # set list {} diff --git a/test/spellfix.test b/test/spellfix.test index afef981..dfa487a 100644 --- a/test/spellfix.test +++ b/test/spellfix.test @@ -16,7 +16,7 @@ set testprefix spellfix ifcapable !vtab { finish_test ; return } -register_spellfix_module db +load_static_extension db spellfix nextchar set vocab { rabbi rabbit rabbits rabble rabid rabies raccoon raccoons race raced racer @@ -84,6 +84,26 @@ foreach {tn word res} { } $res } +# Tests of the next_char function. +# +do_test 1.10 { + db eval { + CREATE TABLE vocab(w TEXT PRIMARY KEY); + INSERT INTO vocab SELECT word FROM t1; + } +} {} +do_execsql_test 1.11 { + SELECT next_char('re','vocab','w'); +} {a} +do_execsql_test 1.12 { + SELECT next_char('r','vocab','w'); +} {ae} +do_execsql_test 1.13 { + SELECT next_char('','vocab','w'); +} {r} +do_test 1.14 { + catchsql {SELECT next_char('','xyzzy','a')} +} {1 {no such table: xyzzy}} do_execsql_test 2.1 { CREATE VIRTUAL TABLE t2 USING spellfix1; @@ -136,16 +156,52 @@ do_test 3.2 { } } {} -breakpoint foreach {tn word res} { 1 kos* {kosher 3 kiosk 4 kudo 2 kiss 3 kissed 3} 2 kellj* {killjoy 5 kill 4 killed 4 killer 4 killers 4} 3 kellj {kill 4 kills 5 killjoy 7 keel 4 killed 6} } { - do_execsql_test 1.2.$tn { + do_execsql_test 3.2.$tn { SELECT word, matchlen FROM t3 WHERE word MATCH $word ORDER BY score, word LIMIT 5 } $res -} +} + +do_execsql_test 4.0 { + INSERT INTO t3(command) VALUES('edit_cost_table=NULL'); +} +foreach {tn word res} { + 1 kosher {kosher 0 kisser 51 kissers 76 kissed 126 kisses 126} + 2 kellj {keels 60 killjoy 68 kills 80 keel 120 kill 125} + 3 kashar {kosher 80 kisser 91 kissers 116 kissed 166 kisses 166} +} { + do_execsql_test 4.1.$tn { + SELECT word, distance FROM t3 WHERE word MATCH $word + ORDER BY score, word LIMIT 5 + } $res +} +do_execsql_test 5.0 { + CREATE TABLE costs2(iLang, cFrom, cTo, iCost); + INSERT INTO costs2 VALUES(0, 'a', 'o', 1); + INSERT INTO costs2 VALUES(0, 'e', 'o', 4); + INSERT INTO costs2 VALUES(0, 'i', 'o', 8); + INSERT INTO costs2 VALUES(0, 'u', 'o', 16); + INSERT INTO t3(command) VALUES('edit_cost_table="costs2"'); +} + +foreach {tn word res} { + 1 kasher {kosher 1} + 2 kesher {kosher 4} + 3 kisher {kosher 8} + 4 kosher {kosher 0} + 5 kusher {kosher 16} +} { + do_execsql_test 5.1.$tn { + SELECT word, distance FROM t3 WHERE word MATCH $word + ORDER BY score, word LIMIT 1 + } $res +} + + finish_test diff --git a/test/subquery.test b/test/subquery.test index d9d2952..f601d3f 100644 --- a/test/subquery.test +++ b/test/subquery.test @@ -421,7 +421,7 @@ do_test subquery-3.5.7 { # and expose bugs to do with re-using statements that have been # passed to sqlite3_reset(). # -# One problem was that VDBE memory cells were not being initialised +# One problem was that VDBE memory cells were not being initialized # to NULL on the second and subsequent executions. # do_test subquery-4.1.1 { diff --git a/test/syscall.test b/test/syscall.test index d841a9a..5bf1225 100644 --- a/test/syscall.test +++ b/test/syscall.test @@ -60,7 +60,7 @@ foreach s { open close access getcwd stat fstat ftruncate fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 unlink openDirectory mkdir rmdir - statvfs fchown umask + statvfs fchown umask mmap munmap mremap } { if {[test_syscall exists $s]} {lappend syscall_list $s} } diff --git a/test/sysfault.test b/test/sysfault.test index 07d525c..92fb534 100644 --- a/test/sysfault.test +++ b/test/sysfault.test @@ -243,5 +243,35 @@ do_faultsim_test 3 -faults vfsfault-* -prep { faultsim_test_result {0 20000} } -finish_test +#------------------------------------------------------------------------- +# Test errors in mmap(). +# +proc vfsfault_install {} { + test_syscall reset + test_syscall install {mmap} +} +faultsim_delete_and_reopen +execsql { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); +} +faultsim_save_and_close + +do_faultsim_test 4 -faults vfsfault-* -prep { + faultsim_restore_and_reopen + file_control_chunksize_test db main 8192 + execsql { + PRAGMA mmap_size = 1000000; + } +} -body { + test_syscall errno mmap EACCES + + execsql { + SELECT * FROM t1; + } +} -test { + faultsim_test_result {0 {1 2}} {1 {disk I/O error}} +} + +finish_test diff --git a/test/temptable.test b/test/temptable.test index 5eeb0f5..6a1e2b9 100644 --- a/test/temptable.test +++ b/test/temptable.test @@ -150,7 +150,7 @@ do_test temptable-3.4 { # Check for correct name collision processing. A name collision can # occur when process A creates a temporary table T then process B # creates a permanent table also named T. The temp table in process A -# hides the existance of the permanent table. +# hides the existence of the permanent table. # do_test temptable-4.1 { execsql { diff --git a/test/tester.tcl b/test/tester.tcl index d3fa607..761a36e 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -54,6 +54,7 @@ # do_ioerr_test TESTNAME ARGS... # crashsql ARGS... # integrity_check TESTNAME ?DB? +# verify_ex_errcode TESTNAME EXPECTED ?DB? # do_test TESTNAME SCRIPT EXPECTED # do_execsql_test TESTNAME SQL EXPECTED # do_catchsql_test TESTNAME SQL EXPECTED @@ -122,7 +123,7 @@ if {[info command sqlite_orig]==""} { set res } else { # This command is not opening a new database connection. Pass the - # arguments through to the C implemenation as the are. + # arguments through to the C implementation as the are. # uplevel 1 sqlite_orig $args } @@ -461,6 +462,7 @@ if {0==[info exists ::SLAVE]} { set TC(count) 0 set TC(fail_list) [list] set TC(omit_list) [list] + set TC(warn_list) [list] proc set_test_counter {counter args} { if {[llength $args]} { @@ -495,6 +497,18 @@ proc fail_test {name} { } } +# Remember a warning message to be displayed at the conclusion of all testing +# +proc warning {msg {append 1}} { + puts "Warning: $msg" + set warnList [set_test_counter warn_list] + if {$append} { + lappend warnList $msg + } + set_test_counter warn_list $warnList +} + + # Increment the number of tests run # proc incr_ntest {} { @@ -783,6 +797,9 @@ proc finalize_testing {} { if {$nErr>0} { puts "Failures on these tests: [set_test_counter fail_list]" } + foreach warning [set_test_counter warn_list] { + puts "Warning: $warning" + } run_thread_tests 1 if {[llength $omitList]>0} { puts "Omitted test cases:" @@ -968,6 +985,12 @@ proc integrity_check {name {db db}} { } } +# Check the extended error code +# +proc verify_ex_errcode {name expected {db db}} { + do_test $name [list sqlite3_extended_errcode $db] $expected +} + # Return true if the SQL statement passed as the second argument uses a # statement transaction. @@ -1030,7 +1053,7 @@ proc ifcapable {expr code {else ""} {elsecode ""}} { # boolean, indicating whether or not the process actually crashed or # reported some other error. The second element in the returned list is the # error message. This is "child process exited abnormally" if the crash -# occured. +# occurred. # # crashsql -delay CRASHDELAY -file CRASHFILE ?-blocksize BLOCKSIZE? $sql # @@ -1111,6 +1134,25 @@ proc crashsql {args} { lappend r $msg } +proc run_ioerr_prep {} { + set ::sqlite_io_error_pending 0 + catch {db close} + catch {db2 close} + catch {forcedelete test.db} + catch {forcedelete test.db-journal} + catch {forcedelete test2.db} + catch {forcedelete test2.db-journal} + set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] + sqlite3_extended_result_codes $::DB $::ioerropts(-erc) + if {[info exists ::ioerropts(-tclprep)]} { + eval $::ioerropts(-tclprep) + } + if {[info exists ::ioerropts(-sqlprep)]} { + execsql $::ioerropts(-sqlprep) + } + expr 0 +} + # Usage: do_ioerr_test # # This proc is used to implement test cases that check that IO errors @@ -1143,10 +1185,26 @@ proc do_ioerr_test {testname args} { # TEMPORARY: For 3.5.9, disable testing of extended result codes. There are # a couple of obscure IO errors that do not return them. set ::ioerropts(-erc) 0 + + # Create a single TCL script from the TCL and SQL specified + # as the body of the test. + set ::ioerrorbody {} + if {[info exists ::ioerropts(-tclbody)]} { + append ::ioerrorbody "$::ioerropts(-tclbody)\n" + } + if {[info exists ::ioerropts(-sqlbody)]} { + append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}" + } + + save_prng_state + if {$::ioerropts(-cksum)} { + run_ioerr_prep + eval $::ioerrorbody + set ::goodcksum [cksum] + } set ::go 1 #reset_prng_state - save_prng_state for {set n $::ioerropts(-start)} {$::go} {incr n} { set ::TN $n incr ::ioerropts(-count) -1 @@ -1163,27 +1221,12 @@ proc do_ioerr_test {testname args} { # Delete the files test.db and test2.db, then execute the TCL and # SQL (in that order) to prepare for the test case. do_test $testname.$n.1 { - set ::sqlite_io_error_pending 0 - catch {db close} - catch {db2 close} - catch {forcedelete test.db} - catch {forcedelete test.db-journal} - catch {forcedelete test2.db} - catch {forcedelete test2.db-journal} - set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] - sqlite3_extended_result_codes $::DB $::ioerropts(-erc) - if {[info exists ::ioerropts(-tclprep)]} { - eval $::ioerropts(-tclprep) - } - if {[info exists ::ioerropts(-sqlprep)]} { - execsql $::ioerropts(-sqlprep) - } - expr 0 + run_ioerr_prep } {0} # Read the 'checksum' of the database. if {$::ioerropts(-cksum)} { - set checksum [cksum] + set ::checksum [cksum] } # Set the Nth IO error to fail. @@ -1191,20 +1234,10 @@ proc do_ioerr_test {testname args} { set ::sqlite_io_error_persist $::ioerropts(-persist) set ::sqlite_io_error_pending $n }] $n - - # Create a single TCL script from the TCL and SQL specified - # as the body of the test. - set ::ioerrorbody {} - if {[info exists ::ioerropts(-tclbody)]} { - append ::ioerrorbody "$::ioerropts(-tclbody)\n" - } - if {[info exists ::ioerropts(-sqlbody)]} { - append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}" - } - # Execute the TCL Script created in the above block. If - # there are at least N IO operations performed by SQLite as - # a result of the script, the Nth will fail. + # Execute the TCL script created for the body of this test. If + # at least N IO operations performed by SQLite as a result of + # the script, the Nth will fail. do_test $testname.$n.3 { set ::sqlite_io_error_hit 0 set ::sqlite_io_error_hardhit 0 @@ -1300,7 +1333,7 @@ proc do_ioerr_test {testname args} { } } - # If an IO error occured, then the checksum of the database should + # If an IO error occurred, then the checksum of the database should # be the same as before the script that caused the IO error was run. # if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} { @@ -1308,8 +1341,15 @@ proc do_ioerr_test {testname args} { catch {db close} catch {db2 close} set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] - cksum - } $checksum + set nowcksum [cksum] + set res [expr {$nowcksum==$::checksum || $nowcksum==$::goodcksum}] + if {$res==0} { + puts "now=$nowcksum" + puts "the=$::checksum" + puts "fwd=$::goodcksum" + } + set res + } 1 } set ::sqlite_io_error_hardhit 0 diff --git a/test/tkt-2d1a5c67d.test b/test/tkt-2d1a5c67d.test index bf9595f..3fef187 100644 --- a/test/tkt-2d1a5c67d.test +++ b/test/tkt-2d1a5c67d.test @@ -46,7 +46,7 @@ for {set ii 1} {$ii<=10} {incr ii} { db close forcedelete test.db test.db-wal sqlite3 db test.db -register_wholenumber_module db +load_static_extension db wholenumber db eval { PRAGMA journal_mode=WAL; CREATE TABLE t1(a,b); diff --git a/test/tkt2409.test b/test/tkt2409.test index 9ac6542..936f57c 100644 --- a/test/tkt2409.test +++ b/test/tkt2409.test @@ -107,7 +107,7 @@ do_test tkt2409-1.2 { integrity_check tkt2409-1.3 # Check that the transaction was rolled back. Because the INSERT -# statement in which the "I/O error" occured did not open a statement +# statement in which the "I/O error" occurred did not open a statement # transaction, SQLite had no choice but to roll back the transaction. # do_test tkt2409-1.4 { @@ -175,7 +175,7 @@ do_test tkt2409-3.2 { integrity_check tkt2409-3.3 # Check that the transaction was rolled back. Because the INSERT -# statement in which the "I/O error" occured did not open a statement +# statement in which the "I/O error" occurred did not open a statement # transaction, SQLite had no choice but to roll back the transaction. # do_test tkt2409-3.4 { diff --git a/test/tkt2822.test b/test/tkt2822.test index d0b1633..d3512d3 100644 --- a/test/tkt2822.test +++ b/test/tkt2822.test @@ -208,12 +208,15 @@ do_test tkt2822-5.4 { # In "ORDER BY +b" the term is now an expression rather than # a label. It therefore matches by rule (3) instead of rule (2). +# +# 2013-04-13: This is busted. Changed to conform to PostgreSQL and +# MySQL and Oracle behavior. # do_test tkt2822-5.5 { execsql { SELECT a AS b FROM t3 ORDER BY +b; } -} {9 1} +} {1 9} # Tests for rule 2 in compound queries # diff --git a/test/tkt3457.test b/test/tkt3457.test index 7b9a1b3..0475741 100644 --- a/test/tkt3457.test +++ b/test/tkt3457.test @@ -62,6 +62,14 @@ do_test tkt3457-1.1 { execsql COMMIT } {} +# Disable fchmod to make sure SQLite itself does not try to change the +# permission bits on us +# +catch { + test_syscall install fchmod + test_syscall fault 1 1 +} + do_test tkt3457-1.2 { forcecopy bak.db-journal test.db-journal file attributes test.db-journal -permissions --------- @@ -84,4 +92,10 @@ do_test tkt3457-1.5 { catchsql { SELECT * FROM t1 } } {0 {1 2 3 4 5 6}} +# Reenable fchmod +catch { + test_syscall uninstall + test_syscall fault 0 0 +} + finish_test diff --git a/test/tkt3762.test b/test/tkt3762.test index c24041c..424b1e6 100644 --- a/test/tkt3762.test +++ b/test/tkt3762.test @@ -10,8 +10,8 @@ #*********************************************************************** # # Ticket #3762: Make sure that an incremental vacuum that reduces the -# size of the database file such that a pointer-map page is elemented -# can be correctly rolled back. +# size of the database file such that if a pointer-map page is eliminated +# it can be correctly rolled back. # # That ticket #3762 has been fixed has already been verified by the # savepoint6.test test script. But this script is simplier and a diff --git a/test/trigger1.test b/test/trigger1.test index c49bc65..1ebe12c 100644 --- a/test/trigger1.test +++ b/test/trigger1.test @@ -423,6 +423,7 @@ do_test trigger1-6.2 { do_test trigger1-6.3 { catchsql {DELETE FROM t2} } {1 {deletes are not permitted}} +verify_ex_errcode trigger1-6.3b SQLITE_CONSTRAINT_TRIGGER do_test trigger1-6.4 { execsql {SELECT * FROM t2} } {3 4 7 8} diff --git a/test/trigger3.test b/test/trigger3.test index 34d1970..fe91b9d 100644 --- a/test/trigger3.test +++ b/test/trigger3.test @@ -45,6 +45,7 @@ do_test trigger3-1.1 { INSERT INTO tbl VALUES (1, 5, 6); } } {1 {Trigger abort}} +verify_ex_errcode trigger3-1.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-1.2 { execsql { SELECT * FROM tbl; @@ -63,6 +64,7 @@ do_test trigger3-2.1 { INSERT INTO tbl VALUES (2, 5, 6); } } {1 {Trigger fail}} +verify_ex_errcode trigger3-2.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-2.2 { execsql { SELECT * FROM tbl; @@ -77,6 +79,7 @@ do_test trigger3-3.1 { INSERT INTO tbl VALUES (3, 5, 6); } } {1 {Trigger rollback}} +verify_ex_errcode trigger3-3.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-3.2 { execsql { SELECT * FROM tbl; @@ -92,6 +95,7 @@ do_test trigger3-3.3 { INSERT INTO tbl VALUES (3, 9, 10); } } {1 {Trigger rollback}} +verify_ex_errcode trigger3-3.3b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-3.4 { execsql {SELECT * FROM tbl} } {} @@ -172,6 +176,7 @@ do_test trigger3-7.1 { INSERT INTO tbl_view VALUES(1, 2, 3); } } {1 {View rollback}} +verify_ex_errcode trigger3-7.1b SQLITE_CONSTRAINT_TRIGGER do_test trigger3-7.2 { catchsql { INSERT INTO tbl_view VALUES(2, 2, 3); @@ -182,6 +187,7 @@ do_test trigger3-7.3 { INSERT INTO tbl_view VALUES(3, 2, 3); } } {1 {View abort}} +verify_ex_errcode trigger3-7.3b SQLITE_CONSTRAINT_TRIGGER } ;# ifcapable view diff --git a/test/triggerA.test b/test/triggerA.test index 0bc017f..821e2d9 100644 --- a/test/triggerA.test +++ b/test/triggerA.test @@ -192,6 +192,13 @@ do_test triggerA-2.10 { SELECT * FROM result4 ORDER BY a; } } {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504} +do_test triggerA-2.11 { + db eval { + DELETE FROM result4; + UPDATE v5 SET b = main.v5.b+9900000 WHERE main.v5.x BETWEEN 3 AND 5; + SELECT * FROM result4 ORDER BY a; + } +} {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504} # Only run the reamining tests if memory debugging is turned on. # diff --git a/test/unique.test b/test/unique.test index 56c49ee..eac19b5 100644 --- a/test/unique.test +++ b/test/unique.test @@ -48,6 +48,7 @@ do_test unique-1.3 { INSERT INTO t1(a,b,c) VALUES(1,3,4) } } {1 {column a is not unique}} +verify_ex_errcode unique-1.3b SQLITE_CONSTRAINT_UNIQUE do_test unique-1.4 { execsql { SELECT * FROM t1 ORDER BY a; @@ -58,6 +59,7 @@ do_test unique-1.5 { INSERT INTO t1(a,b,c) VALUES(3,2,4) } } {1 {column b is not unique}} +verify_ex_errcode unique-1.5b SQLITE_CONSTRAINT_UNIQUE do_test unique-1.6 { execsql { SELECT * FROM t1 ORDER BY a; @@ -99,6 +101,7 @@ do_test unique-2.3 { INSERT INTO t2 VALUES(1,5); } } {1 {column a is not unique}} +verify_ex_errcode unique-2.3b SQLITE_CONSTRAINT_UNIQUE do_test unique-2.4 { catchsql { SELECT * FROM t2 ORDER BY a @@ -125,6 +128,7 @@ do_test unique-2.8 { CREATE UNIQUE INDEX i2 ON t2(a); } } {1 {indexed columns are not unique}} +verify_ex_errcode unique-2.8b SQLITE_CONSTRAINT_UNIQUE do_test unique-2.9 { catchsql { CREATE INDEX i2 ON t2(a); @@ -163,6 +167,7 @@ do_test unique-3.4 { SELECT * FROM t3 ORDER BY a,b,c,d; } } {1 {columns a, c, d are not unique}} +verify_ex_errcode unique-3.4b SQLITE_CONSTRAINT_UNIQUE integrity_check unique-3.5 # Make sure NULLs are distinct as far as the UNIQUE tests are @@ -217,6 +222,7 @@ do_test unique-4.9 { do_test unique-4.10 { catchsql {CREATE UNIQUE INDEX i4c ON t4(b)} } {1 {indexed columns are not unique}} +verify_ex_errcode unique-4.10b SQLITE_CONSTRAINT_UNIQUE integrity_check unique-4.99 # Test the error message generation logic. In particular, make sure we @@ -249,5 +255,7 @@ do_test unique-5.2 { INSERT INTO t5 VALUES(1,2,3,4,5,6); } } {1 {columns first_column_with_long_name, second_column_with_long_name, third_column_with_long_name, fourth_column_with_long_name, fifth_column_with_long_name, sixth_column_with_long_name are not unique}} +verify_ex_errcode unique-5.2b SQLITE_CONSTRAINT_UNIQUE + finish_test diff --git a/test/view.test b/test/view.test index b444090..779f77b 100644 --- a/test/view.test +++ b/test/view.test @@ -576,4 +576,39 @@ do_test view-20.1 { } } {} +# Ticket [d58ccbb3f1b]: Prevent Table.nRef overflow. +db close +sqlite3 db :memory: +do_test view-21.1 { + catchsql { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(5); + CREATE VIEW v1 AS SELECT x*2 FROM t1; + CREATE VIEW v2 AS SELECT * FROM v1 UNION SELECT * FROM v1; + CREATE VIEW v4 AS SELECT * FROM v2 UNION SELECT * FROM v2; + CREATE VIEW v8 AS SELECT * FROM v4 UNION SELECT * FROM v4; + CREATE VIEW v16 AS SELECT * FROM v8 UNION SELECT * FROM v8; + CREATE VIEW v32 AS SELECT * FROM v16 UNION SELECT * FROM v16; + CREATE VIEW v64 AS SELECT * FROM v32 UNION SELECT * FROM v32; + CREATE VIEW v128 AS SELECT * FROM v64 UNION SELECT * FROM v64; + CREATE VIEW v256 AS SELECT * FROM v128 UNION SELECT * FROM v128; + CREATE VIEW v512 AS SELECT * FROM v256 UNION SELECT * FROM v256; + CREATE VIEW v1024 AS SELECT * FROM v512 UNION SELECT * FROM v512; + CREATE VIEW v2048 AS SELECT * FROM v1024 UNION SELECT * FROM v1024; + CREATE VIEW v4096 AS SELECT * FROM v2048 UNION SELECT * FROM v2048; + CREATE VIEW v8192 AS SELECT * FROM v4096 UNION SELECT * FROM v4096; + CREATE VIEW v16384 AS SELECT * FROM v8192 UNION SELECT * FROM v8192; + CREATE VIEW v32768 AS SELECT * FROM v16384 UNION SELECT * FROM v16384; + CREATE VIEW vx AS SELECT * FROM v32768 UNION SELECT * FROM v32768; + } +} {1 {too many references to "v1": max 65535}} +ifcapable progress { + do_test view-21.2 { + db progress 1000 {expr 1} + catchsql { + SELECT * FROM v32768; + } + } {1 interrupted} +} + finish_test diff --git a/test/vtab1.test b/test/vtab1.test index 95811d7..1f17e53 100644 --- a/test/vtab1.test +++ b/test/vtab1.test @@ -1091,12 +1091,54 @@ do_test vtab1.13-3 { } {15 {} 16} +do_test vtab1-14.001 { + execsql {SELECT rowid, * FROM echo_c WHERE +rowid IN (1,2,3)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.002 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid IN (1,2,3)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.003 { + execsql {SELECT rowid, * FROM echo_c WHERE +rowid IN (0,1,5,2,'a',3,NULL)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.004 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid IN (0,1,5,'a',2,3,NULL)} +} {1 3 G H 2 {} 15 16 3 15 {} 16} +do_test vtab1-14.005 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,1,5,'a',2,3)} +} {} +do_test vtab1-14.006 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,5,'a',2,3)} +} {1 3 G H} +do_test vtab1-14.007 { + execsql {SELECT rowid, * FROM echo_c WHERE +rowid NOT IN (0,5,'a',2,3,NULL)} +} {} +do_test vtab1-14.008 { + execsql {SELECT rowid, * FROM echo_c WHERE rowid NOT IN (0,5,'a',2,3,NULL)} +} {} +do_test vtab1-14.011 { + execsql {SELECT * FROM echo_c WHERE +a IN (1,3,8,'x',NULL,15,24)} +} {3 G H 15 {} 16} +do_test vtab1-14.012 { + execsql {SELECT * FROM echo_c WHERE a IN (1,3,8,'x',NULL,15,24)} +} {3 G H 15 {} 16} +do_test vtab1-14.013 { + execsql {SELECT * FROM echo_c WHERE a NOT IN (1,8,'x',15,24)} +} {3 G H} +do_test vtab1-14.014 { + execsql {SELECT * FROM echo_c WHERE a NOT IN (1,8,'x',NULL,15,24)} +} {} +do_test vtab1-14.015 { + execsql {SELECT * FROM echo_c WHERE +a NOT IN (1,8,'x',NULL,15,24)} +} {} + + + do_test vtab1-14.1 { execsql { DELETE FROM c } set echo_module "" execsql { SELECT * FROM echo_c WHERE rowid IN (1, 2, 3) } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}] +} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c' WHERE rowid = .} 1/} do_test vtab1-14.2 { set echo_module "" @@ -1114,7 +1156,7 @@ do_test vtab1-14.4 { set echo_module "" execsql { SELECT * FROM echo_c WHERE a IN (1, 2) } set echo_module -} [list xBestIndex {SELECT rowid, * FROM 'c'} xFilter {SELECT rowid, * FROM 'c'}] +} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE a = .} xFilter {SELECT rowid, . FROM 'c' WHERE a = .} 1/} do_test vtab1-15.1 { execsql { diff --git a/test/wal.test b/test/wal.test index 24ce5f8..c8078a1 100644 --- a/test/wal.test +++ b/test/wal.test @@ -727,6 +727,9 @@ do_test wal-11.9 { list [expr [file size test.db]/1024] [log_deleted test.db-wal] } {37 1} sqlite3_wal db test.db +set nWal 39 +if {[permutation]!="mmap"} {set nWal 37} +ifcapable !mmap {set nWal 37} do_test wal-11.10 { execsql { PRAGMA cache_size = 10; @@ -735,7 +738,7 @@ do_test wal-11.10 { SELECT count(*) FROM t1; } list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [wal_file_size 37 1024]] +} [list 37 [wal_file_size $nWal 1024]] do_test wal-11.11 { execsql { SELECT count(*) FROM t1; @@ -745,7 +748,7 @@ do_test wal-11.11 { } {32 16} do_test wal-11.12 { list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [wal_file_size 37 1024]] +} [list 37 [wal_file_size $nWal 1024]] do_test wal-11.13 { execsql { INSERT INTO t1 VALUES( blob(900) ); @@ -755,7 +758,7 @@ do_test wal-11.13 { } {17 ok} do_test wal-11.14 { list [expr [file size test.db]/1024] [file size test.db-wal] -} [list 37 [wal_file_size 37 1024]] +} [list 37 [wal_file_size $nWal 1024]] #------------------------------------------------------------------------- @@ -1509,10 +1512,10 @@ do_test wal-23.3 { faultsim_restore_and_reopen execsql { SELECT * FROM t1 } } {1 2 3 4} -set nPage [expr 2+$AUTOVACUUM] do_test wal-23.4 { set ::log -} [list SQLITE_OK "Recovered $nPage frames from WAL file $walfile"] +} [list SQLITE_NOTICE_RECOVER_WAL \ + "recovered 2 frames from WAL file $walfile"] ifcapable autovacuum { diff --git a/test/wal5.test b/test/wal5.test index 6eceed5..68750f1 100644 --- a/test/wal5.test +++ b/test/wal5.test @@ -235,7 +235,16 @@ foreach {testprefix do_wal_checkpoint} { do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {} do_test 2.3.$tn.6 { file_page_counts } {1 4 1 4} do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 4 3} - do_test 2.3.$tn.8 { file_page_counts } {1 4 2 4} + + # The checkpoint above only writes page 1 of the db file. The other + # page (page 2) is locked by the read-transaction opened by the + # [sql2] commmand above. So normally, the db is 1 page in size here. + # However, in mmap() mode, the db is pre-allocated to 2 pages at the + # start of the checkpoint, even though page 2 cannot be written. + set nDb 2 + if {[permutation]!="mmap"} {set nDb 1} + ifcapable !mmap {set nDb 1} + do_test 2.3.$tn.8 { file_page_counts } [list $nDb 4 2 4] } # Check that checkpoints block on the correct locks. And respond correctly @@ -343,4 +352,3 @@ foreach {testprefix do_wal_checkpoint} { finish_test - diff --git a/test/walfault.test b/test/walfault.test index 6f9aedd..4a9d98a 100644 --- a/test/walfault.test +++ b/test/walfault.test @@ -548,6 +548,44 @@ do_faultsim_test walfault-14 -prep { set nRow [db eval {SELECT count(*) FROM abc}] if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" } } -finish_test + +#------------------------------------------------------------------------- +# Test fault-handling when switching out of exclusive-locking mode. +# +do_test walfault-14-pre { + faultsim_delete_and_reopen + execsql { + PRAGMA auto_vacuum = 0; + PRAGMA journal_mode = WAL; + BEGIN; + CREATE TABLE abc(a PRIMARY KEY); + INSERT INTO abc VALUES(randomblob(1500)); + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } + faultsim_save_and_close +} {} +do_faultsim_test walfault-14 -prep { + faultsim_restore_and_reopen + breakpoint + execsql { + SELECT count(*) FROM abc; + PRAGMA locking_mode = exclusive; + BEGIN; + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } +} -body { + db eval { + PRAGMA locking_mode = normal; + BEGIN; + INSERT INTO abc VALUES(randomblob(1500)); + COMMIT; + } +} -test { + faultsim_integrity_check + set nRow [db eval {SELECT count(*) FROM abc}] + if {$nRow!=3 && $nRow!=4} { error "Bad db content" } +} finish_test diff --git a/test/where.test b/test/where.test index 05c7b2f..2dbc283 100644 --- a/test/where.test +++ b/test/where.test @@ -379,11 +379,26 @@ ifcapable subquery { SELECT * FROM t1 WHERE rowid+0 IN (1,2,3,1234) order by 1; } } {1 0 4 2 1 9 3 1 16 102} - do_test where-5.3 { + do_test where-5.3a { count { SELECT * FROM t1 WHERE w IN (-1,1,2,3) order by 1; } - } {1 0 4 2 1 9 3 1 16 14} + } {1 0 4 2 1 9 3 1 16 13} + do_test where-5.3b { + count { + SELECT * FROM t1 WHERE w IN (3,-1,1,2) order by 1; + } + } {1 0 4 2 1 9 3 1 16 13} + do_test where-5.3c { + count { + SELECT * FROM t1 WHERE w IN (3,2,-1,1,2) order by 1; + } + } {1 0 4 2 1 9 3 1 16 13} + do_test where-5.3d { + count { + SELECT * FROM t1 WHERE w IN (-1,1,2,3) order by 1 DESC; + } + } {3 1 16 2 1 9 1 0 4 12} do_test where-5.4 { count { SELECT * FROM t1 WHERE w+0 IN (-1,1,2,3) order by 1; @@ -452,6 +467,30 @@ ifcapable subquery { SELECT * FROM t1 WHERE x IN (1,7) AND y IN (9,16) ORDER BY 1; } } {2 1 9 3 1 16 11} + do_test where-5.100 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x, y + } + } {2 1 9 54 5 3025 62 5 3969} + do_test where-5.101 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x DESC, y DESC + } + } {62 5 3969 54 5 3025 2 1 9} + do_test where-5.102 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x DESC, y + } + } {54 5 3025 62 5 3969 2 1 9} + do_test where-5.103 { + db eval { + SELECT w, x, y FROM t1 WHERE x IN (1,5) AND y IN (9,8,3025,1000,3969) + ORDER BY x, y DESC + } + } {2 1 9 62 5 3969 54 5 3025} } # This procedure executes the SQL. Then it checks to see if the OP_Sort @@ -511,11 +550,16 @@ do_test where-6.7 { } } {1 100 4 2 99 9 3 98 16 nosort} ifcapable subquery { - do_test where-6.8 { + do_test where-6.8a { cksort { SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a LIMIT 3 } - } {1 100 4 2 99 9 3 98 16 sort} + } {1 100 4 2 99 9 3 98 16 nosort} + do_test where-6.8b { + cksort { + SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a DESC LIMIT 3 + } + } {9 92 100 7 94 64 5 96 36 nosort} } do_test where-6.9.1 { cksort { diff --git a/test/where2.test b/test/where2.test index d61c089..e8c2f36 100644 --- a/test/where2.test +++ b/test/where2.test @@ -167,24 +167,54 @@ ifcapable subquery { } } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} } - do_test where2-4.6 { + do_test where2-4.6a { queryplan { SELECT * FROM t1 WHERE x IN (1,2,3,4,5,6,7,8) AND y IN (10000,10001,10002,10003,10004,10005) - ORDER BY 2 + ORDER BY x + } + } {99 6 10000 10006 nosort t1 i1xy} + do_test where2-4.6b { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY x DESC + } + } {99 6 10000 10006 nosort t1 i1xy} + do_test where2-4.6c { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY x, y + } + } {99 6 10000 10006 nosort t1 i1xy} + do_test where2-4.6d { + queryplan { + SELECT * FROM t1 + WHERE x IN (1,2,3,4,5,6,7,8) + AND y IN (10000,10001,10002,10003,10004,10005) + ORDER BY x, y DESC } } {99 6 10000 10006 sort t1 i1xy} # Duplicate entires on the RHS of an IN operator do not cause duplicate # output rows. # - do_test where2-4.6 { + do_test where2-4.6x { queryplan { SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207) ORDER BY w } } {99 6 10000 10006 100 6 10201 10207 sort t1 i1zyx} + do_test where2-4.6y { + queryplan { + SELECT * FROM t1 WHERE z IN (10207,10006,10006,10207) + ORDER BY w DESC + } + } {100 6 10201 10207 99 6 10000 10006 sort t1 i1zyx} ifcapable compound { do_test where2-4.7 { queryplan { @@ -207,11 +237,16 @@ do_test where2-5.1 { } {99 6 10000 10006 nosort t1 i1w} ifcapable subquery { - do_test where2-5.2 { + do_test where2-5.2a { queryplan { SELECT * FROM t1 WHERE w IN (99) ORDER BY w } - } {99 6 10000 10006 sort t1 i1w} + } {99 6 10000 10006 nosort t1 i1w} + do_test where2-5.2b { + queryplan { + SELECT * FROM t1 WHERE w IN (99) ORDER BY w DESC + } + } {99 6 10000 10006 nosort t1 i1w} } # Verify that OR clauses get translated into IN operators. diff --git a/test/where8.test b/test/where8.test index a7d5edb..9b6014e 100644 --- a/test/where8.test +++ b/test/where8.test @@ -290,6 +290,38 @@ do_test where8-3.15 { } } {I I I I I I I I I I II II II II II II II II II II III III III III III 9 1} + +do_test where8-3.21 { + execsql_status { + SELECT a, d FROM t1, (t2) WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +do_test where8-3.21.1 { + execsql_status { + SELECT a, d FROM t1, ((t2)) AS t3 WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +if {[permutation] != "no_optimization"} { +do_test where8-3.21.2 { + execsql_status { + SELECT a, d FROM t1, ((SELECT * FROM t2)) AS t3 WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +} +do_test where8-3.22 { + execsql_status { + SELECT a, d FROM ((((((t1))), (((t2)))))) + WHERE (a=d OR b=e) AND a<5 ORDER BY a + } +} {1 1 2 2 3 3 4 2 4 4 0 0} +if {[permutation] != "no_optimization"} { +do_test where8-3.23 { + execsql_status { + SELECT * FROM ((SELECT * FROM t2)) AS t3; + } +} {1 {} I 2 four IV 3 {} IX 4 sixteen XVI 5 {} XXV 6 thirtysix XXXVI 7 fortynine XLIX 8 sixtyeight LXIV 9 eightyone LXXXIX 10 {} C 9 0} +} + #----------------------------------------------------------------------- # The following tests - where8-4.* - verify that adding or removing # indexes does not change the results returned by various queries. diff --git a/test/where9.test b/test/where9.test index d618208..1e94fdf 100644 --- a/test/where9.test +++ b/test/where9.test @@ -232,7 +232,7 @@ do_test where9-1.3.3 { } {90 91 92 97 scan 98 sort 0} do_test where9-1.3.4 { count_steps { - SELECT a FROM t4 + SELECT a FROM (t4) WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) OR (b NOT NULL AND c NOT NULL AND d IS NULL) OR (b NOT NULL AND c IS NULL AND d NOT NULL) @@ -876,5 +876,42 @@ do_test where9-8.1 { ORDER BY +a; } } {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} +do_test where9-8.2 { + db eval { + SELECT * + FROM t81 LEFT JOIN (t82) ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} +do_test where9-8.3 { + db eval { + SELECT * + FROM (t81) LEFT JOIN (main.t82) ON y=b JOIN t83 + WHERE c==p OR d==p + ORDER BY +a; + } +} {2 3 4 5 {} {} 5 55 3 4 5 6 2 4 5 55} + +# Fix for ticket [f2369304e47167e3e644e2f1fe9736063391d7b7] +# Incorrect results when OR is used in the ON clause of a LEFT JOIN +# +do_test where9-9.1 { + db eval { + CREATE TABLE t91(x); INSERT INTO t91 VALUES(1); + CREATE TABLE t92(y INTEGER PRIMARY KEY,a,b); + INSERT INTO t92 VALUES(1,2,3); + SELECT 1 FROM t91 LEFT JOIN t92 ON a=2 OR b=3; + SELECT 2 FROM t91 LEFT JOIN t92 ON a=2 AND b=3; + SELECT 3 FROM t91 LEFT JOIN t92 ON (a=2 OR b=3) AND y IS NULL; + SELECT 4 FROM t91 LEFT JOIN t92 ON (a=2 AND b=3) AND y IS NULL; + CREATE TEMP TABLE x9 AS SELECT * FROM t91 LEFT JOIN t92 ON a=2 OR b=3; + SELECT 5 FROM x9 WHERE y IS NULL; + SELECT 6 FROM t91 LEFT JOIN t92 ON a=2 OR b=3 WHERE y IS NULL; + SELECT 7 FROM t91 LEFT JOIN t92 ON a=2 AND b=3 WHERE y IS NULL; + SELECT 8 FROM t91 LEFT JOIN t92 ON a=22 OR b=33 WHERE y IS NULL; + SELECT 9 FROM t91 LEFT JOIN t92 ON a=22 AND b=33 WHERE y IS NULL; + } +} {1 2 3 4 8 9} finish_test diff --git a/test/win32lock.test b/test/win32lock.test index d014be4..7241720 100644 --- a/test/win32lock.test +++ b/test/win32lock.test @@ -27,6 +27,7 @@ proc xLog {error_code msg} { lappend ::log $msg } sqlite3 db test.db +db eval {PRAGMA mmap_size=0} do_test win32lock-1.1 { db eval { diff --git a/test/zerodamage.test b/test/zerodamage.test index 217749a..de5088b 100644 --- a/test/zerodamage.test +++ b/test/zerodamage.test @@ -59,7 +59,7 @@ do_test zerodamage-2.0 { } tv filter xDelete tv script xDeleteCallback - register_wholenumber_module db + load_static_extension db wholenumber db eval { PRAGMA page_size=1024; PRAGMA journal_mode=DELETE; diff --git a/tool/build-shell.sh b/tool/build-shell.sh index 8e62a71..6a48299 100644 --- a/tool/build-shell.sh +++ b/tool/build-shell.sh @@ -17,5 +17,6 @@ gcc -o sqlite3 -g -Os -I. \ -DSQLITE_ENABLE_RTREE \ -DHAVE_READLINE \ -DHAVE_USLEEP=1 \ - ../sqlite/src/shell.c ../sqlite/src/test_vfstrace.c \ + ../sqlite/src/shell.c \ + ../sqlite/src/test_vfstrace.c \ sqlite3.c -ldl -lreadline -lncurses diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 2c569d7..c93bcea 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -23,7 +23,7 @@ # # Begin by reading the "sqlite3.h" header file. Extract the version number -# from in this file. The versioon number is needed to generate the header +# from in this file. The version number is needed to generate the header # comment of the amalgamation. # if {[lsearch $argv --nostatic]>=0} { @@ -311,6 +311,7 @@ foreach file { fts3_porter.c fts3_tokenizer.c fts3_tokenizer1.c + fts3_tokenize_vtab.c fts3_write.c fts3_snippet.c fts3_unicode.c diff --git a/tool/mksqlite3h.tcl b/tool/mksqlite3h.tcl index f68f61a..a89b9f9 100644 --- a/tool/mksqlite3h.tcl +++ b/tool/mksqlite3h.tcl @@ -68,9 +68,14 @@ set declpattern {^ *[a-zA-Z][a-zA-Z_0-9 ]+ \**sqlite3_[_a-zA-Z0-9]+\(} # Force the output to use unix line endings, even on Windows. fconfigure stdout -translation lf -# Process the src/sqlite.h.in ext/rtree/sqlite3rtree.h files. +set filelist [subst { + $TOP/src/sqlite.h.in + $TOP/ext/rtree/sqlite3rtree.h +}] + +# Process the source files. # -foreach file [list $TOP/src/sqlite.h.in $TOP/ext/rtree/sqlite3rtree.h] { +foreach file $filelist { set in [open $file] while {![eof $in]} { diff --git a/tool/showdb.c b/tool/showdb.c index a100bba..27424e0 100644 --- a/tool/showdb.c +++ b/tool/showdb.c @@ -176,7 +176,7 @@ static void print_db_header(void){ print_decode_line(aData, 56, 4, "Text encoding"); print_decode_line(aData, 60, 4, "User version"); print_decode_line(aData, 64, 4, "Incremental-vacuum mode"); - print_decode_line(aData, 68, 4, "meta[7]"); + print_decode_line(aData, 68, 4, "Application ID"); print_decode_line(aData, 72, 4, "meta[8]"); print_decode_line(aData, 76, 4, "meta[9]"); print_decode_line(aData, 80, 4, "meta[10]"); @@ -471,7 +471,7 @@ static void page_usage_msg(int pgno, const char *zFormat, ...){ zMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); if( pgno<=0 || pgno>mxPage ){ - printf("ERROR: page %d out of bounds. Range=1..%d. Msg: %s\n", + printf("ERROR: page %d out of range 1..%d: %s\n", pgno, mxPage, zMsg); sqlite3_free(zMsg); return; @@ -479,7 +479,7 @@ static void page_usage_msg(int pgno, const char *zFormat, ...){ if( zPageUse[pgno]!=0 ){ printf("ERROR: page %d used multiple times:\n", pgno); printf("ERROR: previous: %s\n", zPageUse[pgno]); - printf("ERROR: current: %s\n", zPageUse[pgno]); + printf("ERROR: current: %s\n", zMsg); sqlite3_free(zPageUse[pgno]); } zPageUse[pgno] = zMsg; @@ -615,15 +615,32 @@ static void page_usage_freelist(int pgno){ } } +/* +** Determine pages used as PTRMAP pages +*/ +static void page_usage_ptrmap(unsigned char *a){ + if( a[55] ){ + int usable = pagesize - a[20]; + int pgno = 2; + int perPage = usable/5; + while( pgno<=mxPage ){ + page_usage_msg(pgno, "PTRMAP page covering %d..%d", + pgno+1, pgno+perPage); + pgno += perPage + 1; + } + } +} + /* ** Try to figure out how every page in the database file is being used. */ static void page_usage_report(const char *zDbName){ - int i; + int i, j; int rc; sqlite3 *db; sqlite3_stmt *pStmt; unsigned char *a; + char zQuery[200]; /* Avoid the pathological case */ if( mxPage<1 ){ @@ -648,20 +665,26 @@ static void page_usage_report(const char *zDbName){ /* Discover the usage of each page */ a = getContent(0, 100); page_usage_freelist(decodeInt32(a+32)); + page_usage_ptrmap(a); free(a); page_usage_btree(1, 0, 0, "sqlite_master"); - rc = sqlite3_prepare_v2(db, - "SELECT type, name, rootpage FROM SQLITE_MASTER WHERE rootpage", - -1, &pStmt, 0); - if( rc==SQLITE_OK ){ - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - int pgno = sqlite3_column_int(pStmt, 2); - page_usage_btree(pgno, 0, 0, sqlite3_column_text(pStmt, 1)); + sqlite3_exec(db, "PRAGMA writable_schema=ON", 0, 0, 0); + for(j=0; j<2; j++){ + sqlite3_snprintf(sizeof(zQuery), zQuery, + "SELECT type, name, rootpage FROM SQLITE_MASTER WHERE rootpage" + " ORDER BY rowid %s", j?"DESC":""); + rc = sqlite3_prepare_v2(db, zQuery, -1, &pStmt, 0); + if( rc==SQLITE_OK ){ + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + int pgno = sqlite3_column_int(pStmt, 2); + page_usage_btree(pgno, 0, 0, sqlite3_column_text(pStmt, 1)); + } + }else{ + printf("ERROR: cannot query database: %s\n", sqlite3_errmsg(db)); } - }else{ - printf("ERROR: cannot query database: %s\n", sqlite3_errmsg(db)); + rc = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) break; } - sqlite3_finalize(pStmt); sqlite3_close(db); /* Print the report and free memory used */ @@ -673,6 +696,53 @@ static void page_usage_report(const char *zDbName){ zPageUse = 0; } +/* +** Try to figure out how every page in the database file is being used. +*/ +static void ptrmap_coverage_report(const char *zDbName){ + unsigned int pgno; + unsigned char *aHdr; + unsigned char *a; + int usable; + int perPage; + unsigned int i; + + /* Avoid the pathological case */ + if( mxPage<1 ){ + printf("empty database\n"); + return; + } + + /* Make sure PTRMAPs are used in this database */ + aHdr = getContent(0, 100); + if( aHdr[55]==0 ){ + printf("database does not use PTRMAP pages\n"); + return; + } + usable = pagesize - aHdr[20]; + perPage = usable/5; + free(aHdr); + printf("%5d: root of sqlite_master\n", 1); + for(pgno=2; pgno<=mxPage; pgno += perPage+1){ + printf("%5d: PTRMAP page covering %d..%d\n", pgno, + pgno+1, pgno+perPage); + a = getContent((pgno-1)*pagesize, usable); + for(i=0; i+5<=usable && pgno+1+i/5<=mxPage; i+=5){ + const char *zType = "???"; + unsigned int iFrom = decodeInt32(&a[i+1]); + switch( a[i] ){ + case 1: zType = "b-tree root page"; break; + case 2: zType = "freelist page"; break; + case 3: zType = "first page of overflow"; break; + case 4: zType = "later page of overflow"; break; + case 5: zType = "b-tree non-root page"; break; + } + printf("%5d: %s, parent=%u\n", pgno+1+i/5, zType, iFrom); + } + free(a); + } +} + /* ** Print a usage comment */ @@ -682,6 +752,7 @@ static void usage(const char *argv0){ "args:\n" " dbheader Show database header\n" " pgidx Index of how each page is used\n" + " ptrmap Show all PTRMAP page content\n" " NNN..MMM Show hex of pages NNN through MMM\n" " NNN..end Show hex of pages NNN through end of file\n" " NNNb Decode btree page NNN\n" @@ -731,6 +802,14 @@ int main(int argc, char **argv){ page_usage_report(argv[1]); continue; } + if( strcmp(argv[i], "ptrmap")==0 ){ + ptrmap_coverage_report(argv[1]); + continue; + } + if( strcmp(argv[i], "help")==0 ){ + usage(argv[0]); + continue; + } if( !isdigit(argv[i][0]) ){ fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]); continue; diff --git a/tool/showwal.c b/tool/showwal.c index ae25a59..2888c10 100644 --- a/tool/showwal.c +++ b/tool/showwal.c @@ -18,6 +18,65 @@ static int perLine = 16; /* HEX elements to print per line */ typedef long long int i64; /* Datatype for 64-bit integers */ +/* Information for computing the checksum */ +typedef struct Cksum Cksum; +struct Cksum { + int bSwap; /* True to do byte swapping on 32-bit words */ + unsigned s0, s1; /* Current checksum value */ +}; + +/* +** extract a 32-bit big-endian integer +*/ +static unsigned int getInt32(const unsigned char *a){ + unsigned int x = (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; + return x; +} + +/* +** Swap bytes on a 32-bit unsigned integer +*/ +static unsigned int swab32(unsigned int x){ + return (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) + + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24); +} + +/* Extend the checksum. Reinitialize the checksum if bInit is true. +*/ +static void extendCksum( + Cksum *pCksum, + unsigned char *aData, + unsigned int nByte, + int bInit +){ + unsigned int *a32; + if( bInit ){ + int a = 0; + *((char*)&a) = 1; + if( a==1 ){ + /* Host is little-endian */ + pCksum->bSwap = getInt32(aData)!=0x377f0682; + }else{ + /* Host is big-endian */ + pCksum->bSwap = getInt32(aData)!=0x377f0683; + } + pCksum->s0 = 0; + pCksum->s1 = 0; + } + a32 = (unsigned int*)aData; + while( nByte>0 ){ + unsigned int x0 = a32[0]; + unsigned int x1 = a32[1]; + if( pCksum->bSwap ){ + x0 = swab32(x0); + x1 = swab32(x1); + } + pCksum->s0 += x0 + pCksum->s1; + pCksum->s1 += x1 + pCksum->s0; + nByte -= 8; + a32 += 2; + } +} /* ** Convert the var-int format into i64. Return the number of bytes @@ -152,39 +211,46 @@ static void print_frame(int iFrame){ } /* -** extract a 32-bit big-endian integer +** Summarize a single frame on a single line. */ -static unsigned int getInt32(const unsigned char *a){ - unsigned int x = (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3]; - return x; -} - -/* -** Print an entire page of content as hex -*/ -static void print_oneline_frame(int iFrame){ +static void print_oneline_frame(int iFrame, Cksum *pCksum){ int iStart; unsigned char *aData; + unsigned int s0, s1; iStart = 32 + (iFrame-1)*(pagesize+24); aData = getContent(iStart, 24); - fprintf(stdout, "Frame %4d: %6d %6d 0x%08x 0x%08x 0x%08x 0x%08x\n", + extendCksum(pCksum, aData, 8, 0); + extendCksum(pCksum, getContent(iStart+24, pagesize), pagesize, 0); + s0 = getInt32(aData+16); + s1 = getInt32(aData+20); + fprintf(stdout, "Frame %4d: %6d %6d 0x%08x,%08x 0x%08x,%08x %s\n", iFrame, getInt32(aData), getInt32(aData+4), getInt32(aData+8), getInt32(aData+12), - getInt32(aData+16), - getInt32(aData+20) + s0, + s1, + (s0==pCksum->s0 && s1==pCksum->s1) ? "" : "cksum-fail" ); + + /* Reset the checksum so that a single frame checksum failure will not + ** cause all subsequent frames to also show a failure. */ + pCksum->s0 = s0; + pCksum->s1 = s1; free(aData); } /* ** Decode the WAL header. */ -static void print_wal_header(void){ +static void print_wal_header(Cksum *pCksum){ unsigned char *aData; aData = getContent(0, 32); + if( pCksum ){ + extendCksum(pCksum, aData, 24, 1); + printf("Checksum byte order: %s\n", pCksum->bSwap ? "swapped" : "native"); + } printf("WAL Header:\n"); print_decode_line(aData, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)"); print_decode_line(aData, 4, 4, 0, "File format"); @@ -194,60 +260,199 @@ static void print_wal_header(void){ print_decode_line(aData, 20,4, 1, "Salt-2"); print_decode_line(aData, 24,4, 1, "Checksum-1"); print_decode_line(aData, 28,4, 1, "Checksum-2"); + if( pCksum ){ + if( pCksum->s0!=getInt32(aData+24) ){ + printf("**** cksum-1 mismatch: 0x%08x\n", pCksum->s0); + } + if( pCksum->s1!=getInt32(aData+28) ){ + printf("**** cksum-2 mismatch: 0x%08x\n", pCksum->s1); + } + } free(aData); } +/* +** Describe cell content. +*/ +static int describeContent( + unsigned char *a, /* Cell content */ + int nLocal, /* Bytes in a[] */ + char *zDesc /* Write description here */ +){ + int nDesc = 0; + int n, i, j; + i64 x, v; + const unsigned char *pData; + const unsigned char *pLimit; + char sep = ' '; + + pLimit = &a[nLocal]; + n = decodeVarint(a, &x); + pData = &a[x]; + a += n; + i = x - n; + while( i>0 && pData<=pLimit ){ + n = decodeVarint(a, &x); + a += n; + i -= n; + nLocal -= n; + zDesc[0] = sep; + sep = ','; + nDesc++; + zDesc++; + if( x==0 ){ + sprintf(zDesc, "*"); /* NULL is a "*" */ + }else if( x>=1 && x<=6 ){ + v = (signed char)pData[0]; + pData++; + switch( x ){ + case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; + case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; + case 4: v = (v<<8) + pData[0]; pData++; + case 3: v = (v<<8) + pData[0]; pData++; + case 2: v = (v<<8) + pData[0]; pData++; + } + sprintf(zDesc, "%lld", v); + }else if( x==7 ){ + sprintf(zDesc, "real"); + pData += 8; + }else if( x==8 ){ + sprintf(zDesc, "0"); + }else if( x==9 ){ + sprintf(zDesc, "1"); + }else if( x>=12 ){ + int size = (x-12)/2; + if( (x&1)==0 ){ + sprintf(zDesc, "blob(%d)", size); + }else{ + sprintf(zDesc, "txt(%d)", size); + } + pData += size; + } + j = strlen(zDesc); + zDesc += j; + nDesc += j; + } + return nDesc; +} + +/* +** Compute the local payload size given the total payload size and +** the page size. +*/ +static int localPayload(i64 nPayload, char cType){ + int maxLocal; + int minLocal; + int surplus; + int nLocal; + if( cType==13 ){ + /* Table leaf */ + maxLocal = pagesize-35; + minLocal = (pagesize-12)*32/255-23; + }else{ + maxLocal = (pagesize-12)*64/255-23; + minLocal = (pagesize-12)*32/255-23; + } + if( nPayload>maxLocal ){ + surplus = minLocal + (nPayload-minLocal)%(pagesize-4); + if( surplus<=maxLocal ){ + nLocal = surplus; + }else{ + nLocal = minLocal; + } + }else{ + nLocal = nPayload; + } + return nLocal; +} /* ** Create a description for a single cell. +** +** The return value is the local cell size. */ -static int describeCell(unsigned char cType, unsigned char *a, char **pzDesc){ +static int describeCell( + unsigned char cType, /* Page type */ + unsigned char *a, /* Cell content */ + int showCellContent, /* Show cell content if true */ + char **pzDesc /* Store description here */ +){ int i; int nDesc = 0; int n = 0; int leftChild; i64 nPayload; i64 rowid; - static char zDesc[100]; + int nLocal; + static char zDesc[1000]; i = 0; if( cType<=5 ){ leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3]; a += 4; n += 4; - sprintf(zDesc, "left-child: %d ", leftChild); + sprintf(zDesc, "lx: %d ", leftChild); nDesc = strlen(zDesc); } if( cType!=5 ){ i = decodeVarint(a, &nPayload); a += i; n += i; - sprintf(&zDesc[nDesc], "sz: %lld ", nPayload); + sprintf(&zDesc[nDesc], "n: %lld ", nPayload); nDesc += strlen(&zDesc[nDesc]); + nLocal = localPayload(nPayload, cType); + }else{ + nPayload = nLocal = 0; } if( cType==5 || cType==13 ){ i = decodeVarint(a, &rowid); a += i; n += i; - sprintf(&zDesc[nDesc], "rowid: %lld ", rowid); + sprintf(&zDesc[nDesc], "r: %lld ", rowid); nDesc += strlen(&zDesc[nDesc]); } + if( nLocal0 ){ + printf(" key: lx=left-child n=payload-size r=rowid\n"); + } + if( showMap ){ + zMap = malloc(pagesize); + memset(zMap, '.', pagesize); + memset(zMap, '1', hdrSize); + memset(&zMap[hdrSize], 'H', iCellPtr); + memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell); + } for(i=0; i