diff --git a/Makefile.msc b/Makefile.msc index 71fb5bdd07..6345539788 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -94,6 +94,14 @@ WIN32HEAP = 0 DEBUG = 0 !ENDIF +# Enable use of available compiler optimizations? Normally, this should be +# non-zero. Setting this to zero, thus disabling all compiler optimizations, +# can be useful for testing. +# +!IFNDEF OPTIMIZATIONS +OPTIMIZATIONS = 2 +!ENDIF + # Check for the predefined command macro CC. This should point to the compiler # binary for the target platform. If it is not defined, simply define it to # the legacy default value 'cl.exe'. @@ -345,11 +353,15 @@ TCLSH_CMD = tclsh85 # Compiler options needed for programs that use the readline() library. # +!IFNDEF READLINE_FLAGS READLINE_FLAGS = -DHAVE_READLINE=0 +!ENDIF # The library that programs using readline() must link against. # +!IFNDEF LIBREADLINE LIBREADLINE = +!ENDIF # Should the database engine be compiled threadsafe # @@ -401,17 +413,30 @@ RCC = $(RCC) $(OPT_FEATURE_FLAGS) TCC = $(TCC) $(OPTS) RCC = $(RCC) $(OPTS) -# If symbols are enabled, enable PDBs. -# If debugging is enabled, disable all optimizations and enable PDBs. +# If compiling for debugging, add some defines. !IF $(DEBUG)>0 -TCC = $(TCC) -Od -D_DEBUG -BCC = $(BCC) -Od -D_DEBUG +TCC = $(TCC) -D_DEBUG +BCC = $(BCC) -D_DEBUG RCC = $(RCC) -D_DEBUG -!ELSE -TCC = $(TCC) -O2 -BCC = $(BCC) -O2 !ENDIF +# If optimizations are enabled or disabled (either implicitly or +# explicitly), add the necessary flags. +!IF $(DEBUG)>0 || $(OPTIMIZATIONS)==0 +TCC = $(TCC) -Od +BCC = $(BCC) -Od +!ELSEIF $(OPTIMIZATIONS)>=3 +TCC = $(TCC) -Ox +BCC = $(BCC) -Ox +!ELSEIF $(OPTIMIZATIONS)==2 +TCC = $(TCC) -O2 +BCC = $(BCC) -O2 +!ELSEIF $(OPTIMIZATIONS)==1 +TCC = $(TCC) -O1 +BCC = $(BCC) -O1 +!ENDIF + +# If symbols are enabled (or compiling for debugging), enable PDBs. !IF $(DEBUG)>0 || $(SYMBOLS)!=0 TCC = $(TCC) -Zi BCC = $(BCC) -Zi @@ -469,7 +494,9 @@ LTLIBS = $(LTLIBS) $(LIBICU) !ENDIF # nawk compatible awk. +!IFNDEF NAWK NAWK = gawk.exe +!ENDIF # You should not have to change anything below this line ############################################################################### diff --git a/VERSION b/VERSION index 19811903a7..f280719674 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.8.0 +3.8.1 diff --git a/configure b/configure index a7281f089c..57546c1a58 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.8.0. +# Generated by GNU Autoconf 2.62 for sqlite 3.8.1. # # 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.8.0' -PACKAGE_STRING='sqlite 3.8.0' +PACKAGE_VERSION='3.8.1' +PACKAGE_STRING='sqlite 3.8.1' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. @@ -874,7 +874,6 @@ TEMP_STORE BUILD_EXEEXT SQLITE_OS_UNIX SQLITE_OS_WIN -SQLITE_OS_OS2 TARGET_EXEEXT TCL_VERSION TCL_BIN_DIR @@ -1484,7 +1483,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.8.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.8.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1549,7 +1548,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.8.0:";; + short | recursive ) echo "Configuration of sqlite 3.8.1:";; esac cat <<\_ACEOF @@ -1665,7 +1664,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.8.0 +sqlite configure 3.8.1 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, @@ -1679,7 +1678,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.8.0, which was +It was created by sqlite $as_me 3.8.1, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ @@ -3733,13 +3732,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:3736: $ac_compile\"" >&5) + (eval echo "\"\$as_me:3735: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 - (eval echo "\"\$as_me:3739: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval echo "\"\$as_me:3738: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 - (eval echo "\"\$as_me:3742: output\"" >&5) + (eval echo "\"\$as_me:3741: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" @@ -4961,7 +4960,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4964 "configure"' > conftest.$ac_ext + echo '#line 4963 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -6830,11 +6829,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:6833: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6832: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6837: \$? = $ac_status" >&5 + echo "$as_me:6836: \$? = $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 +7168,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:7172: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7171: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7176: \$? = $ac_status" >&5 + echo "$as_me:7175: \$? = $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 +7273,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:7277: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7276: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7281: \$? = $ac_status" >&5 + echo "$as_me:7280: \$? = $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 +7328,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:7332: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7331: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7336: \$? = $ac_status" >&5 + echo "$as_me:7335: \$? = $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 +10141,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10145 "configure" +#line 10144 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -10238,7 +10237,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 10241 "configure" +#line 10240 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12257,7 +12256,7 @@ USE_AMALGAMATION=1 # if not, then we fall back to plain tclsh. # TODO: try other versions before falling back? # -for ac_prog in tclsh8.5 tclsh +for ac_prog in tclsh8.6 tclsh8.5 tclsh do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -12713,21 +12712,12 @@ else TARGET_EXEEXT=$config_TARGET_EXEEXT fi if test "$TARGET_EXEEXT" = ".exe"; then - if test $OS2_SHELL ; then - SQLITE_OS_UNIX=0 - SQLITE_OS_WIN=0 - SQLITE_OS_OS2=1 - CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1" - else - SQLITE_OS_UNIX=0 - SQLITE_OS_WIN=1 - SQLITE_OS_OS2=0 - CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1" - fi + SQLITE_OS_UNIX=0 + SQLITE_OS_WIN=1 + CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1" else SQLITE_OS_UNIX=1 SQLITE_OS_WIN=0 - SQLITE_OS_OS2=0 CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1" fi @@ -12736,7 +12726,6 @@ fi - ########## # Figure out all the parameters needed to compile against Tcl. # @@ -14032,7 +14021,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.8.0, which was +This file was extended by sqlite $as_me 3.8.1, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14085,7 +14074,7 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.8.0 +sqlite config.status 3.8.1 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.ac b/configure.ac index 4a7043ede1..2e70f2c235 100644 --- a/configure.ac +++ b/configure.ac @@ -139,7 +139,7 @@ USE_AMALGAMATION=1 # if not, then we fall back to plain tclsh. # TODO: try other versions before falling back? # -AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.5 tclsh], none) +AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.6 tclsh8.5 tclsh], none) if test "$TCLSH_CMD" = "none"; then # If we can't find a local tclsh, then building the amalgamation will fail. # We act as though --disable-amalgamation has been used. @@ -340,28 +340,18 @@ else TARGET_EXEEXT=$config_TARGET_EXEEXT fi if test "$TARGET_EXEEXT" = ".exe"; then - if test $OS2_SHELL ; then - SQLITE_OS_UNIX=0 - SQLITE_OS_WIN=0 - SQLITE_OS_OS2=1 - CFLAGS="$CFLAGS -DSQLITE_OS_OS2=1" - else - SQLITE_OS_UNIX=0 - SQLITE_OS_WIN=1 - SQLITE_OS_OS2=0 - CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1" - fi + SQLITE_OS_UNIX=0 + SQLITE_OS_WIN=1 + CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1" else SQLITE_OS_UNIX=1 SQLITE_OS_WIN=0 - SQLITE_OS_OS2=0 CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1" fi AC_SUBST(BUILD_EXEEXT) AC_SUBST(SQLITE_OS_UNIX) AC_SUBST(SQLITE_OS_WIN) -AC_SUBST(SQLITE_OS_OS2) AC_SUBST(TARGET_EXEEXT) ########## diff --git a/manifest b/manifest index a468c90153..933db6b26c 100644 --- a/manifest +++ b/manifest @@ -1,12 +1,12 @@ -C Merge\srecent\strunk\schanges. -D 2013-08-23T17:54:46.168 +C Merge\sfrom\strunk:\s\s(1)\sRecent\sbug\sfixes\s(2)\sSTAT4\ssupport\s(3)\swin32-longpath\nsupport. +D 2013-08-29T15:08:38.537 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in aff38bc64c582dd147f18739532198372587b0f0 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 -F Makefile.msc e5cc521bbc9fb09f032f7fef563542da7ac5544a +F Makefile.msc a3ad9cc70b639cb8d2fe42d24b5c6c127aaac346 F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 -F VERSION f135b651727f978b7191bd6fa12c7fc1e13e13ac +F VERSION a8d1f6839521130dc73c5408cdd24bcfd791df34 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 @@ -38,8 +38,8 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 -F configure 27e9279a219b652bd1c53439353a5e5be70214b2 x -F configure.ac 81c43d151d0b0e406be056394cc9ff4cb3fd0444 +F configure d9bb8485d40dd905e09c895692a0699628abace8 x +F configure.ac 4cf9f60785143fa141b10962ccc885d973792e9a F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 @@ -169,27 +169,27 @@ 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 a33fcb0b3a399d966951feb9f32115106b3ecc2e +F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a +F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083 +F src/analyze.c 7f869a251f3d58d3b95883eeae425d4ee17a66bd F src/attach.c fea00cab11c854646a27641a263f5876569a51f9 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 2f1987981139bd2f6d8c728d64bf09fb387443c3 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c b7cbb0bcc61cbf1e0ed3cfcc79ee12ffcf4f469e +F src/btree.c b9b57df546df2636294bfb21a986f5707b417df2 F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf F src/btreeInt.h 51cf220a9b9223354770883e93a859dc377aa27f -F src/build.c f99a715ff9290996b579d5e1ec8e94239dc9ae5e +F src/build.c f63e8929c7f89c0074fbc74929bc946ea117b2f8 F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac -F src/ctime.c 4262c227bc91cecc61ae37ed3a40f08069cfa267 +F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 30ed4bc76a1a32c55bf17ac1528c5867aa5502c0 -F src/expr.c 6bab61c757c4c4c94a92e98d507025d5d18afd3c +F src/expr.c 4d89bd03a04fcdb5ff71d86b4e0cc7d3230797b8 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 914a6bbd987d857c41ac9d244efa6641f36faadb -F src/func.c d7be77897c91d5b9887beb372f1e6deb515c92af +F src/func.c 5b064acd303b3e74f019ab551d423ff6cace4023 F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 @@ -203,7 +203,7 @@ F src/main.c 525985af588234b3d01b48fe856b83d9bdc2d912 F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa -F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf +F src/mem2.c e243acb034f91e75e4c06aed5d3d960025c62d1f F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 F src/mem5.c 0025308a93838022bd5696cf9627ff4e40b19918 F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785 @@ -211,36 +211,36 @@ 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/mutex_w32.c 6108c88e1cb38d8fbb3534b170793815cbedbf97 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 -F src/os_unix.c 9eafa5458cf2ff684ddccff82c9bb113c7cad847 -F src/os_win.c 1d84f2079d9b91f91a4b5dbfa5e08f1b1a0ed0ff +F src/os_unix.c c27a14a05061e4e690bd3949dc0246bda35e399d +F src/os_win.c 26d752736dff0c7e4e384ab65b353cce1e7e19c5 F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8 F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c F src/parse.y 27c6b4138497d6f8360ba7847da6ed48033f957f F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63 -F src/pragma.c dc9ceab9b361e3fbd461ebaa7fb8003233e20725 +F src/pragma.c 3aa3d8c8623b7b71c5b1bfb72dcc31fb0c25665f F src/prepare.c fa6988589f39af8504a61731614cd4f6ae71554f F src/printf.c da9119eb31a187a4b99f60aa4a225141c0ebb74b F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 F src/resolve.c 9d53899cc6e1f4ec0b4632d07e97d57827bf63b9 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 F src/select.c 8b148eb851f384412aea57091659d14b369918ca -F src/shell.c 1c317a4c96d61d8d9fdad9fd1811d9b10b8c7f57 +F src/shell.c dbe064d404bb497acd8a44c066cd6b8460a71236 F src/sqlite.h.in dcca940021f8f6643630fb7711bfd4e3fa002566 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 93fe5b4c6234942cb9ca14fc25ad634d0e2ff775 +F src/sqliteInt.h 7806162e2d9796e9d9b6025f0757904b605ff91c F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c a25933b085bde305a59b43e52f5624871db46a1e -F src/test1.c 870fc648a48cb6d6808393174f7ebe82b8c840fa +F src/test1.c 26226cfd2b6dc3f77d2eb27f07ffcf236b4e728b F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35 F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df @@ -253,18 +253,18 @@ F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8 F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12 F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16 -F src/test_config.c 6b614c603cb4db1c996f1b192ca0a46ef0d152cd +F src/test_config.c 9ec20c94a3290f64d60c6db12c3e88ed3de9e501 F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f -F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170 +F src/test_func.c f8235719dff4bf9ffee04c55a190af8782ce9ab5 F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61 F src/test_intarray.h 2ece66438cfd177b78d1bfda7a4180cd3a10844d F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64 F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e -F src/test_malloc.c a105801222c0514f8befa2a1712a95505cce099a +F src/test_malloc.c eba4e1c5847cc98e7edc98f62265cd2abafba7a6 F src/test_multiplex.c 5d691eeb6cb6aa7888da28eba5e62a9a857d3c0f F src/test_multiplex.h 110a8c4d356e0aa464ca8730375608a9a0b61ae1 F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f @@ -287,41 +287,43 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2 F src/trigger.c 5c0ea9b8755e7c5e1a700f3e27ac4f8d92dd221e -F src/update.c e3668141dd9701023681239265e001388f182236 -F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f +F src/update.c 8bacc585ae5d6a85b0944934bed5d445f4a872ef +F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9 F src/vacuum.c d9c5759f4c5a438bb43c2086f72c5d2edabc36c8 -F src/vdbe.c 08a46d04e5bdfd95884664eeeb8fbe5e4de426ee +F src/vdbe.c 8ebe0cbc71f99b7a6fc6c49d92b555d0b96f3717 F src/vdbe.h 7aa3ab6210a68471c8490dedfc9aa4ef5684b9a0 F src/vdbeInt.h 9bf236dc477fb8f1f54b078cf80064663f8a409a F src/vdbeapi.c ae87d198eb3ce67c02886601baee0fd478092076 F src/vdbeaux.c 20ec92cdc43374b6537fbd7bc2cf7a28967bbe44 F src/vdbeblob.c 1268e0bcb8e21fa32520b0fc376e1bcdfaa0c642 -F src/vdbemem.c 61d5ddb8e4d4e14f625062bf5bcc7ce08bb20af3 +F src/vdbemem.c 4511e1d2304a7d7916d14be20080036331740fcf F src/vdbesort.c 3937e06b2a0e354500e17dc206ef4c35770a5017 F src/vdbetrace.c e7ec40e1999ff3c6414424365d5941178966dcbc F src/vtab.c 165ce0e797c2cd23badb104c9f2ae9042d6d942c F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 -F src/where.c 6e718c39d6b2964f15f6c96ce5938b4652e3538e +F src/where.c cad6497f79307c2c94aaf72e64866bb33cf99d59 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783 -F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748 +F test/alter.test 7e771c3c3f401198557dbbcf4a2c21950ba934f3 F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d -F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5 +F test/alter4.test 8e93bf7a7e6919b14b0c9a6c1e4908bcf21b0165 F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc -F test/analyze.test f8ab7d15858b4093b06caf5e57e2a5ff7104bdae -F test/analyze3.test 53cfd07625d110e70b92b57a6ff656ea844dfbee +F test/analyze.test 1772936d66471c65221e437b6d1999c3a03166c4 +F test/analyze3.test 412f690dfe95b337475e3e78a84a85d25f6f125d F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213 -F test/analyze5.test 3e57f192307be931f9ab2f6ff456f9063358ac77 -F test/analyze6.test cdbf9887d40ab41301f570fe85c6e1525dd3b1c9 -F test/analyze7.test 7de3ab66e1981303e783102a012d62cb876bf1d5 -F test/analyze8.test ea4972c76820ac8d6a0754e6f5b851292f0f5a61 +F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4 +F test/analyze6.test 19151da2c4e918905d2081b74ac5c4d47fc850ab +F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f +F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88 +F test/analyze9.test 3095a9ebfea4a2b1f9db60375320ae7f219595ba +F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944 F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7 @@ -333,7 +335,7 @@ F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966 F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0 -F test/auth.test 4a4c3b034fff7750513520defa910f376c96ab49 +F test/auth.test 9bea29041871807d9f289ee679d05d3ed103642f F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf @@ -425,7 +427,7 @@ F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8 F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47 F test/date.test f3228180c87bbe5d39c9397bf001c0095c3821b9 -F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b +F test/dbstatus.test aee30c3f337e6c217ff06df59fb8fe6e6448dce2 F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2 F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701 @@ -604,7 +606,7 @@ F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6 F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7 F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026 F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33 -F test/index6.test 6a754747f444fecb7f4ae50ce4ac7a0d269e090d +F test/index6.test e96324d8c1ade4b30a4c6cee14b1fc2e5a367cda F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 @@ -663,7 +665,7 @@ F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151 F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d F test/malloc9.test 2307c6ee3703b0a21391f3ea92388b4b73f9105e -F test/mallocA.test 47006c8d70f29b030652e251cb9d35ba60289198 +F test/mallocA.test 71e4b57e640c017cf2833e51fe6e8e43e8575b73 F test/mallocAll.test 98f1be74bc9f49a858bc4f361fc58e26486798be F test/mallocB.test bc475ab850cda896142ab935bbfbc74c24e51ed6 F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4 @@ -728,7 +730,7 @@ F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54 -F test/permutations.test 742b8005bb3c782797a20beccdbe213ef52531fb +F test/permutations.test f71ed33a1e9939ea6e2cde406afedfc46a958088 F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178 F test/pragma2.test 224f0381f9411a78ae685cac24c13656a62021b7 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 @@ -797,7 +799,7 @@ F test/shell1.test 928547277d385038c696428e9d791cbbad098974 F test/shell2.test 037d6ad16e873354195d30bb2dc4b5321788154a F test/shell3.test 9196c42772d575685e722c92b4b39053c6ebba59 F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9 -F test/shell5.test 946e620a41b64f90d45dcf91c36e8d408d435cfd +F test/shell5.test bfa21ecc173adcbc15db2c075baa468778f67f88 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868 F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329 @@ -824,13 +826,13 @@ F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 F test/syscall.test a653783d985108c4912cc64d341ffbbb55ad2806 F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6 -F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2 +F test/table.test 30423211108121884588d24d6776c7f38702ad7b F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tclsqlite.test a7308276aad2e6c0bfb5b0414424dd0d9cc0cad7 F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d -F test/tester.tcl eea7b3220ca0dd4b49736dfad48c9b867aa58b46 +F test/tester.tcl 282c1a6b541bd2518a28030884073d1b6dfe98ba F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -872,6 +874,7 @@ F test/tkt-868145d012.test a5f941107ece6a64410ca4755c6329b7eb57a356 F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5 F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71 F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67 +F test/tkt-9f2eb3abac.test 85bc63e749f050e6a61c8f9207f1eee65c9d3395 F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4 F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 @@ -879,7 +882,7 @@ F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3 F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898 F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447 -F test/tkt-cbd054fa6b.test 9c27ed07b333eed458e5d4543f91ecdcf05aeb19 +F test/tkt-cbd054fa6b.test 06ccd57af3c0c7895d0f7dc844f13c51f8258885 F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09 F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30 @@ -1062,7 +1065,7 @@ F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8 F test/where8.test 6f95896633cf2d307b5263145b942b7d33e837c6 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 -F test/where9.test 9a7fda4a4512abc26a855e8b2b6572b200f6019b +F test/where9.test 4f3eab951353a3ae164befc521c777dfa903e46c F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a @@ -1072,6 +1075,7 @@ F test/whereF.test 136a7301512d72a08a272806c8767066311b7bc1 F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 +F test/win32longpath.test e2aafc07e6990fe86c69be22a3d1a0e210cd329b F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd F tool/build-all-msvc.bat c55f64ca200308fb5fa5c1ee751ea95a13977b5a x @@ -1120,7 +1124,7 @@ F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/wherecosttest.c f407dc4c79786982a475261866a161cd007947ae F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac -P b7e4dd889d37c8f57c2d3c7900e802f644aac3ea ed310201628cf79c0f57674ae7478ee6738b1c6e -R 4028fc1f369293594c0fe8d5f10d386c -U dan -Z 6b7f4d8151d5a58f275c430cbe09ca16 +P 6cc54de88bba00e07fac9de36caac216d94bf070 d4b6ad3333cc3bad500c2ebf7a6ea552b6762b69 +R d689ec9368fec2d3c01e607f2975aafd +U drh +Z 0eee6dedcb9d483d45a308318f1cb363 diff --git a/manifest.uuid b/manifest.uuid index 710b102279..bc88aac0d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6cc54de88bba00e07fac9de36caac216d94bf070 \ No newline at end of file +e7ebc8f74fe91dee26f952fdf49e427b45448667 \ No newline at end of file diff --git a/sqlite3.pc.in b/sqlite3.pc.in index c8d0aa9d5d..3799671e61 100644 --- a/sqlite3.pc.in +++ b/sqlite3.pc.in @@ -7,7 +7,7 @@ includedir=@includedir@ Name: SQLite Description: SQL database engine -Version: @RELEASE@ +Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lsqlite3 Libs.private: @LIBS@ Cflags: -I${includedir} diff --git a/src/alter.c b/src/alter.c index a49d3349d7..9d34b07b0c 100644 --- a/src/alter.c +++ b/src/alter.c @@ -687,7 +687,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ ** can handle (i.e. not CURRENT_TIME etc.) */ if( pDflt ){ - sqlite3_value *pVal; + sqlite3_value *pVal = 0; if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){ db->mallocFailed = 1; return; diff --git a/src/analyze.c b/src/analyze.c index d25a9b196c..46ebac4c3a 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -1,5 +1,5 @@ /* -** 2005 July 8 +** 2005-07-08 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -20,6 +20,7 @@ ** CREATE TABLE sqlite_stat1(tbl, idx, stat); ** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample); ** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample); +** CREATE TABLE sqlite_stat4(tbl, idx, nEq, nLt, nDLt, sample); ** ** Additional tables might be added in future releases of SQLite. ** The sqlite_stat2 table is not created or used unless the SQLite version @@ -27,8 +28,15 @@ ** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated. ** The sqlite_stat2 table is superseded by sqlite_stat3, which is only ** created and used by SQLite versions 3.7.9 and later and with -** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3 -** is a superset of sqlite_stat2. +** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3 +** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced +** version of sqlite_stat3 and is only available when compiled with +** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.0 and later. It is +** not possible to enable both STAT3 and STAT4 at the same time. If they +** are both enabled, then STAT4 is precedence. +** +** For most applications, sqlite_stat1 provides all the statisics required +** for the query planner to make good choices. ** ** Format of sqlite_stat1: ** @@ -36,7 +44,8 @@ ** name in the idx column. The tbl column is the name of the table to ** which the index belongs. In each such row, the stat column will be ** a string consisting of a list of integers. The first integer in this -** list is the number of rows in the index and in the table. The second +** list is the number of rows in the index. (This is the same as the +** number of rows in the table, except for partial indices.) The second ** integer is the average number of rows in the index that have the same ** value in the first column of the index. The third integer is the average ** number of rows in the index that have the same value for the first two @@ -83,54 +92,82 @@ ** ** Format for sqlite_stat3: ** -** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is -** used to avoid compatibility problems. +** The sqlite_stat3 format is a subset of sqlite_stat4. Hence, the +** sqlite_stat4 format will be described first. Further information +** about sqlite_stat3 follows the sqlite_stat4 description. ** -** The format of the sqlite_stat3 table is similar to the format of -** the sqlite_stat2 table. There are multiple entries for each index. +** Format for sqlite_stat4: +** +** As with sqlite_stat2, the sqlite_stat4 table contains histogram data +** to aid the query planner in choosing good indices based on the values +** that indexed columns are compared against in the WHERE clauses of +** queries. +** +** The sqlite_stat4 table contains multiple entries for each index. ** The idx column names the index and the tbl column is the table of the ** index. If the idx and tbl columns are the same, then the sample is -** of the INTEGER PRIMARY KEY. The sample column is a value taken from -** the left-most column of the index. The nEq column is the approximate -** number of entires in the index whose left-most column exactly matches -** the sample. nLt is the approximate number of entires whose left-most -** column is less than the sample. The nDLt column is the approximate -** number of distinct left-most entries in the index that are less than -** the sample. +** of the INTEGER PRIMARY KEY. The sample column is a blob which is the +** binary encoding of a key from the index, with the trailing rowid +** omitted. The nEq column is a list of integers. The first integer +** is the approximate number of entries in the index whose left-most +** column exactly matches the left-most column of the sample. The second +** integer in nEq is the approximate number of entries in the index where +** the first two columns match the first two columns of the sample. +** And so forth. nLt is another list of integers that show the approximate +** number of entries that are strictly less than the sample. The first +** integer in nLt contains the number of entries in the index where the +** left-most column is less than the left-most column of the sample. +** The K-th integer in the nLt entry is the number of index entries +** where the first K columns are less than the first K columns of the +** sample. The nDLt column is like nLt except that it contains the +** number of distinct entries in the index that are less than the +** sample. ** -** Future versions of SQLite might change to store a string containing -** multiple integers values in the nDLt column of sqlite_stat3. The first -** integer will be the number of prior index entires that are distinct in -** the left-most column. The second integer will be the number of prior index -** entries that are distinct in the first two columns. The third integer -** will be the number of prior index entries that are distinct in the first -** three columns. And so forth. With that extension, the nDLt field is -** similar in function to the sqlite_stat1.stat field. -** -** There can be an arbitrary number of sqlite_stat3 entries per index. -** The ANALYZE command will typically generate sqlite_stat3 tables +** There can be an arbitrary number of sqlite_stat4 entries per index. +** The ANALYZE command will typically generate sqlite_stat4 tables ** that contain between 10 and 40 samples which are distributed across ** the key space, though not uniformly, and which include samples with -** largest possible nEq values. +** large nEq values. +** +** Format for sqlite_stat3 redux: +** +** The sqlite_stat3 table is like sqlite_stat4 except that it only +** looks at the left-most column of the index. The sqlite_stat3.sample +** column contains the actual value of the left-most column instead +** of a blob encoding of the complete index key as is found in +** sqlite_stat4.sample. The nEq, nLt, and nDLt entries of sqlite_stat3 +** all contain just a single integer which is the same as the first +** integer in the equivalent columns in sqlite_stat4. */ #ifndef SQLITE_OMIT_ANALYZE #include "sqliteInt.h" +#if defined(SQLITE_ENABLE_STAT4) +# define IsStat4 1 +# define IsStat3 0 +#elif defined(SQLITE_ENABLE_STAT3) +# define IsStat4 0 +# define IsStat3 1 +#else +# define IsStat4 0 +# define IsStat3 0 +# undef SQLITE_STAT4_SAMPLES +# define SQLITE_STAT4_SAMPLES 1 +#endif +#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */ + /* -** This routine generates code that opens the sqlite_stat1 table for -** writing with cursor iStatCur. If the library was built with the -** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is -** opened for writing using cursor (iStatCur+1) +** This routine generates code that opens the sqlite_statN tables. +** The sqlite_stat1 table is always relevant. sqlite_stat2 is now +** obsolete. sqlite_stat3 and sqlite_stat4 are only opened when +** appropriate compile-time options are provided. ** -** If the sqlite_stat1 tables does not previously exist, it is created. -** Similarly, if the sqlite_stat3 table does not exist and the library -** is compiled with SQLITE_ENABLE_STAT3 defined, it is created. +** If the sqlite_statN tables do not previously exist, it is created. ** ** Argument zWhere may be a pointer to a buffer containing a table name, ** or it may be a NULL pointer. If it is not NULL, then all entries in -** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated -** with the named table are deleted. If zWhere==0, then code is generated -** to delete all stat table entries. +** the sqlite_statN tables associated with the named table are deleted. +** If zWhere==0, then code is generated to delete all stat table entries. */ static void openStatTable( Parse *pParse, /* Parsing context */ @@ -144,18 +181,24 @@ static void openStatTable( const char *zCols; } aTable[] = { { "sqlite_stat1", "tbl,idx,stat" }, -#ifdef SQLITE_ENABLE_STAT3 +#if defined(SQLITE_ENABLE_STAT4) + { "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" }, + { "sqlite_stat3", 0 }, +#elif defined(SQLITE_ENABLE_STAT3) { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" }, + { "sqlite_stat4", 0 }, +#else + { "sqlite_stat3", 0 }, + { "sqlite_stat4", 0 }, #endif }; - - int aRoot[] = {0, 0}; - u8 aCreateTbl[] = {0, 0}; - int i; sqlite3 *db = pParse->db; Db *pDb; Vdbe *v = sqlite3GetVdbe(pParse); + int aRoot[ArraySize(aTable)]; + u8 aCreateTbl[ArraySize(aTable)]; + if( v==0 ) return; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3VdbeDb(v)==db ); @@ -168,34 +211,39 @@ static void openStatTable( const char *zTab = aTable[i].zName; Table *pStat; if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){ - /* The sqlite_stat[12] table does not exist. Create it. Note that a - ** side-effect of the CREATE TABLE statement is to leave the rootpage - ** of the new table in register pParse->regRoot. This is important - ** because the OpenWrite opcode below will be needing it. */ - sqlite3NestedParse(pParse, - "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols - ); - aRoot[i] = pParse->regRoot; - aCreateTbl[i] = OPFLAG_P2ISREG; + if( aTable[i].zCols ){ + /* The sqlite_statN table does not exist. Create it. Note that a + ** side-effect of the CREATE TABLE statement is to leave the rootpage + ** of the new table in register pParse->regRoot. This is important + ** because the OpenWrite opcode below will be needing it. */ + sqlite3NestedParse(pParse, + "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols + ); + aRoot[i] = pParse->regRoot; + aCreateTbl[i] = OPFLAG_P2ISREG; + } }else{ /* The table already exists. If zWhere is not NULL, delete all entries ** associated with the table zWhere. If zWhere is NULL, delete the ** entire contents of the table. */ aRoot[i] = pStat->tnum; + aCreateTbl[i] = 0; sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); if( zWhere ){ sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere + "DELETE FROM %Q.%s WHERE %s=%Q", + pDb->zName, zTab, zWhereType, zWhere ); }else{ - /* The sqlite_stat[12] table already exists. Delete all rows. */ + /* The sqlite_stat[134] table already exists. Delete all rows. */ sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); } } } - /* Open the sqlite_stat[13] tables for writing. */ - for(i=0; ia[0])*mxSample; - p = sqlite3MallocZero( n ); + nCol = sqlite3_value_int(argv[0]); + assert( nCol>1 ); /* >1 because it includes the rowid column */ + nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol; + + /* Allocate the space required for the Stat4Accum object */ + n = sizeof(*p) + + sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */ + + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */ + + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */ + + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample) +#endif + ; + p = sqlite3MallocZero(n); if( p==0 ){ sqlite3_result_error_nomem(context); return; } - p->a = (struct Stat3Sample*)&p[1]; - p->nRow = nRow; - p->mxSample = mxSample; - p->nPSample = p->nRow/(mxSample/3+1) + 1; - sqlite3_randomness(sizeof(p->iPrn), &p->iPrn); + + p->nRow = 0; + p->nCol = nCol; + p->current.anDLt = (tRowcnt*)&p[1]; + p->current.anEq = &p->current.anDLt[nColUp]; + +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + { + u8 *pSpace; /* Allocated space not yet assigned */ + int i; /* Used to iterate through p->aSample[] */ + + p->iGet = -1; + p->mxSample = mxSample; + p->nPSample = sqlite3_value_int64(argv[1])/(mxSample/3+1) + 1; + p->current.anLt = &p->current.anEq[nColUp]; + sqlite3_randomness(sizeof(p->iPrn), &p->iPrn); + + /* Set up the Stat4Accum.a[] and aBest[] arrays */ + p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; + p->aBest = &p->a[mxSample]; + pSpace = (u8*)(&p->a[mxSample+nCol]); + for(i=0; i<(mxSample+nCol); i++){ + p->a[i].anEq = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp); + p->a[i].anLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp); + p->a[i].anDLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp); + } + assert( (pSpace - (u8*)p)==n ); + + for(i=0; iaBest[i].iCol = i; + } + } +#endif + + /* Return a pointer to the allocated object to the caller */ sqlite3_result_blob(context, p, sizeof(p), sqlite3_free); } -static const FuncDef stat3InitFuncdef = { - 2, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - stat3Init, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "stat3_init", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ +static const FuncDef statInitFuncdef = { + 1+IsStat34, /* nArg */ + SQLITE_UTF8, /* iPrefEnc */ + 0, /* flags */ + 0, /* pUserData */ + 0, /* pNext */ + statInit, /* xFunc */ + 0, /* xStep */ + 0, /* xFinalize */ + "stat_init", /* zName */ + 0, /* pHash */ + 0 /* pDestructor */ }; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +/* +** Return true if pNew is to be preferred over pOld. +*/ +static int sampleIsBetter(Stat4Sample *pNew, Stat4Sample *pOld){ + tRowcnt nEqNew = pNew->anEq[pNew->iCol]; + tRowcnt nEqOld = pOld->anEq[pOld->iCol]; + + assert( pOld->isPSample==0 && pNew->isPSample==0 ); + assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) ); + + if( (nEqNew>nEqOld) + || (nEqNew==nEqOld && pNew->iColiCol) + || (nEqNew==nEqOld && pNew->iCol==pOld->iCol && pNew->iHash>pOld->iHash) + ){ + return 1; + } + return 0; +} /* -** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The -** arguments describe a single key instance. This routine makes the -** decision about whether or not to retain this key for the sqlite_stat3 -** table. -** -** The return value is NULL. +** Copy the contents of object (*pFrom) into (*pTo). */ -static void stat3Push( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]); - tRowcnt nEq = sqlite3_value_int64(argv[0]); - tRowcnt nLt = sqlite3_value_int64(argv[1]); - tRowcnt nDLt = sqlite3_value_int64(argv[2]); - i64 rowid = sqlite3_value_int64(argv[3]); - u8 isPSample = 0; - u8 doInsert = 0; - int iMin = p->iMin; - struct Stat3Sample *pSample; +void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ + pTo->iRowid = pFrom->iRowid; + pTo->isPSample = pFrom->isPSample; + pTo->iCol = pFrom->iCol; + pTo->iHash = pFrom->iHash; + memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol); + memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol); + memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol); +} + +/* +** Copy the contents of sample *pNew into the p->a[] array. If necessary, +** remove the least desirable sample from p->a[] to make room. +*/ +static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ + Stat4Sample *pSample; int i; - u32 h; + i64 iSeq; + i64 iPos; - UNUSED_PARAMETER(context); - UNUSED_PARAMETER(argc); - if( nEq==0 ) return; - h = p->iPrn = p->iPrn*1103515245 + 12345; - if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){ - doInsert = isPSample = 1; - }else if( p->nSamplemxSample ){ - doInsert = 1; - }else{ - if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){ - doInsert = 1; - } - } - if( !doInsert ) return; - if( p->nSample==p->mxSample ){ - assert( p->nSample - iMin - 1 >= 0 ); - memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1)); - pSample = &p->a[p->nSample-1]; - }else{ - pSample = &p->a[p->nSample++]; - } - pSample->iRowid = rowid; - pSample->nEq = nEq; - pSample->nLt = nLt; - pSample->nDLt = nDLt; - pSample->iHash = h; - pSample->isPSample = isPSample; + assert( IsStat4 || nEqZero==0 ); - /* Find the new minimum */ - if( p->nSample==p->mxSample ){ - pSample = p->a; - i = 0; - while( pSample->isPSample ){ - i++; - pSample++; - assert( inSample ); - } - nEq = pSample->nEq; - h = pSample->iHash; - iMin = i; - for(i++, pSample++; inSample; i++, pSample++){ - if( pSample->isPSample ) continue; - if( pSample->nEqnEq==nEq && pSample->iHashnEq; - h = pSample->iHash; + if( pNew->isPSample==0 ){ + Stat4Sample *pUpgrade = 0; + assert( pNew->anEq[pNew->iCol]>0 ); + + /* This sample is being added because the prefix that ends in column + ** iCol occurs many times in the table. However, if we have already + ** added a sample that shares this prefix, there is no need to add + ** this one. Instead, upgrade the priority of the highest priority + ** existing sample that shares this prefix. */ + for(i=p->nSample-1; i>=0; i--){ + Stat4Sample *pOld = &p->a[i]; + if( pOld->anEq[pNew->iCol]==0 ){ + if( pOld->isPSample ) return; + assert( sampleIsBetter(pNew, pOld) ); + if( pUpgrade==0 || sampleIsBetter(pOld, pUpgrade) ){ + pUpgrade = pOld; + } } } + if( pUpgrade ){ + pUpgrade->iCol = pNew->iCol; + pUpgrade->anEq[pUpgrade->iCol] = pNew->anEq[pUpgrade->iCol]; + goto find_new_min; + } + } + + /* If necessary, remove sample iMin to make room for the new sample. */ + if( p->nSample>=p->mxSample ){ + Stat4Sample *pMin = &p->a[p->iMin]; + tRowcnt *anEq = pMin->anEq; + tRowcnt *anLt = pMin->anLt; + tRowcnt *anDLt = pMin->anDLt; + memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1)); + pSample = &p->a[p->nSample-1]; + pSample->anEq = anEq; + pSample->anDLt = anDLt; + pSample->anLt = anLt; + p->nSample = p->mxSample-1; + } + + /* Figure out where in the a[] array the new sample should be inserted. */ + iSeq = pNew->anLt[p->nCol-1]; + for(iPos=p->nSample; iPos>0; iPos--){ + if( iSeq>p->a[iPos-1].anLt[p->nCol-1] ) break; + } + + /* Insert the new sample */ + pSample = &p->a[iPos]; + if( iPos!=p->nSample ){ + Stat4Sample *pEnd = &p->a[p->nSample]; + tRowcnt *anEq = pEnd->anEq; + tRowcnt *anLt = pEnd->anLt; + tRowcnt *anDLt = pEnd->anDLt; + memmove(&p->a[iPos], &p->a[iPos+1], (p->nSample-iPos)*sizeof(p->a[0])); + pSample->anEq = anEq; + pSample->anDLt = anDLt; + pSample->anLt = anLt; + } + p->nSample++; + sampleCopy(p, pSample, pNew); + + /* Zero the first nEqZero entries in the anEq[] array. */ + memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero); + + find_new_min: + if( p->nSample>=p->mxSample ){ + int iMin = -1; + for(i=0; imxSample; i++){ + if( p->a[i].isPSample ) continue; + if( iMin<0 || sampleIsBetter(&p->a[iMin], &p->a[i]) ){ + iMin = i; + } + } + assert( iMin>=0 ); p->iMin = iMin; } } -static const FuncDef stat3PushFuncdef = { - 5, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - stat3Push, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "stat3_push", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ -}; +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ /* -** Implementation of the stat3_get(P,N,...) SQL function. This routine is -** used to query the results. Content is returned for the Nth sqlite_stat3 -** row where N is between 0 and S-1 and S is the number of samples. The -** value returned depends on the number of arguments. -** -** argc==2 result: rowid -** argc==3 result: nEq -** argc==4 result: nLt -** argc==5 result: nDLt +** Field iChng of the index being scanned has changed. So at this point +** p->current contains a sample that reflects the previous row of the +** index. The value of anEq[iChng] and subsequent anEq[] elements are +** correct at this point. */ -static void stat3Get( +static void samplePushPrevious(Stat4Accum *p, int iChng){ +#ifdef SQLITE_ENABLE_STAT4 + int i; + + /* Check if any samples from the aBest[] array should be pushed + ** into IndexSample.a[] at this point. */ + for(i=(p->nCol-2); i>=iChng; i--){ + Stat4Sample *pBest = &p->aBest[i]; + if( p->nSamplemxSample + || sampleIsBetter(pBest, &p->a[p->iMin]) + ){ + sampleInsert(p, pBest, i); + } + } + + /* Update the anEq[] fields of any samples already collected. */ + for(i=p->nSample-1; i>=0; i--){ + int j; + for(j=iChng; jnCol; j++){ + if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j]; + } + } +#endif + +#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4) + if( iChng==0 ){ + tRowcnt nLt = p->current.anLt[0]; + tRowcnt nEq = p->current.anEq[0]; + + /* Check if this is to be a periodic sample. If so, add it. */ + if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){ + p->current.isPSample = 1; + sampleInsert(p, &p->current, 0); + p->current.isPSample = 0; + }else + + /* Or if it is a non-periodic sample. Add it in this case too. */ + if( p->nSamplemxSample || sampleIsBetter(&p->current, &p->a[p->iMin]) ){ + sampleInsert(p, &p->current, 0); + } + } +#endif +} + +/* +** Implementation of the stat_push SQL function: stat_push(P,R,C) +** Arguments: +** +** P Pointer to the Stat4Accum object created by stat_init() +** C Index of left-most column to differ from previous row +** R Rowid for the current row +** +** The SQL function always returns NULL. +** +** The R parameter is only used for STAT3 and STAT4. +*/ +static void statPush( sqlite3_context *context, int argc, sqlite3_value **argv ){ - int n = sqlite3_value_int(argv[1]); - Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]); + int i; - assert( p!=0 ); - if( p->nSample<=n ) return; - switch( argc ){ - case 2: sqlite3_result_int64(context, p->a[n].iRowid); break; - case 3: sqlite3_result_int64(context, p->a[n].nEq); break; - case 4: sqlite3_result_int64(context, p->a[n].nLt); break; - default: sqlite3_result_int64(context, p->a[n].nDLt); break; + /* The three function arguments */ + Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]); + int iChng = sqlite3_value_int(argv[1]); + + assert( p->nCol>1 ); /* Includes rowid field */ + assert( iChngnCol ); + + if( p->nRow==0 ){ + /* anEq[0] is only zero for the very first call to this function. Do + ** appropriate initialization */ + for(i=0; inCol; i++) p->current.anEq[i] = 1; + }else{ + /* Second and subsequent calls get processed here */ + samplePushPrevious(p, iChng); + + /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply + ** to the current row of the index. */ + for(i=0; icurrent.anEq[i]++; + } + for(i=iChng; inCol; i++){ + p->current.anDLt[i]++; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + p->current.anLt[i] += p->current.anEq[i]; +#endif + p->current.anEq[i] = 1; + } } + p->nRow++; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + p->current.iRowid = sqlite3_value_int64(argv[2]); + p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; +#endif + +#ifdef SQLITE_ENABLE_STAT4 + { + tRowcnt nLt = p->current.anLt[p->nCol-1]; + + /* Check if this is to be a periodic sample. If so, add it. */ + if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ + p->current.isPSample = 1; + p->current.iCol = 0; + sampleInsert(p, &p->current, p->nCol-1); + p->current.isPSample = 0; + } + + /* Update the aBest[] array. */ + for(i=0; i<(p->nCol-1); i++){ + p->current.iCol = i; + if( i>=iChng || sampleIsBetter(&p->current, &p->aBest[i]) ){ + sampleCopy(p, &p->aBest[i], &p->current); + } + } + } +#endif } -static const FuncDef stat3GetFuncdef = { - -1, /* nArg */ - SQLITE_UTF8, /* iPrefEnc */ - 0, /* flags */ - 0, /* pUserData */ - 0, /* pNext */ - stat3Get, /* xFunc */ - 0, /* xStep */ - 0, /* xFinalize */ - "stat3_get", /* zName */ - 0, /* pHash */ - 0 /* pDestructor */ +static const FuncDef statPushFuncdef = { + 2+IsStat34, /* nArg */ + SQLITE_UTF8, /* iPrefEnc */ + 0, /* flags */ + 0, /* pUserData */ + 0, /* pNext */ + statPush, /* xFunc */ + 0, /* xStep */ + 0, /* xFinalize */ + "stat_push", /* zName */ + 0, /* pHash */ + 0 /* pDestructor */ }; -#endif /* SQLITE_ENABLE_STAT3 */ +#define STAT_GET_STAT1 0 /* "stat" column of stat1 table */ +#define STAT_GET_ROWID 1 /* "rowid" column of stat[34] entry */ +#define STAT_GET_NEQ 2 /* "neq" column of stat[34] entry */ +#define STAT_GET_NLT 3 /* "nlt" column of stat[34] entry */ +#define STAT_GET_NDLT 4 /* "ndlt" column of stat[34] entry */ +/* +** Implementation of the stat_get(P,J) SQL function. This routine is +** used to query the results. Content is returned for parameter J +** which is one of the STAT_GET_xxxx values defined above. +** +** If neither STAT3 nor STAT4 are enabled, then J is always +** STAT_GET_STAT1 and is hence omitted and this routine becomes +** a one-parameter function, stat_get(P), that always returns the +** stat1 table entry information. +*/ +static void statGet( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + /* STAT3 and STAT4 have a parameter on this routine. */ + int eCall = sqlite3_value_int(argv[1]); + assert( argc==2 ); + assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ + || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT + || eCall==STAT_GET_NDLT + ); + if( eCall==STAT_GET_STAT1 ) +#else + assert( argc==1 ); +#endif + { + /* Return the value to store in the "stat" column of the sqlite_stat1 + ** table for this index. + ** + ** The value is a string composed of a list of integers describing + ** the index. The first integer in the list is the total number of + ** entries in the index. There is one additional integer in the list + ** for each indexed column. This additional integer is an estimate of + ** the number of rows matched by a stabbing query on the index using + ** a key with the corresponding number of fields. In other words, + ** if the index is on columns (a,b) and the sqlite_stat1 value is + ** "100 10 2", then SQLite estimates that: + ** + ** * the index contains 100 rows, + ** * "WHERE a=?" matches 10 rows, and + ** * "WHERE a=? AND b=?" matches 2 rows. + ** + ** If D is the count of distinct values and K is the total number of + ** rows, then each estimate is computed as: + ** + ** I = (K+D-1)/D + */ + char *z; + int i; + char *zRet = sqlite3MallocZero(p->nCol * 25); + if( zRet==0 ){ + sqlite3_result_error_nomem(context); + return; + } + + sqlite3_snprintf(24, zRet, "%lld", p->nRow); + z = zRet + sqlite3Strlen30(zRet); + for(i=0; i<(p->nCol-1); i++){ + i64 nDistinct = p->current.anDLt[i] + 1; + i64 iVal = (p->nRow + nDistinct - 1) / nDistinct; + sqlite3_snprintf(24, z, " %lld", iVal); + z += sqlite3Strlen30(z); + assert( p->current.anEq[i] ); + } + assert( z[0]=='\0' && z>zRet ); + + sqlite3_result_text(context, zRet, -1, sqlite3_free); + } +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + else if( eCall==STAT_GET_ROWID ){ + if( p->iGet<0 ){ + samplePushPrevious(p, 0); + p->iGet = 0; + } + if( p->iGetnSample ){ + sqlite3_result_int64(context, p->a[p->iGet].iRowid); + } + }else{ + tRowcnt *aCnt = 0; + + assert( p->iGetnSample ); + switch( eCall ){ + case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break; + case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break; + default: { + aCnt = p->a[p->iGet].anDLt; + p->iGet++; + break; + } + } + + if( IsStat3 ){ + sqlite3_result_int64(context, (i64)aCnt[0]); + }else{ + char *zRet = sqlite3MallocZero(p->nCol * 25); + if( zRet==0 ){ + sqlite3_result_error_nomem(context); + }else{ + int i; + char *z = zRet; + for(i=0; inCol; i++){ + sqlite3_snprintf(24, z, "%lld ", aCnt[i]); + z += sqlite3Strlen30(z); + } + assert( z[0]=='\0' && z>zRet ); + z[-1] = '\0'; + sqlite3_result_text(context, zRet, -1, sqlite3_free); + } + } + } +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ +} +static const FuncDef statGetFuncdef = { + 1+IsStat34, /* nArg */ + SQLITE_UTF8, /* iPrefEnc */ + 0, /* flags */ + 0, /* pUserData */ + 0, /* pNext */ + statGet, /* xFunc */ + 0, /* xStep */ + 0, /* xFinalize */ + "stat_get", /* zName */ + 0, /* pHash */ + 0 /* pDestructor */ +}; + +static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){ + assert( regOut!=regStat4 && regOut!=regStat4+1 ); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1); +#else + assert( iParam==STAT_GET_STAT1 ); +#endif + sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4, regOut); + sqlite3VdbeChangeP4(v, -1, (char*)&statGetFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 1 + IsStat34); +} /* ** Generate code to do an analysis of all indices associated with @@ -430,42 +803,31 @@ static void analyzeOneTable( Table *pTab, /* Table whose indices are to be analyzed */ Index *pOnlyIdx, /* If not NULL, only analyze this one index */ int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ - int iMem /* Available memory locations begin here */ + int iMem, /* Available memory locations begin here */ + int iTab /* Next available cursor */ ){ sqlite3 *db = pParse->db; /* Database handle */ Index *pIdx; /* An index to being analyzed */ int iIdxCur; /* Cursor open on index being analyzed */ + int iTabCur; /* Table cursor */ Vdbe *v; /* The virtual machine being built up */ int i; /* Loop counter */ - int topOfLoop; /* The top of the loop */ - int endOfLoop; /* The end of the loop */ int jZeroRows = -1; /* Jump from here if number of rows is zero */ int iDb; /* Index of database containing pTab */ u8 needTableCnt = 1; /* True to count the table */ + int regNewRowid = iMem++; /* Rowid for the inserted record */ + int regStat4 = iMem++; /* Register to hold Stat4Accum object */ + int regChng = iMem++; /* Index of changed index field */ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + int regRowid = iMem++; /* Rowid argument passed to stat_push() */ +#endif + int regTemp = iMem++; /* Temporary use register */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ - int regStat1 = iMem++; /* The stat column of sqlite_stat1 */ -#ifdef SQLITE_ENABLE_STAT3 - int regNumEq = regStat1; /* Number of instances. Same as regStat1 */ - int regNumLt = iMem++; /* Number of keys less than regSample */ - int regNumDLt = iMem++; /* Number of distinct keys less than regSample */ - int regSample = iMem++; /* The next sample value */ - int regRowid = regSample; /* Rowid of a sample */ - int regAccum = iMem++; /* Register to hold Stat3Accum object */ - int regLoop = iMem++; /* Loop counter */ - int regCount = iMem++; /* Number of rows in the table or index */ - int regTemp1 = iMem++; /* Intermediate register */ - int regTemp2 = iMem++; /* Intermediate register */ - int once = 1; /* One-time initialization */ - int shortJump = 0; /* Instruction address */ - int iTabCur = pParse->nTab++; /* Table cursor */ -#endif - int regCol = iMem++; /* Content of a column in analyzed table */ - int regRec = iMem++; /* Register holding completed record */ - int regTemp = iMem++; /* Temporary use register */ - int regNewRowid = iMem++; /* Rowid for the inserted record */ - + int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */ + int regPrev = iMem; /* MUST BE LAST (see below) */ + pParse->nMem = MAX(pParse->nMem, iMem); v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) ){ return; @@ -489,213 +851,226 @@ static void analyzeOneTable( } #endif - /* Establish a read-lock on the table at the shared-cache level. */ + /* Establish a read-lock on the table at the shared-cache level. + ** Open a read-only cursor on the table. Also allocate a cursor number + ** to use for scanning indexes (iIdxCur). No index cursor is opened at + ** this time though. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); - - iIdxCur = pParse->nTab++; + iTabCur = iTab++; + iIdxCur = iTab++; + pParse->nTab = MAX(pParse->nTab, iTab); + sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int nCol; - KeyInfo *pKey; - int addrIfNot = 0; /* address of OP_IfNot */ - int *aChngAddr; /* Array of jump instruction addresses */ + int nCol; /* Number of columns indexed by pIdx */ + KeyInfo *pKey; /* KeyInfo structure for pIdx */ + int *aGotoChng; /* Array of jump instruction addresses */ + int addrRewind; /* Address of "OP_Rewind iIdxCur" */ + int addrGotoChng0; /* Address of "Goto addr_chng_0" */ + int addrNextRow; /* Address of "next_row:" */ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0; VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName)); nCol = pIdx->nColumn; - aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol); - if( aChngAddr==0 ) continue; + aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*(nCol+1)); + if( aGotoChng==0 ) continue; pKey = sqlite3IndexKeyinfo(pParse, pIdx); - if( iMem+1+(nCol*2)>pParse->nMem ){ - pParse->nMem = iMem+1+(nCol*2); - } - - /* Open a cursor to the index to be analyzed. */ - assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); - sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, - (char *)pKey, P4_KEYINFO_HANDOFF); - VdbeComment((v, "%s", pIdx->zName)); /* Populate the register containing the index name. */ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); -#ifdef SQLITE_ENABLE_STAT3 - if( once ){ - once = 0; - sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); - } - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount); - sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt); - sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum); - sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum, - (char*)&stat3InitFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 2); -#endif /* SQLITE_ENABLE_STAT3 */ - - /* The block of memory cells initialized here is used as follows. + /* + ** Pseudo-code for loop that calls stat_push(): ** - ** iMem: - ** The total number of rows in the table. + ** Rewind csr + ** if eof(csr) goto end_of_scan; + ** regChng = 0 + ** goto chng_addr_0; ** - ** iMem+1 .. iMem+nCol: - ** Number of distinct entries in index considering the - ** left-most N columns only, where N is between 1 and nCol, - ** inclusive. + ** next_row: + ** regChng = 0 + ** if( idx(0) != regPrev(0) ) goto chng_addr_0 + ** regChng = 1 + ** if( idx(1) != regPrev(1) ) goto chng_addr_1 + ** ... + ** regChng = N + ** goto chng_addr_N ** - ** iMem+nCol+1 .. Mem+2*nCol: - ** Previous value of indexed columns, from left to right. + ** chng_addr_0: + ** regPrev(0) = idx(0) + ** chng_addr_1: + ** regPrev(1) = idx(1) + ** ... ** - ** Cells iMem through iMem+nCol are initialized to 0. The others are - ** initialized to contain an SQL NULL. + ** chng_addr_N: + ** regRowid = idx(rowid) + ** stat_push(P, regChng, regRowid) + ** Next csr + ** if !eof(csr) goto next_row; + ** + ** end_of_scan: */ - for(i=0; i<=nCol; i++){ - sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i); - } - for(i=0; inMem = MAX(pParse->nMem, regPrev+nCol); - for(i=0; iazColl!=0 ); - assert( pIdx->azColl[i]!=0 ); - pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); - aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1, - (char*)pColl, P4_COLLSEQ); - sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); - VdbeComment((v, "jump if column %d changed", i)); -#ifdef SQLITE_ENABLE_STAT3 - if( i==0 ){ - sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1); - VdbeComment((v, "incr repeat count")); - } + /* Open a read-only cursor on the index being analyzed. */ + assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); + sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + VdbeComment((v, "%s", pIdx->zName)); + + /* Invoke the stat_init() function. The arguments are: + ** + ** (1) the number of columns in the index including the rowid, + ** (2) the number of rows in the index, + ** + ** The second argument is only used for STAT3 and STAT4 + */ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+2); #endif - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); - for(i=0; inColumn, regRowid); - sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt); - sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq); -#endif - } - sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1); - sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1); - } - sqlite3DbFree(db, aChngAddr); + sqlite3VdbeAddOp2(v, OP_Integer, nCol+1, regStat4+1); + sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4+1, regStat4); + sqlite3VdbeChangeP4(v, -1, (char*)&statInitFuncdef, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, 1+IsStat34); - /* Always jump here after updating the iMem+1...iMem+1+nCol counters */ - sqlite3VdbeResolveLabel(v, endOfLoop); - - sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop); - sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); -#ifdef SQLITE_ENABLE_STAT3 - sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2, - (char*)&stat3PushFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 5); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop); - shortJump = - sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 2); - sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1); - sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1); - sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample); - sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 3); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 4); - sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt, - (char*)&stat3GetFuncdef, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, 5); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0); - sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); - sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid); - sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump); - sqlite3VdbeJumpHere(v, shortJump+2); -#endif - - /* Store the results in sqlite_stat1. + /* Implementation of the following: ** - ** The result is a single row of the sqlite_stat1 table. The first - ** two columns are the names of the table and index. The third column - ** is a string composed of a list of integer statistics about the - ** index. The first integer in the list is the total number of entries - ** in the index. There is one additional integer in the list for each - ** column of the table. This additional integer is a guess of how many - ** rows of the table the index will select. If D is the count of distinct - ** values and K is the total number of rows, then the integer is computed - ** as: + ** Rewind csr + ** if eof(csr) goto end_of_scan; + ** regChng = 0 + ** goto next_push_0; ** - ** I = (K+D-1)/D - ** - ** If K==0 then no entry is made into the sqlite_stat1 table. - ** If K>0 then it is always the case the D>0 so division by zero - ** is never possible. */ - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1); - jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); + addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); + addrGotoChng0 = sqlite3VdbeAddOp0(v, OP_Goto); + + /* + ** next_row: + ** regChng = 0 + ** if( idx(0) != regPrev(0) ) goto chng_addr_0 + ** regChng = 1 + ** if( idx(1) != regPrev(1) ) goto chng_addr_1 + ** ... + ** regChng = N + ** goto chng_addr_N + */ + addrNextRow = sqlite3VdbeCurrentAddr(v); for(i=0; iazColl[i]); + sqlite3VdbeAddOp2(v, OP_Integer, i, regChng); + sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp); + aGotoChng[i] = + sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); } - if( pIdx->pPartIdxWhere!=0 ) sqlite3VdbeJumpHere(v, jZeroRows); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); + sqlite3VdbeAddOp2(v, OP_Integer, nCol, regChng); + aGotoChng[nCol] = sqlite3VdbeAddOp0(v, OP_Goto); + + /* + ** chng_addr_0: + ** regPrev(0) = idx(0) + ** chng_addr_1: + ** regPrev(1) = idx(1) + ** ... + */ + sqlite3VdbeJumpHere(v, addrGotoChng0); + for(i=0; ipPartIdxWhere==0 ) sqlite3VdbeJumpHere(v, jZeroRows); + + /* Add the entries to the stat3 or stat4 table. */ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + { + int regEq = regStat1; + int regLt = regStat1+1; + int regDLt = regStat1+2; + int regSample = regStat1+3; + int regCol = regStat1+4; + int regSampleRowid = regCol + nCol; + int addrNext; + int addrIsNull; + + pParse->nMem = MAX(pParse->nMem, regCol+nCol+1); + + addrNext = sqlite3VdbeCurrentAddr(v); + callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid); + addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); + callStatGet(v, regStat4, STAT_GET_NEQ, regEq); + callStatGet(v, regStat4, STAT_GET_NLT, regLt); + callStatGet(v, regStat4, STAT_GET_NDLT, regDLt); + sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, addrNext, regSampleRowid); +#ifdef SQLITE_ENABLE_STAT3 + sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, + pIdx->aiColumn[0], regSample); +#else + for(i=0; iaiColumn[i]; + sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i); + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample); +#endif + sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regTemp, "bbbbbb", 0); + sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeJumpHere(v, addrIsNull); + } +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ + + /* End of analysis */ + sqlite3VdbeJumpHere(v, addrRewind); + sqlite3DbFree(db, aGotoChng); } + /* Create a single sqlite_stat1 entry containing NULL as the index ** name and the row count as the content. */ if( pOnlyIdx==0 && needTableCnt ){ - sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb); VdbeComment((v, "%s", pTab->zName)); - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1); - sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); + sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1); jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); - sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3VdbeJumpHere(v, jZeroRows); } - if( pParse->nMemnMem = regRec; } @@ -719,16 +1094,18 @@ static void analyzeDatabase(Parse *pParse, int iDb){ HashElem *k; int iStatCur; int iMem; + int iTab; sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 3; openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; + iTab = pParse->nTab; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); - analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); + analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); } loadAnalysis(pParse, iDb); } @@ -753,7 +1130,7 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ }else{ openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); } - analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1); + analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur,pParse->nMem+1,pParse->nTab); loadAnalysis(pParse, iDb); } @@ -836,6 +1213,43 @@ struct analysisInfo { const char *zDatabase; }; +/* +** The first argument points to a nul-terminated string containing a +** list of space separated integers. Read the first nOut of these into +** the array aOut[]. +*/ +static void decodeIntArray( + char *zIntArray, + int nOut, + tRowcnt *aOut, + int *pbUnordered +){ + char *z = zIntArray; + int c; + int i; + tRowcnt v; + + assert( pbUnordered==0 || *pbUnordered==0 ); + +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + if( z==0 ) z = ""; +#else + if( NEVER(z==0) ) z = ""; +#endif + for(i=0; *z && i='0' && c<='9' ){ + v = v*10 + c - '0'; + z++; + } + aOut[i] = v; + if( *z==' ' ) z++; + } + if( pbUnordered && strcmp(z, "unordered")==0 ){ + *pbUnordered = 1; + } +} + /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. @@ -851,8 +1265,6 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ analysisInfo *pInfo = (analysisInfo*)pData; Index *pIndex; Table *pTable; - int i, c, n; - tRowcnt v; const char *z; assert( argc==3 ); @@ -870,25 +1282,17 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ }else{ pIndex = 0; } - n = pIndex ? pIndex->nColumn : 0; z = argv[2]; - for(i=0; *z && i<=n; i++){ - v = 0; - while( (c=z[0])>='0' && c<='9' ){ - v = v*10 + c - '0'; - z++; - } - if( i==0 && (pIndex==0 || pIndex->pPartIdxWhere==0) ){ - if( v>0 ) pTable->nRowEst = v; - if( pIndex==0 ) break; - } - pIndex->aiRowEst[i] = v; - if( *z==' ' ) z++; - if( strcmp(z, "unordered")==0 ){ - pIndex->bUnordered = 1; - break; - } + + if( pIndex ){ + int bUnordered = 0; + decodeIntArray((char*)z, pIndex->nColumn+1, pIndex->aiRowEst,&bUnordered); + if( pIndex->pPartIdxWhere==0 ) pTable->nRowEst = pIndex->aiRowEst[0]; + pIndex->bUnordered = bUnordered; + }else{ + decodeIntArray((char*)z, 1, &pTable->nRowEst, 0); } + return 0; } @@ -897,14 +1301,12 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ ** and its contents. */ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( pIdx->aSample ){ int j; for(j=0; jnSample; j++){ IndexSample *p = &pIdx->aSample[j]; - if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ - sqlite3DbFree(db, p->u.z); - } + sqlite3DbFree(db, p->p); } sqlite3DbFree(db, pIdx->aSample); } @@ -915,31 +1317,75 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ #else UNUSED_PARAMETER(db); UNUSED_PARAMETER(pIdx); -#endif +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ } -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 /* -** Load content from the sqlite_stat3 table into the Index.aSample[] -** arrays of all indices. +** Populate the pIdx->aAvgEq[] array based on the samples currently +** stored in pIdx->aSample[]. */ -static int loadStat3(sqlite3 *db, const char *zDb){ +static void initAvgEq(Index *pIdx){ + if( pIdx ){ + IndexSample *aSample = pIdx->aSample; + IndexSample *pFinal = &aSample[pIdx->nSample-1]; + int iCol; + for(iCol=0; iColnColumn; iCol++){ + int i; /* Used to iterate through samples */ + tRowcnt sumEq = 0; /* Sum of the nEq values */ + int nSum = 0; /* Number of terms contributing to sumEq */ + tRowcnt avgEq = 0; + tRowcnt nDLt = pFinal->anDLt[iCol]; + + /* Set nSum to the number of distinct (iCol+1) field prefixes that + ** occur in the stat4 table for this index before pFinal. Set + ** sumEq to the sum of the nEq values for column iCol for the same + ** set (adding the value only once where there exist dupicate + ** prefixes). */ + for(i=0; i<(pIdx->nSample-1); i++){ + if( aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){ + sumEq += aSample[i].anEq[iCol]; + nSum++; + } + } + if( nDLt>nSum ){ + avgEq = (pFinal->anLt[iCol] - sumEq)/(nDLt - nSum); + } + if( avgEq==0 ) avgEq = 1; + pIdx->aAvgEq[iCol] = avgEq; + if( pIdx->nSampleCol==1 ) break; + } + } +} + +/* +** Load the content from either the sqlite_stat4 or sqlite_stat3 table +** into the relevant Index.aSample[] arrays. +** +** Arguments zSql1 and zSql2 must point to SQL statements that return +** data equivalent to the following (statements are different for stat3, +** see the caller of this function for details): +** +** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx +** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4 +** +** where %Q is replaced with the database name before the SQL is executed. +*/ +static int loadStatTbl( + sqlite3 *db, /* Database handle */ + int bStat3, /* Assume single column records only */ + const char *zSql1, /* SQL statement 1 (see above) */ + const char *zSql2, /* SQL statement 2 (see above) */ + const char *zDb /* Database name (e.g. "main") */ +){ int rc; /* Result codes from subroutines */ sqlite3_stmt *pStmt = 0; /* An SQL statement being run */ char *zSql; /* Text of the SQL statement */ Index *pPrevIdx = 0; /* Previous index in the loop */ - int idx = 0; /* slot in pIdx->aSample[] for next sample */ - int eType; /* Datatype of a sample */ IndexSample *pSample; /* A slot in pIdx->aSample[] */ assert( db->lookaside.bEnabled==0 ); - if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){ - return SQLITE_OK; - } - - zSql = sqlite3MPrintf(db, - "SELECT idx,count(*) FROM %Q.sqlite_stat3" - " GROUP BY idx", zDb); + zSql = sqlite3MPrintf(db, zSql1, zDb); if( !zSql ){ return SQLITE_NOMEM; } @@ -948,30 +1394,51 @@ static int loadStat3(sqlite3 *db, const char *zDb){ if( rc ) return rc; while( sqlite3_step(pStmt)==SQLITE_ROW ){ + int nIdxCol = 1; /* Number of columns in stat4 records */ + int nAvgCol = 1; /* Number of entries in Index.aAvgEq */ + char *zIndex; /* Index name */ Index *pIdx; /* Pointer to the index object */ int nSample; /* Number of samples */ + int nByte; /* Bytes of space required */ + int i; /* Bytes of space required */ + tRowcnt *pSpace; zIndex = (char *)sqlite3_column_text(pStmt, 0); if( zIndex==0 ) continue; nSample = sqlite3_column_int(pStmt, 1); pIdx = sqlite3FindIndex(db, zIndex, zDb); - if( pIdx==0 ) continue; - assert( pIdx->nSample==0 ); - pIdx->nSample = nSample; - pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample)); - pIdx->avgEq = pIdx->aiRowEst[1]; + assert( pIdx==0 || bStat3 || pIdx->nSample==0 ); + /* Index.nSample is non-zero at this point if data has already been + ** loaded from the stat4 table. In this case ignore stat3 data. */ + if( pIdx==0 || pIdx->nSample ) continue; + if( bStat3==0 ){ + nIdxCol = pIdx->nColumn+1; + nAvgCol = pIdx->nColumn; + } + pIdx->nSampleCol = nIdxCol; + nByte = sizeof(IndexSample) * nSample; + nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample; + nByte += nAvgCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */ + + pIdx->aSample = sqlite3DbMallocZero(db, nByte); if( pIdx->aSample==0 ){ - db->mallocFailed = 1; sqlite3_finalize(pStmt); return SQLITE_NOMEM; } + pSpace = (tRowcnt*)&pIdx->aSample[nSample]; + pIdx->aAvgEq = pSpace; pSpace += nAvgCol; + for(i=0; iaSample[i].anEq = pSpace; pSpace += nIdxCol; + pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol; + pIdx->aSample[i].anDLt = pSpace; pSpace += nIdxCol; + } + assert( ((u8*)pSpace)-nByte==(u8*)(pIdx->aSample) ); } rc = sqlite3_finalize(pStmt); if( rc ) return rc; - zSql = sqlite3MPrintf(db, - "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb); + zSql = sqlite3MPrintf(db, zSql2, zDb); if( !zSql ){ return SQLITE_NOMEM; } @@ -980,86 +1447,88 @@ static int loadStat3(sqlite3 *db, const char *zDb){ if( rc ) return rc; while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char *zIndex; /* Index name */ - Index *pIdx; /* Pointer to the index object */ - int i; /* Loop counter */ - tRowcnt sumEq; /* Sum of the nEq values */ + char *zIndex; /* Index name */ + Index *pIdx; /* Pointer to the index object */ + int nCol = 1; /* Number of columns in index */ zIndex = (char *)sqlite3_column_text(pStmt, 0); if( zIndex==0 ) continue; pIdx = sqlite3FindIndex(db, zIndex, zDb); if( pIdx==0 ) continue; - if( pIdx==pPrevIdx ){ - idx++; - }else{ + /* This next condition is true if data has already been loaded from + ** the sqlite_stat4 table. In this case ignore stat3 data. */ + nCol = pIdx->nSampleCol; + if( bStat3 && nCol>1 ) continue; + if( pIdx!=pPrevIdx ){ + initAvgEq(pPrevIdx); pPrevIdx = pIdx; - idx = 0; } - assert( idxnSample ); - pSample = &pIdx->aSample[idx]; - pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1); - pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2); - pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3); - if( idx==pIdx->nSample-1 ){ - if( pSample->nDLt>0 ){ - for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq; - pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt; - } - if( pIdx->avgEq<=0 ) pIdx->avgEq = 1; - } - eType = sqlite3_column_type(pStmt, 4); - pSample->eType = (u8)eType; - switch( eType ){ - case SQLITE_INTEGER: { - pSample->u.i = sqlite3_column_int64(pStmt, 4); - break; - } - case SQLITE_FLOAT: { - pSample->u.r = sqlite3_column_double(pStmt, 4); - break; - } - case SQLITE_NULL: { - break; - } - default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); { - const char *z = (const char *)( - (eType==SQLITE_BLOB) ? - sqlite3_column_blob(pStmt, 4): - sqlite3_column_text(pStmt, 4) - ); - int n = z ? sqlite3_column_bytes(pStmt, 4) : 0; - pSample->nByte = n; - if( n < 1){ - pSample->u.z = 0; - }else{ - pSample->u.z = sqlite3DbMallocRaw(db, n); - if( pSample->u.z==0 ){ - db->mallocFailed = 1; - sqlite3_finalize(pStmt); - return SQLITE_NOMEM; - } - memcpy(pSample->u.z, z, n); - } - } + pSample = &pIdx->aSample[pIdx->nSample]; + decodeIntArray((char*)sqlite3_column_text(pStmt,1), nCol, pSample->anEq, 0); + decodeIntArray((char*)sqlite3_column_text(pStmt,2), nCol, pSample->anLt, 0); + decodeIntArray((char*)sqlite3_column_text(pStmt,3), nCol, pSample->anDLt,0); + + /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer. + ** This is in case the sample record is corrupted. In that case, the + ** sqlite3VdbeRecordCompare() may read up to two varints past the + ** end of the allocated buffer before it realizes it is dealing with + ** a corrupt record. Adding the two 0x00 bytes prevents this from causing + ** a buffer overread. */ + pSample->n = sqlite3_column_bytes(pStmt, 4); + pSample->p = sqlite3DbMallocZero(db, pSample->n + 2); + if( pSample->p==0 ){ + sqlite3_finalize(pStmt); + return SQLITE_NOMEM; } + memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n); + pIdx->nSample++; } - return sqlite3_finalize(pStmt); + rc = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) initAvgEq(pPrevIdx); + return rc; } -#endif /* SQLITE_ENABLE_STAT3 */ /* -** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The +** Load content from the sqlite_stat4 and sqlite_stat3 tables into +** the Index.aSample[] arrays of all indices. +*/ +static int loadStat4(sqlite3 *db, const char *zDb){ + int rc = SQLITE_OK; /* Result codes from subroutines */ + + assert( db->lookaside.bEnabled==0 ); + if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){ + rc = loadStatTbl(db, 0, + "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx", + "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4", + zDb + ); + } + + if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){ + rc = loadStatTbl(db, 1, + "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx", + "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3", + zDb + ); + } + + return rc; +} +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ + +/* +** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The ** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] -** arrays. The contents of sqlite_stat3 are used to populate the +** arrays. The contents of sqlite_stat3/4 are used to populate the ** Index.aSample[] arrays. ** ** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR -** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined -** during compilation and the sqlite_stat3 table is present, no data is +** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined +** during compilation and the sqlite_stat3/4 table is present, no data is ** read from it. ** -** If SQLITE_ENABLE_STAT3 was defined during compilation and the -** sqlite_stat3 table is not present in the database, SQLITE_ERROR is +** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the +** sqlite_stat4 table is not present in the database, SQLITE_ERROR is ** returned. However, in this case, data is read from the sqlite_stat1 ** table (if it is present) before returning. ** @@ -1081,7 +1550,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 sqlite3DeleteIndexSamples(db, pIdx); pIdx->aSample = 0; #endif @@ -1105,12 +1574,12 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ } - /* Load the statistics from the sqlite_stat3 table. */ -#ifdef SQLITE_ENABLE_STAT3 + /* Load the statistics from the sqlite_stat4 table. */ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( rc==SQLITE_OK ){ int lookasideEnabled = db->lookaside.bEnabled; db->lookaside.bEnabled = 0; - rc = loadStat3(db, sInfo.zDatabase); + rc = loadStat4(db, sInfo.zDatabase); db->lookaside.bEnabled = lookasideEnabled; } #endif diff --git a/src/btree.c b/src/btree.c index d6fb5834c5..2ea55886aa 100644 --- a/src/btree.c +++ b/src/btree.c @@ -2508,6 +2508,7 @@ static int lockBtree(BtShared *pBt){ assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) ); pBt->pPage1 = pPage1; pBt->nPage = nPage; +assert( pPage1->leaf==0 || pPage1->leaf==1 ); return SQLITE_OK; page1_init_failed: diff --git a/src/build.c b/src/build.c index 0239017389..491ca28382 100644 --- a/src/build.c +++ b/src/build.c @@ -2024,7 +2024,7 @@ static void sqlite3ClearStatTables( ){ int i; const char *zDbName = pParse->db->aDb[iDb].zName; - for(i=1; i<=3; i++){ + for(i=1; i<=4; i++){ char zTab[24]; sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i); if( sqlite3FindTable(pParse->db, zTab, zDbName) ){ diff --git a/src/ctime.c b/src/ctime.c index 60595ff88d..7c915d58c6 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -117,7 +117,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_ENABLE_RTREE "ENABLE_RTREE", #endif -#ifdef SQLITE_ENABLE_STAT3 +#if defined(SQLITE_ENABLE_STAT4) + "ENABLE_STAT4", +#elif defined(SQLITE_ENABLE_STAT3) "ENABLE_STAT3", #endif #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY diff --git a/src/expr.c b/src/expr.c index dd4f5de15f..aa6f275438 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1278,7 +1278,7 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){ case TK_UMINUS: { int v; if( sqlite3ExprIsInteger(p->pLeft, &v) ){ - assert( v!=-2147483648 ); + assert( v!=(-2147483647-1) ); *pValue = -v; rc = 1; } diff --git a/src/func.c b/src/func.c index be6e238670..07c5069fcb 100644 --- a/src/func.c +++ b/src/func.c @@ -1715,4 +1715,7 @@ void sqlite3RegisterGlobalFunctions(void){ #ifndef SQLITE_OMIT_ALTERTABLE sqlite3AlterFunctions(); #endif +#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4) + sqlite3AnalyzeFunctions(); +#endif } diff --git a/src/mem2.c b/src/mem2.c index 26448ea8aa..d461dffab1 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -179,7 +179,7 @@ static int sqlite3MemSize(void *p){ return 0; } pHdr = sqlite3MemsysGetHeader(p); - return pHdr->iSize; + return (int)pHdr->iSize; } /* @@ -221,7 +221,7 @@ static void randomFill(char *pBuf, int nByte){ x = SQLITE_PTR_TO_INT(pBuf); y = nByte | 1; while( nByte >= 4 ){ - x = (x>>1) ^ (-(x&1) & 0xd0000001); + x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); y = y*1103515245 + 12345; r = x ^ y; *(int*)pBuf = r; @@ -229,7 +229,7 @@ static void randomFill(char *pBuf, int nByte){ nByte -= 4; } while( nByte-- > 0 ){ - x = (x>>1) ^ (-(x&1) & 0xd0000001); + x = (x>>1) ^ (-(int)(x&1) & 0xd0000001); y = y*1103515245 + 12345; r = x ^ y; *(pBuf++) = r & 0xff; @@ -324,9 +324,9 @@ static void sqlite3MemFree(void *pPrior){ } z = (char*)pBt; z -= pHdr->nTitle; - adjustStats(pHdr->iSize, -1); + adjustStats((int)pHdr->iSize, -1); randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + - pHdr->iSize + sizeof(int) + pHdr->nTitle); + (int)pHdr->iSize + sizeof(int) + pHdr->nTitle); free(z); sqlite3_mutex_leave(mem.mutex); } @@ -350,7 +350,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){ if( pNew ){ memcpy(pNew, pPrior, nByteiSize ? nByte : pOldHdr->iSize); if( nByte>pOldHdr->iSize ){ - randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); + randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize); } sqlite3MemFree(pPrior); } @@ -465,7 +465,7 @@ void sqlite3MemdebugSync(){ for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){ void **pBt = (void**)pHdr; pBt -= pHdr->nBacktraceSlots; - mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]); + mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]); } } diff --git a/src/mutex_w32.c b/src/mutex_w32.c index 27d10af5bd..4b88c17452 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -69,7 +69,7 @@ struct sqlite3_mutex { } return osType==2; } -#endif /* SQLITE_OS_WINCE */ +#endif /* SQLITE_OS_WINCE || SQLITE_OS_WINRT */ #endif #ifdef SQLITE_DEBUG @@ -107,7 +107,7 @@ static int winMutex_isInit = 0; ** processing, the "interlocked" magic is probably not ** strictly necessary. */ -static long winMutex_lock = 0; +static LONG winMutex_lock = 0; void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ diff --git a/src/os_unix.c b/src/os_unix.c index 152bf9a113..580f8e60e7 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -218,11 +218,13 @@ struct unixFile { const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ +#if SQLITE_MAX_MMAP_SIZE>0 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 */ +#endif #ifdef __QNXNTO__ int sectorSize; /* Device sector size */ int deviceCharacteristics; /* Precomputed device characteristics */ @@ -449,6 +451,7 @@ static struct unix_syscall { { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent) @@ -461,6 +464,7 @@ static struct unix_syscall { { "mremap", (sqlite3_syscall_ptr)0, 0 }, #endif #define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent) +#endif }; /* End of the overrideable system calls */ @@ -1871,8 +1875,10 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ return posixUnlock(id, eFileLock, 0); } +#if SQLITE_MAX_MMAP_SIZE>0 static int unixMapfile(unixFile *pFd, i64 nByte); static void unixUnmapfile(unixFile *pFd); +#endif /* ** This function performs the parts of the "close file" operation @@ -1886,7 +1892,9 @@ static void unixUnmapfile(unixFile *pFd); */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; +#if SQLITE_MAX_MMAP_SIZE>0 unixUnmapfile(pFile); +#endif if( pFile->h>=0 ){ robust_close(pFile, pFile->h, __LINE__); pFile->h = -1; @@ -3590,6 +3598,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ } #endif +#if SQLITE_MAX_MMAP_SIZE>0 /* 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. @@ -3597,6 +3606,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ if( nBytemmapSize ){ pFile->mmapSize = nByte; } +#endif return SQLITE_OK; } @@ -3686,6 +3696,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ } } +#if SQLITE_MAX_MMAP_SIZE>0 if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ int rc; if( pFile->szChunk<=0 ){ @@ -3698,6 +3709,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ rc = unixMapfile(pFile, nByte); return rc; } +#endif return SQLITE_OK; } @@ -3766,6 +3778,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } +#if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; int rc = SQLITE_OK; @@ -3782,6 +3795,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return rc; } +#endif #ifdef SQLITE_DEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and @@ -4592,22 +4606,20 @@ static int unixShmUnmap( # define unixShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ +#if SQLITE_MAX_MMAP_SIZE>0 /* ** 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. */ @@ -4620,9 +4632,7 @@ static int unixGetPagesize(void){ 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. @@ -4707,7 +4717,6 @@ static void unixRemapfile( pFd->pMapRegion = (void *)pNew; pFd->mmapSize = pFd->mmapSizeActual = nNew; } -#endif /* ** Memory map or remap the file opened by file-descriptor pFd (if the file @@ -4726,7 +4735,6 @@ static void unixRemapfile( ** code otherwise. */ static int unixMapfile(unixFile *pFd, i64 nByte){ -#if SQLITE_MAX_MMAP_SIZE>0 i64 nMap = nByte; int rc; @@ -4752,10 +4760,10 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ unixUnmapfile(pFd); } } -#endif return SQLITE_OK; } +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ /* ** If possible, return a pointer to a mapping of file fd starting at offset @@ -4804,6 +4812,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ UNUSED_PARAMETER(iOff); +#if SQLITE_MAX_MMAP_SIZE>0 /* 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. */ @@ -4819,6 +4828,7 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ } assert( pFd->nFetchOut>=0 ); +#endif return SQLITE_OK; } @@ -5150,7 +5160,9 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; +#if SQLITE_MAX_MMAP_SIZE>0 pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap; +#endif if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; diff --git a/src/os_win.c b/src/os_win.c index 10c4316448..89e64aa431 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -30,7 +30,7 @@ ** available in Windows platforms based on the NT kernel. */ #if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL) -# error "WAL mode requires support from the Windows NT kernel, compile\ +# error "WAL mode requires support from the Windows NT kernel, compile\ with SQLITE_OMIT_WAL." #endif @@ -50,6 +50,66 @@ # define SQLITE_WIN32_HAS_WIDE #endif +/* +** Maximum pathname length (in chars) for Win32. This should normally be +** MAX_PATH. +*/ +#ifndef SQLITE_WIN32_MAX_PATH_CHARS +# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH) +#endif + +/* +** Maximum pathname length (in chars) for WinNT. This should normally be +** 32767. +*/ +#ifndef SQLITE_WINNT_MAX_PATH_CHARS +# define SQLITE_WINNT_MAX_PATH_CHARS (32767) +#endif + +/* +** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in +** characters, so we allocate 3 bytes per character assuming worst-case of +** 4-bytes-per-character for UTF8. +*/ +#ifndef SQLITE_WIN32_MAX_PATH_BYTES +# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4) +#endif + +/* +** Maximum pathname length (in bytes) for WinNT. This should normally be +** 32767 * sizeof(WCHAR). +*/ +#ifndef SQLITE_WINNT_MAX_PATH_BYTES +# define SQLITE_WINNT_MAX_PATH_BYTES \ + (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS) +#endif + +/* +** Maximum error message length (in chars) for WinRT. +*/ +#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS +# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024) +#endif + +/* +** Returns non-zero if the character should be treated as a directory +** separator. +*/ +#ifndef winIsDirSep +# define winIsDirSep(a) (((a) == '/') || ((a) == '\\')) +#endif + +/* +** Returns the string that should be used as the directory separator. +*/ +#ifndef winGetDirDep +# ifdef __CYGWIN__ +# define winGetDirDep() "/" +# else +# define winGetDirDep() "\\" +# endif +#endif + /* ** Do we need to manually define the Win32 file mapping APIs for use with WAL ** mode (e.g. these APIs are available in the Windows CE SDK; however, they @@ -1057,11 +1117,11 @@ void sqlite3_win32_sleep(DWORD milliseconds){ ** the LockFileEx() API. */ #if SQLITE_OS_WINCE || SQLITE_OS_WINRT -# define isNT() (1) +# define osIsNT() (1) #elif !defined(SQLITE_WIN32_HAS_WIDE) -# define isNT() (0) +# define osIsNT() (0) #else - static int isNT(void){ + static int osIsNT(void){ if( sqlite3_os_type==0 ){ OSVERSIONINFOA sInfo; sInfo.dwOSVersionInfoSize = sizeof(sInfo); @@ -1272,7 +1332,7 @@ void sqlite3MemSetDefault(void){ ** ** Space to hold the returned string is obtained from malloc. */ -static LPWSTR utf8ToUnicode(const char *zFilename){ +static LPWSTR winUtf8ToUnicode(const char *zFilename){ int nChar; LPWSTR zWideFilename; @@ -1297,7 +1357,7 @@ static LPWSTR utf8ToUnicode(const char *zFilename){ ** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is ** obtained from sqlite3_malloc(). */ -static char *unicodeToUtf8(LPCWSTR zWideFilename){ +static char *winUnicodeToUtf8(LPCWSTR zWideFilename){ int nByte; char *zFilename; @@ -1325,7 +1385,7 @@ static char *unicodeToUtf8(LPCWSTR zWideFilename){ ** Space to hold the returned string is obtained ** from sqlite3_malloc. */ -static LPWSTR mbcsToUnicode(const char *zFilename){ +static LPWSTR winMbcsToUnicode(const char *zFilename){ int nByte; LPWSTR zMbcsFilename; int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; @@ -1355,7 +1415,7 @@ static LPWSTR mbcsToUnicode(const char *zFilename){ ** Space to hold the returned string is obtained from ** sqlite3_malloc(). */ -static char *unicodeToMbcs(LPCWSTR zWideFilename){ +static char *winUnicodeToMbcs(LPCWSTR zWideFilename){ int nByte; char *zFilename; int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; @@ -1385,11 +1445,11 @@ char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ char *zFilenameUtf8; LPWSTR zTmpWide; - zTmpWide = mbcsToUnicode(zFilename); + zTmpWide = winMbcsToUnicode(zFilename); if( zTmpWide==0 ){ return 0; } - zFilenameUtf8 = unicodeToUtf8(zTmpWide); + zFilenameUtf8 = winUnicodeToUtf8(zTmpWide); sqlite3_free(zTmpWide); return zFilenameUtf8; } @@ -1402,11 +1462,11 @@ char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ char *zFilenameMbcs; LPWSTR zTmpWide; - zTmpWide = utf8ToUnicode(zFilename); + zTmpWide = winUtf8ToUnicode(zFilename); if( zTmpWide==0 ){ return 0; } - zFilenameMbcs = unicodeToMbcs(zTmpWide); + zFilenameMbcs = winUnicodeToMbcs(zTmpWide); sqlite3_free(zTmpWide); return zFilenameMbcs; } @@ -1436,7 +1496,7 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ if( ppDirectory ){ char *zValueUtf8 = 0; if( zValue && zValue[0] ){ - zValueUtf8 = unicodeToUtf8(zValue); + zValueUtf8 = winUnicodeToUtf8(zValue); if ( zValueUtf8==0 ){ return SQLITE_NOMEM; } @@ -1449,11 +1509,11 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ } /* -** The return value of getLastErrorMsg +** The return value of winGetLastErrorMsg ** is zero if the error message fits in the buffer, or non-zero ** otherwise (if the message was truncated). */ -static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ +static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ /* FormatMessage returns 0 on failure. Otherwise it ** returns the number of TCHARs written to the output ** buffer, excluding the terminating null char. @@ -1461,16 +1521,16 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ DWORD dwLen = 0; char *zOut = 0; - if( isNT() ){ + if( osIsNT() ){ #if SQLITE_OS_WINRT - WCHAR zTempWide[MAX_PATH+1]; /* NOTE: Somewhat arbitrary. */ + WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1]; dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, lastErrno, 0, zTempWide, - MAX_PATH, + SQLITE_WIN32_MAX_ERRMSG_CHARS, 0); #else LPWSTR zTempWide = NULL; @@ -1487,7 +1547,7 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ if( dwLen > 0 ){ /* allocate a buffer and convert to UTF8 */ sqlite3BeginBenignMalloc(); - zOut = unicodeToUtf8(zTempWide); + zOut = winUnicodeToUtf8(zTempWide); sqlite3EndBenignMalloc(); #if !SQLITE_OS_WINRT /* free the system buffer allocated by FormatMessage */ @@ -1555,7 +1615,7 @@ static int winLogErrorAtLine( int i; /* Loop counter */ zMsg[0] = 0; - getLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); + winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); assert( errcode!=SQLITE_OK ); if( zPath==0 ) zPath = ""; for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} @@ -1580,17 +1640,17 @@ static int winLogErrorAtLine( #ifndef SQLITE_WIN32_IOERR_RETRY_DELAY # define SQLITE_WIN32_IOERR_RETRY_DELAY 25 #endif -static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY; -static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; +static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY; +static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; /* ** If a ReadFile() or WriteFile() error occurs, invoke this routine ** to see if it should be retried. Return TRUE to retry. Return FALSE ** to give up with an error. */ -static int retryIoerr(int *pnRetry, DWORD *pError){ +static int winRetryIoerr(int *pnRetry, DWORD *pError){ DWORD e = osGetLastError(); - if( *pnRetry>=win32IoerrRetry ){ + if( *pnRetry>=winIoerrRetry ){ if( pError ){ *pError = e; } @@ -1599,7 +1659,7 @@ static int retryIoerr(int *pnRetry, DWORD *pError){ if( e==ERROR_ACCESS_DENIED || e==ERROR_LOCK_VIOLATION || e==ERROR_SHARING_VIOLATION ){ - sqlite3_win32_sleep(win32IoerrRetryDelay*(1+*pnRetry)); + sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); ++*pnRetry; return 1; } @@ -1612,11 +1672,11 @@ static int retryIoerr(int *pnRetry, DWORD *pError){ /* ** Log a I/O error retry episode. */ -static void logIoerr(int nRetry){ +static void winLogIoerr(int nRetry){ if( nRetry ){ sqlite3_log(SQLITE_IOERR, "delayed %dms for lock/sharing conflict", - win32IoerrRetryDelay*nRetry*(nRetry+1)/2 + winIoerrRetryDelay*nRetry*(nRetry+1)/2 ); } } @@ -1681,7 +1741,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){ BOOL bLogged = FALSE; BOOL bInit = TRUE; - zName = utf8ToUnicode(zFilename); + zName = winUtf8ToUnicode(zFilename); if( zName==0 ){ /* out of memory */ return SQLITE_IOERR_NOMEM; @@ -1954,7 +2014,7 @@ static BOOL winLockFile( return winceLockFile(phFile, offsetLow, offsetHigh, numBytesLow, numBytesHigh); #else - if( isNT() ){ + if( osIsNT() ){ OVERLAPPED ovlp; memset(&ovlp, 0, sizeof(OVERLAPPED)); ovlp.Offset = offsetLow; @@ -1985,7 +2045,7 @@ static BOOL winUnlockFile( return winceUnlockFile(phFile, offsetLow, offsetHigh, numBytesLow, numBytesHigh); #else - if( isNT() ){ + if( osIsNT() ){ OVERLAPPED ovlp; memset(&ovlp, 0, sizeof(OVERLAPPED)); ovlp.Offset = offsetLow; @@ -2015,7 +2075,7 @@ static BOOL winUnlockFile( ** argument to offset iOffset within the file. If successful, return 0. ** Otherwise, set pFile->lastErrno and return non-zero. */ -static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ +static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ #if !SQLITE_OS_WINRT LONG upperBits; /* Most sig. 32 bits of new offset */ LONG lowerBits; /* Least sig. 32 bits of new offset */ @@ -2040,7 +2100,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ pFile->lastErrno = lastErrno; winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "seekWinFile", pFile->zPath); + "winSeekFile", pFile->zPath); OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); return 1; } @@ -2061,7 +2121,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ if(!bRet){ pFile->lastErrno = osGetLastError(); winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, - "seekWinFile", pFile->zPath); + "winSeekFile", pFile->zPath); OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); return 1; } @@ -2176,7 +2236,7 @@ static int winRead( #endif #if SQLITE_OS_WINCE - if( seekWinFile(pFile, offset) ){ + if( winSeekFile(pFile, offset) ){ OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); return SQLITE_FULL; } @@ -2189,13 +2249,13 @@ static int winRead( osGetLastError()!=ERROR_HANDLE_EOF ){ #endif DWORD lastErrno; - if( retryIoerr(&nRetry, &lastErrno) ) continue; + if( winRetryIoerr(&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); } - logIoerr(nRetry); + winLogIoerr(nRetry); if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[nRead], 0, amt-nRead); @@ -2248,7 +2308,7 @@ static int winWrite( #endif #if SQLITE_OS_WINCE - rc = seekWinFile(pFile, offset); + rc = winSeekFile(pFile, offset); if( rc==0 ){ #else { @@ -2273,7 +2333,7 @@ static int winWrite( #else if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ #endif - if( retryIoerr(&nRetry, &lastErrno) ) continue; + if( winRetryIoerr(&nRetry, &lastErrno) ) continue; break; } assert( nWrite==0 || nWrite<=(DWORD)nRem ); @@ -2305,7 +2365,7 @@ static int winWrite( return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, "winWrite", pFile->zPath); }else{ - logIoerr(nRetry); + winLogIoerr(nRetry); } OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; @@ -2334,7 +2394,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ } /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ - if( seekWinFile(pFile, nByte) ){ + if( winSeekFile(pFile, nByte) ){ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, "winTruncate1", pFile->zPath); }else if( 0==osSetEndOfFile(pFile->h) && @@ -2415,6 +2475,7 @@ static int winSync(sqlite3_file *id, int flags){ ** no-op */ #ifdef SQLITE_NO_SYNC + OSTRACE(("SYNC-NOP file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; #else rc = osFlushFileBuffers(pFile->h); @@ -2512,10 +2573,10 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ ** Different API routines are called depending on whether or not this ** is Win9x or WinNT. */ -static int getReadLock(winFile *pFile){ +static int winGetReadLock(winFile *pFile){ int res; OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - if( isNT() ){ + if( osIsNT() ){ #if SQLITE_OS_WINCE /* ** NOTE: Windows CE is handled differently here due its lack of the Win32 @@ -2547,11 +2608,11 @@ static int getReadLock(winFile *pFile){ /* ** Undo a readlock */ -static int unlockReadLock(winFile *pFile){ +static int winUnlockReadLock(winFile *pFile){ int res; DWORD lastErrno; OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); - if( isNT() ){ + if( osIsNT() ){ res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); } #ifdef SQLITE_WIN32_HAS_ANSI @@ -2562,7 +2623,7 @@ static int unlockReadLock(winFile *pFile){ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ pFile->lastErrno = lastErrno; winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, - "unlockReadLock", pFile->zPath); + "winUnlockReadLock", pFile->zPath); } OSTRACE(("READ-UNLOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res))); return res; @@ -2653,7 +2714,7 @@ static int winLock(sqlite3_file *id, int locktype){ */ if( locktype==SHARED_LOCK && res ){ assert( pFile->locktype==NO_LOCK ); - res = getReadLock(pFile); + res = winGetReadLock(pFile); if( res ){ newLocktype = SHARED_LOCK; }else{ @@ -2684,14 +2745,14 @@ static int winLock(sqlite3_file *id, int locktype){ */ if( locktype==EXCLUSIVE_LOCK && res ){ assert( pFile->locktype>=SHARED_LOCK ); - res = unlockReadLock(pFile); + res = winUnlockReadLock(pFile); res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, SHARED_SIZE, 0); if( res ){ newLocktype = EXCLUSIVE_LOCK; }else{ lastErrno = osGetLastError(); - getReadLock(pFile); + winGetReadLock(pFile); } } @@ -2708,10 +2769,10 @@ static int winLock(sqlite3_file *id, int locktype){ if( res ){ rc = SQLITE_OK; }else{ - OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n", - pFile->h, locktype, newLocktype)); pFile->lastErrno = lastErrno; rc = SQLITE_BUSY; + OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n", + pFile->h, locktype, newLocktype)); } pFile->locktype = (u8)newLocktype; OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n", @@ -2771,7 +2832,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ type = pFile->locktype; if( type>=EXCLUSIVE_LOCK ){ winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); - if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ + if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), @@ -2782,7 +2843,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); } if( locktype==NO_LOCK && type>=SHARED_LOCK ){ - unlockReadLock(pFile); + winUnlockReadLock(pFile); } if( type>=PENDING_LOCK ){ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); @@ -2810,7 +2871,7 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ } /* Forward declaration */ -static int getTempname(int nBuf, char *zBuf); +static int winGetTempname(sqlite3_vfs *, char **); #if SQLITE_MAX_MMAP_SIZE>0 static int winMapfile(winFile*, sqlite3_int64); #endif @@ -2873,26 +2934,26 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ case SQLITE_FCNTL_WIN32_AV_RETRY: { int *a = (int*)pArg; if( a[0]>0 ){ - win32IoerrRetry = a[0]; + winIoerrRetry = a[0]; }else{ - a[0] = win32IoerrRetry; + a[0] = winIoerrRetry; } if( a[1]>0 ){ - win32IoerrRetryDelay = a[1]; + winIoerrRetryDelay = a[1]; }else{ - a[1] = win32IoerrRetryDelay; + a[1] = winIoerrRetryDelay; } OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); return SQLITE_OK; } case SQLITE_FCNTL_TEMPFILENAME: { - char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname ); - if( zTFile ){ - getTempname(pFile->pVfs->mxPathname, zTFile); + char *zTFile = 0; + int rc = winGetTempname(pFile->pVfs, &zTFile); + if( rc==SQLITE_OK ){ *(char**)pArg = zTFile; } - OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); - return SQLITE_OK; + OSTRACE(("FCNTL file=%p, rc=%d\n", pFile->h, rc)); + return rc; } #if SQLITE_MAX_MMAP_SIZE>0 case SQLITE_FCNTL_MMAP_SIZE: { @@ -3854,10 +3915,10 @@ static const sqlite3_io_methods winIoMethod = { ** is obtained from malloc and must be freed by the calling ** function. */ -static void *convertUtf8Filename(const char *zFilename){ +static void *winConvertUtf8Filename(const char *zFilename){ void *zConverted = 0; - if( isNT() ){ - zConverted = utf8ToUnicode(zFilename); + if( osIsNT() ){ + zConverted = winUtf8ToUnicode(zFilename); } #ifdef SQLITE_WIN32_HAS_ANSI else{ @@ -3869,26 +3930,29 @@ static void *convertUtf8Filename(const char *zFilename){ } /* -** Maximum pathname length (in bytes) for windows. The MAX_PATH macro is -** in characters, so we allocate 3 bytes per character assuming worst-case -** 3-bytes-per-character UTF8. +** This function returns non-zero if the specified UTF-8 string buffer +** ends with a directory separator character. */ -#ifndef SQLITE_WIN32_MAX_PATH -# define SQLITE_WIN32_MAX_PATH (MAX_PATH*3) -#endif +static int winEndsInDirSep(char *zBuf){ + if( zBuf ){ + int nLen = sqlite3Strlen30(zBuf); + return nLen>0 && winIsDirSep(zBuf[nLen-1]); + } + return 0; +} /* -** Create a temporary file name in zBuf. zBuf must be big enough to -** hold at pVfs->mxPathname characters. +** Create a temporary file name and store the resulting pointer into pzBuf. +** The pointer returned in pzBuf must be freed via sqlite3_free(). */ -static int getTempname(int nBuf, char *zBuf){ +static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; size_t i, j; - int nTempPath; - char zTempPath[SQLITE_WIN32_MAX_PATH+2]; + int nBuf, nLen; + char *zBuf; /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this @@ -3896,23 +3960,49 @@ static int getTempname(int nBuf, char *zBuf){ */ SimulateIOError( return SQLITE_IOERR ); + /* Allocate a temporary buffer to store the fully qualified file + ** name for the temporary file. If this fails, we cannot continue. + */ + nBuf = pVfs->mxPathname; + zBuf = sqlite3MallocZero( nBuf+2 ); + if( !zBuf ){ + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM; + } + + /* Figure out the effective temporary directory. First, check if one + ** has been explicitly set by the application; otherwise, use the one + ** configured by the operating system. + */ + assert( nBuf>30 ); if( sqlite3_temp_directory ){ - sqlite3_snprintf(SQLITE_WIN32_MAX_PATH-30, zTempPath, "%s", - sqlite3_temp_directory); + sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory, + winEndsInDirSep(sqlite3_temp_directory) ? "" : + winGetDirDep()); } #if !SQLITE_OS_WINRT - else if( isNT() ){ + else if( osIsNT() ){ char *zMulti; - WCHAR zWidePath[MAX_PATH]; - if( osGetTempPathW(MAX_PATH-30, zWidePath)==0 ){ + LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) ); + if( !zWidePath ){ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM; + } + if( osGetTempPathW(nBuf, zWidePath)==0 ){ + sqlite3_free(zWidePath); + sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); return SQLITE_IOERR_GETTEMPPATH; } - zMulti = unicodeToUtf8(zWidePath); + zMulti = winUnicodeToUtf8(zWidePath); if( zMulti ){ - sqlite3_snprintf(SQLITE_WIN32_MAX_PATH-30, zTempPath, "%s", zMulti); + sqlite3_snprintf(nBuf-30, zBuf, "%s", zMulti); sqlite3_free(zMulti); + sqlite3_free(zWidePath); }else{ + sqlite3_free(zWidePath); + sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; } @@ -3920,55 +4010,43 @@ static int getTempname(int nBuf, char *zBuf){ #ifdef SQLITE_WIN32_HAS_ANSI else{ char *zUtf8; - char zMbcsPath[SQLITE_WIN32_MAX_PATH]; - if( osGetTempPathA(SQLITE_WIN32_MAX_PATH-30, zMbcsPath)==0 ){ + char *zMbcsPath = sqlite3MallocZero( nBuf ); + if( !zMbcsPath ){ + sqlite3_free(zBuf); + OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); + return SQLITE_IOERR_NOMEM; + } + if( osGetTempPathA(nBuf, zMbcsPath)==0 ){ + sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); return SQLITE_IOERR_GETTEMPPATH; } zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); if( zUtf8 ){ - sqlite3_snprintf(SQLITE_WIN32_MAX_PATH-30, zTempPath, "%s", zUtf8); + sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8); sqlite3_free(zUtf8); }else{ + sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); return SQLITE_IOERR_NOMEM; } } -#else - else{ - /* - ** Compiled without ANSI support and the current operating system - ** is not Windows NT; therefore, just zero the temporary buffer. - */ - memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH+2); - } #endif /* SQLITE_WIN32_HAS_ANSI */ -#else - else{ - /* - ** Compiled for WinRT and the sqlite3_temp_directory is not set; - ** therefore, just zero the temporary buffer. - */ - memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH+2); - } #endif /* !SQLITE_OS_WINRT */ /* Check that the output buffer is large enough for the temporary file ** name. If it is not, return SQLITE_ERROR. */ - nTempPath = sqlite3Strlen30(zTempPath); + nLen = sqlite3Strlen30(zBuf); - if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){ + if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){ + sqlite3_free(zBuf); OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); return SQLITE_ERROR; } - for(i=nTempPath; i>0 && zTempPath[i-1]=='\\'; i--){} - zTempPath[i] = 0; + sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); - sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ? - "%s\\"SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX, - zTempPath); j = sqlite3Strlen30(zBuf); sqlite3_randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ @@ -3976,6 +4054,7 @@ static int getTempname(int nBuf, char *zBuf){ } zBuf[j] = 0; zBuf[j+1] = 0; + *pzBuf = zBuf; OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf)); return SQLITE_OK; @@ -3991,13 +4070,13 @@ static int winIsDir(const void *zConverted){ int rc = 0; DWORD lastErrno; - if( isNT() ){ + if( osIsNT() ){ int cnt = 0; WIN32_FILE_ATTRIBUTE_DATA sAttrData; memset(&sAttrData, 0, sizeof(sAttrData)); while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, GetFileExInfoStandard, - &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){} + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} if( !rc ){ return 0; /* Invalid name? */ } @@ -4014,7 +4093,7 @@ static int winIsDir(const void *zConverted){ ** Open a file. */ static int winOpen( - sqlite3_vfs *pVfs, /* Not used */ + sqlite3_vfs *pVfs, /* Used to get maximum path name length */ const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ @@ -4037,7 +4116,7 @@ static int winOpen( /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ - char zTmpname[SQLITE_WIN32_MAX_PATH+2]; /* Buffer used to create temp filename */ + char *zTmpname = 0; /* For temporary filename, if necessary. */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE @@ -4092,7 +4171,7 @@ static int winOpen( pFile->h = INVALID_HANDLE_VALUE; #if SQLITE_OS_WINRT - if( !sqlite3_temp_directory ){ + if( !zUtf8Name && !sqlite3_temp_directory ){ sqlite3_log(SQLITE_ERROR, "sqlite3_temp_directory variable should be set for WinRT"); } @@ -4102,8 +4181,8 @@ static int winOpen( ** temporary file name to use */ if( !zUtf8Name ){ - assert(isDelete && !isOpenJournal); - rc = getTempname(SQLITE_WIN32_MAX_PATH+2, zTmpname); + assert( isDelete && !isOpenJournal ); + rc = winGetTempname(pVfs, &zTmpname); if( rc!=SQLITE_OK ){ OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc))); return rc; @@ -4116,17 +4195,19 @@ static int winOpen( ** sqlite3_uri_parameter(). */ assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || - zUtf8Name[strlen(zUtf8Name)+1]==0 ); + zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 ); /* Convert the filename to the system encoding. */ - zConverted = convertUtf8Filename(zUtf8Name); + zConverted = winConvertUtf8Filename(zUtf8Name); if( zConverted==0 ){ + sqlite3_free(zTmpname); OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); return SQLITE_IOERR_NOMEM; } if( winIsDir(zConverted) ){ sqlite3_free(zConverted); + sqlite3_free(zTmpname); OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name)); return SQLITE_CANTOPEN_ISDIR; } @@ -4173,7 +4254,7 @@ static int winOpen( dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; #endif - if( isNT() ){ + if( osIsNT() ){ #if SQLITE_OS_WINRT CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); @@ -4188,7 +4269,7 @@ static int winOpen( dwShareMode, dwCreationDisposition, &extendedParameters))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt, &lastErrno) ){ + winRetryIoerr(&cnt, &lastErrno) ){ /* Noop */ } #else @@ -4198,7 +4279,7 @@ static int winOpen( dwCreationDisposition, dwFlagsAndAttributes, NULL))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt, &lastErrno) ){ + winRetryIoerr(&cnt, &lastErrno) ){ /* Noop */ } #endif @@ -4211,12 +4292,12 @@ static int winOpen( dwCreationDisposition, dwFlagsAndAttributes, NULL))==INVALID_HANDLE_VALUE && - retryIoerr(&cnt, &lastErrno) ){ + winRetryIoerr(&cnt, &lastErrno) ){ /* Noop */ } } #endif - logIoerr(cnt); + winLogIoerr(cnt); OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); @@ -4225,6 +4306,7 @@ static int winOpen( pFile->lastErrno = lastErrno; winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); sqlite3_free(zConverted); + sqlite3_free(zTmpname); if( isReadWrite && !isExclusive ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY) & @@ -4253,6 +4335,7 @@ static int winOpen( ){ osCloseHandle(h); sqlite3_free(zConverted); + sqlite3_free(zTmpname); OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc))); return rc; } @@ -4262,6 +4345,7 @@ static int winOpen( #endif { sqlite3_free(zConverted); + sqlite3_free(zTmpname); } pFile->pMethod = &winIoMethod; @@ -4315,11 +4399,12 @@ static int winDelete( SimulateIOError(return SQLITE_IOERR_DELETE); OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir)); - zConverted = convertUtf8Filename(zFilename); + zConverted = winConvertUtf8Filename(zFilename); if( zConverted==0 ){ + OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM; } - if( isNT() ){ + if( osIsNT() ){ do { #if SQLITE_OS_WINRT WIN32_FILE_ATTRIBUTE_DATA sAttrData; @@ -4358,7 +4443,7 @@ static int winDelete( rc = SQLITE_OK; /* Deleted OK. */ break; } - if ( !retryIoerr(&cnt, &lastErrno) ){ + if ( !winRetryIoerr(&cnt, &lastErrno) ){ rc = SQLITE_ERROR; /* No more retries. */ break; } @@ -4386,7 +4471,7 @@ static int winDelete( rc = SQLITE_OK; /* Deleted OK. */ break; } - if ( !retryIoerr(&cnt, &lastErrno) ){ + if ( !winRetryIoerr(&cnt, &lastErrno) ){ rc = SQLITE_ERROR; /* No more retries. */ break; } @@ -4397,7 +4482,7 @@ static int winDelete( rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); }else{ - logIoerr(cnt); + winLogIoerr(cnt); } sqlite3_free(zConverted); OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc))); @@ -4423,18 +4508,18 @@ static int winAccess( OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", zFilename, flags, pResOut)); - zConverted = convertUtf8Filename(zFilename); + zConverted = winConvertUtf8Filename(zFilename); if( zConverted==0 ){ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); return SQLITE_IOERR_NOMEM; } - if( isNT() ){ + if( osIsNT() ){ int cnt = 0; WIN32_FILE_ATTRIBUTE_DATA sAttrData; memset(&sAttrData, 0, sizeof(sAttrData)); while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, GetFileExInfoStandard, - &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){} + &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} if( rc ){ /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file ** as if it does not exist. @@ -4447,7 +4532,7 @@ static int winAccess( attr = sAttrData.dwFileAttributes; } }else{ - logIoerr(cnt); + winLogIoerr(cnt); if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename); sqlite3_free(zConverted); @@ -4498,7 +4583,7 @@ static BOOL winIsVerbatimPathname( ** the final two cases; therefore, we return the safer return value of TRUE ** so that callers of this function will simply use it verbatim. */ - if ( zPathname[0]=='/' || zPathname[0]=='\\' ){ + if ( winIsDirSep(zPathname[0]) ){ return TRUE; } @@ -4534,7 +4619,6 @@ static int winFullPathname( #if defined(__CYGWIN__) SimulateIOError( return SQLITE_ERROR ); UNUSED_PARAMETER(nFull); - assert( pVfs->mxPathname>=SQLITE_WIN32_MAX_PATH ); assert( nFull>=pVfs->mxPathname ); if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ /* @@ -4543,15 +4627,21 @@ static int winFullPathname( ** for converting the relative path name to an absolute ** one by prepending the data directory and a slash. */ - char zOut[SQLITE_WIN32_MAX_PATH+1]; + char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); + if( !zOut ){ + winLogError(SQLITE_IOERR_NOMEM, 0, "winFullPathname", zRelative); + return SQLITE_IOERR_NOMEM; + } if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut, - SQLITE_WIN32_MAX_PATH+1)<0 ){ + pVfs->mxPathname+1)<0 ){ winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path", zRelative); + sqlite3_free(zOut); return SQLITE_CANTOPEN_FULLPATH; } - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", - sqlite3_data_directory, zOut); + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", + sqlite3_data_directory, winGetDirDep(), zOut); + sqlite3_free(zOut); }else{ if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){ winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path", @@ -4573,8 +4663,8 @@ static int winFullPathname( ** for converting the relative path name to an absolute ** one by prepending the data directory and a backslash. */ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", - sqlite3_data_directory, zRelative); + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", + sqlite3_data_directory, winGetDirDep(), zRelative); }else{ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); } @@ -4606,15 +4696,15 @@ static int winFullPathname( ** for converting the relative path name to an absolute ** one by prepending the data directory and a backslash. */ - sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s", - sqlite3_data_directory, zRelative); + sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s", + sqlite3_data_directory, winGetDirDep(), zRelative); return SQLITE_OK; } - zConverted = convertUtf8Filename(zRelative); + zConverted = winConvertUtf8Filename(zRelative); if( zConverted==0 ){ return SQLITE_IOERR_NOMEM; } - if( isNT() ){ + if( osIsNT() ){ LPWSTR zTemp; nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); if( nByte==0 ){ @@ -4638,7 +4728,7 @@ static int winFullPathname( return SQLITE_CANTOPEN_FULLPATH; } sqlite3_free(zConverted); - zOut = unicodeToUtf8(zTemp); + zOut = winUnicodeToUtf8(zTemp); sqlite3_free(zTemp); } #ifdef SQLITE_WIN32_HAS_ANSI @@ -4691,12 +4781,12 @@ static int winFullPathname( */ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ HANDLE h; - void *zConverted = convertUtf8Filename(zFilename); + void *zConverted = winConvertUtf8Filename(zFilename); UNUSED_PARAMETER(pVfs); if( zConverted==0 ){ return 0; } - if( isNT() ){ + if( osIsNT() ){ #if SQLITE_OS_WINRT h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0); #else @@ -4713,7 +4803,7 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ } static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ UNUSED_PARAMETER(pVfs); - getLastErrorMsg(osGetLastError(), nBuf, zBufOut); + winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut); } static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ UNUSED_PARAMETER(pVfs); @@ -4889,7 +4979,7 @@ static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ */ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ UNUSED_PARAMETER(pVfs); - return getLastErrorMsg(osGetLastError(), nBuf, zBuf); + return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf); } /* @@ -4899,7 +4989,7 @@ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ - SQLITE_WIN32_MAX_PATH, /* mxPathname */ + SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ 0, /* pNext */ "win32", /* zName */ 0, /* pAppData */ @@ -4920,6 +5010,32 @@ int sqlite3_os_init(void){ winGetSystemCall, /* xGetSystemCall */ winNextSystemCall, /* xNextSystemCall */ }; +#if defined(SQLITE_WIN32_HAS_WIDE) + static sqlite3_vfs winLongPathVfs = { + 3, /* iVersion */ + sizeof(winFile), /* szOsFile */ + SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ + 0, /* pNext */ + "win32-longpath", /* zName */ + 0, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ + winSetSystemCall, /* xSetSystemCall */ + winGetSystemCall, /* xGetSystemCall */ + winNextSystemCall, /* xNextSystemCall */ + }; +#endif /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ @@ -4936,6 +5052,11 @@ int sqlite3_os_init(void){ assert( winSysInfo.dwPageSize>0 ); sqlite3_vfs_register(&winVfs, 1); + +#if defined(SQLITE_WIN32_HAS_WIDE) + sqlite3_vfs_register(&winLongPathVfs, 0); +#endif + return SQLITE_OK; } diff --git a/src/pragma.c b/src/pragma.c index ffaf69e9e4..d4cf597ff8 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -661,7 +661,7 @@ void sqlite3Pragma( Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ - sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); + sqlite3Atoi64(zRight, &iLimit, sqlite3Strlen30(zRight), SQLITE_UTF8); if( iLimit<-1 ) iLimit = -1; } iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); @@ -795,10 +795,11 @@ void sqlite3Pragma( */ if( sqlite3StrICmp(zLeft,"mmap_size")==0 ){ sqlite3_int64 sz; +#if SQLITE_MAX_MMAP_SIZE>0 assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( zRight ){ int ii; - sqlite3Atoi64(zRight, &sz, 1000, SQLITE_UTF8); + sqlite3Atoi64(zRight, &sz, sqlite3Strlen30(zRight), SQLITE_UTF8); if( sz<0 ) sz = sqlite3GlobalConfig.szMmap; if( pId2->n==0 ) db->szMmap = sz; for(ii=db->nDb-1; ii>=0; ii--){ @@ -809,8 +810,9 @@ void sqlite3Pragma( } sz = -1; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_MMAP_SIZE, &sz); -#if SQLITE_MAX_MMAP_SIZE==0 +#else sz = 0; + rc = SQLITE_OK; #endif if( rc==SQLITE_OK ){ returnSingleInt(pParse, "mmap_size", sz); diff --git a/src/shell.c b/src/shell.c index 64c17ed054..915952cfbe 100644 --- a/src/shell.c +++ b/src/shell.c @@ -71,12 +71,12 @@ /* Make sure isatty() has a prototype. */ extern int isatty(int); -#endif /* popen and pclose are not C89 functions and so are sometimes omitted from ** the header */ -FILE *popen(const char*,const char*); -int pclose(FILE*); +extern FILE *popen(const char*,const char*); +extern int pclose(FILE*); +#endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() @@ -554,7 +554,7 @@ static void output_c_string(FILE *out, const char *z){ }else if( c=='\r' ){ fputc('\\', out); fputc('r', out); - }else if( !isprint(c) ){ + }else if( !isprint(c&0xff) ){ fprintf(out, "\\%03o", c&0xff); }else{ fputc(c, out); @@ -1280,7 +1280,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){ if( strcmp(zTable, "sqlite_sequence")==0 ){ zPrepStmt = "DELETE FROM sqlite_sequence;\n"; - }else if( strcmp(zTable, "sqlite_stat1")==0 ){ + }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ fprintf(p->out, "ANALYZE sqlite_master;\n"); }else if( strncmp(zTable, "sqlite_", 7)==0 ){ return 0; @@ -1717,7 +1717,7 @@ static char *csv_read_one_field(CSVReader *p){ } if( (c==cSep && pc==cQuote) || (c=='\n' && pc==cQuote) - || (c=='\n' && pc=='\r' && p->n>2 && p->z[p->n-2]==cQuote) + || (c=='\n' && pc=='\r' && p->n>=2 && p->z[p->n-2]==cQuote) || (c==EOF && pc==cQuote) ){ do{ p->n--; }while( p->z[p->n]!=cQuote ); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 00e4d9ef93..039cdcb5fc 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -568,6 +568,20 @@ extern const int sqlite3one; # define SQLITE_DEFAULT_MMAP_SIZE SQLITE_MAX_MMAP_SIZE #endif +/* +** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined. +** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also +** define SQLITE_ENABLE_STAT3_OR_STAT4 +*/ +#ifdef SQLITE_ENABLE_STAT4 +# undef SQLITE_ENABLE_STAT3 +# define SQLITE_ENABLE_STAT3_OR_STAT4 1 +#elif SQLITE_ENABLE_STAT3 +# define SQLITE_ENABLE_STAT3_OR_STAT4 1 +#elif SQLITE_ENABLE_STAT3_OR_STAT4 +# undef SQLITE_ENABLE_STAT3_OR_STAT4 +#endif + /* ** An instance of the following structure is used to store the busy-handler ** callback for a given sqlite handle. @@ -1558,9 +1572,10 @@ struct Index { unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int nSample; /* Number of elements in aSample[] */ - tRowcnt avgEq; /* Average nEq value for key values not in aSample */ + int nSampleCol; /* Size of IndexSample.anEq[] and so on */ + tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ #endif }; @@ -1571,16 +1586,11 @@ struct Index { ** analyze.c source file for additional information. */ struct IndexSample { - union { - char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ - double r; /* Value if eType is SQLITE_FLOAT */ - i64 i; /* Value if eType is SQLITE_INTEGER */ - } u; - u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ - int nByte; /* Size in byte of text or blob. */ - tRowcnt nEq; /* Est. number of rows where the key equals this sample */ - tRowcnt nLt; /* Est. number of rows where key is less than this sample */ - tRowcnt nDLt; /* Est. number of distinct keys less than this sample */ + void *p; /* Pointer to sampled record */ + int n; /* Size of record in bytes */ + tRowcnt *anEq; /* Est. number of rows where the key equals this sample */ + tRowcnt *anLt; /* Est. number of rows where key is less than this sample */ + tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */ }; /* @@ -3056,9 +3066,6 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(sqlite3 *); char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8); -#ifdef SQLITE_ENABLE_STAT3 -char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *); -#endif int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION @@ -3125,6 +3132,12 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); void sqlite3BackupRestart(sqlite3_backup *); void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +void sqlite3AnalyzeFunctions(void); +int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*); +void sqlite3Stat4ProbeFree(UnpackedRecord*); +#endif + /* ** The interface to the LEMON-generated parser */ diff --git a/src/test1.c b/src/test1.c index 9c38b11a6d..b99efa7106 100644 --- a/src/test1.c +++ b/src/test1.c @@ -5934,6 +5934,145 @@ static int win32_file_lock( CloseHandle(ev); return TCL_OK; } + +/* +** exists_win32_path PATH +** +** Returns non-zero if the specified path exists, whose fully qualified name +** may exceed 260 characters if it is prefixed with "\\?\". +*/ +static int win32_exists_path( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "PATH"); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj( + GetFileAttributesW( Tcl_GetUnicode(objv[1]))!=INVALID_FILE_ATTRIBUTES )); + return TCL_OK; +} + +/* +** find_win32_file PATTERN +** +** Returns a list of entries in a directory that match the specified pattern, +** whose fully qualified name may exceed 248 characters if it is prefixed with +** "\\?\". +*/ +static int win32_find_file( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + HANDLE hFindFile = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW findData; + Tcl_Obj *listObj; + DWORD lastErrno; + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "PATTERN"); + return TCL_ERROR; + } + hFindFile = FindFirstFileW(Tcl_GetUnicode(objv[1]), &findData); + if( hFindFile==INVALID_HANDLE_VALUE ){ + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError())); + return TCL_ERROR; + } + listObj = Tcl_NewObj(); + Tcl_IncrRefCount(listObj); + do { + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewUnicodeObj( + findData.cFileName, -1)); + Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj( + findData.dwFileAttributes)); + } while( FindNextFileW(hFindFile, &findData) ); + lastErrno = GetLastError(); + if( lastErrno!=NO_ERROR && lastErrno!=ERROR_NO_MORE_FILES ){ + FindClose(hFindFile); + Tcl_DecrRefCount(listObj); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError())); + return TCL_ERROR; + } + FindClose(hFindFile); + Tcl_SetObjResult(interp, listObj); + return TCL_OK; +} + +/* +** delete_win32_file FILENAME +** +** Deletes the specified file, whose fully qualified name may exceed 260 +** characters if it is prefixed with "\\?\". +*/ +static int win32_delete_file( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "FILENAME"); + return TCL_ERROR; + } + if( !DeleteFileW(Tcl_GetUnicode(objv[1])) ){ + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError())); + return TCL_ERROR; + } + Tcl_ResetResult(interp); + return TCL_OK; +} + +/* +** make_win32_dir DIRECTORY +** +** Creates the specified directory, whose fully qualified name may exceed 248 +** characters if it is prefixed with "\\?\". +*/ +static int win32_mkdir( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY"); + return TCL_ERROR; + } + if( !CreateDirectoryW(Tcl_GetUnicode(objv[1]), NULL) ){ + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError())); + return TCL_ERROR; + } + Tcl_ResetResult(interp); + return TCL_OK; +} + +/* +** remove_win32_dir DIRECTORY +** +** Removes the specified directory, whose fully qualified name may exceed 248 +** characters if it is prefixed with "\\?\". +*/ +static int win32_rmdir( + void *clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY"); + return TCL_ERROR; + } + if( !RemoveDirectoryW(Tcl_GetUnicode(objv[1])) ){ + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError())); + return TCL_ERROR; + } + Tcl_ResetResult(interp); + return TCL_OK; +} #endif @@ -6193,6 +6332,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "optimization_control", optimization_control,0}, #if SQLITE_OS_WIN { "lock_win32_file", win32_file_lock, 0 }, + { "exists_win32_path", win32_exists_path, 0 }, + { "find_win32_file", win32_find_file, 0 }, + { "delete_win32_file", win32_delete_file, 0 }, + { "make_win32_dir", win32_mkdir, 0 }, + { "remove_win32_dir", win32_rmdir, 0 }, #endif { "tcl_objproc", runAsObjProc, 0 }, diff --git a/src/test_config.c b/src/test_config.c index a1aa03aa14..c64222b373 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -470,7 +470,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "sqlite_options", "session", "0", TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT4 + Tcl_SetVar2(interp, "sqlite_options", "stat4", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "stat4", "0", TCL_GLOBAL_ONLY); +#endif +#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4) Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY); diff --git a/src/test_func.c b/src/test_func.c index 6f9bb03dc8..b250e33106 100644 --- a/src/test_func.c +++ b/src/test_func.c @@ -18,6 +18,9 @@ #include #include +#include "sqliteInt.h" +#include "vdbeInt.h" + /* ** Allocate nByte bytes of space using sqlite3_malloc(). If the @@ -458,6 +461,147 @@ static void real2hex( sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT); } +/* +** tclcmd: test_extract(record, field) +** +** This function implements an SQL user-function that accepts a blob +** containing a formatted database record as the first argument. The +** second argument is the index of the field within that record to +** extract and return. +*/ +static void test_extract( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + u8 *pRec; + u8 *pEndHdr; /* Points to one byte past record header */ + u8 *pHdr; /* Current point in record header */ + u8 *pBody; /* Current point in record data */ + u64 nHdr; /* Bytes in record header */ + int iIdx; /* Required field */ + int iCurrent = 0; /* Current field */ + + assert( argc==2 ); + pRec = (u8*)sqlite3_value_blob(argv[0]); + iIdx = sqlite3_value_int(argv[1]); + + pHdr = pRec + sqlite3GetVarint(pRec, &nHdr); + pBody = pEndHdr = &pRec[nHdr]; + + for(iCurrent=0; pHdr> 4) & 0x0F)]; + hex[1] = hexdigit[(z[i] & 0x0F)]; + hex[2] = '\0'; + Tcl_AppendStringsToObj(pVal, hex, 0); + } + Tcl_AppendStringsToObj(pVal, "'", 0); + break; + } + + case SQLITE_FLOAT: + pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem)); + break; + + case SQLITE_INTEGER: + pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem)); + break; + + case SQLITE_NULL: + pVal = Tcl_NewStringObj("NULL", -1); + break; + + default: + assert( 0 ); + } + + Tcl_ListObjAppendElement(0, pRet, pVal); + + if( mem.zMalloc ){ + sqlite3DbFree(db, mem.zMalloc); + } + } + + sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); + Tcl_DecrRefCount(pRet); +} + static int registerTestFunctions(sqlite3 *db){ static const struct { @@ -482,6 +626,8 @@ static int registerTestFunctions(sqlite3 *db){ { "test_isolation", 2, SQLITE_UTF8, test_isolation}, { "test_counter", 1, SQLITE_UTF8, counterFunc}, { "real2hex", 1, SQLITE_UTF8, real2hex}, + { "test_decode", 1, SQLITE_UTF8, test_decode}, + { "test_extract", 2, SQLITE_UTF8, test_extract}, }; int i; diff --git a/src/test_malloc.c b/src/test_malloc.c index 2e31f0833d..f513e24bf4 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -749,7 +749,7 @@ static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){ int isNew; int aKey[MALLOC_LOG_KEYINTS]; - int nKey = sizeof(int)*MALLOC_LOG_KEYINTS; + unsigned int nKey = sizeof(int)*MALLOC_LOG_KEYINTS; memset(aKey, 0, nKey); if( (sizeof(void*)*nFrame)pSelect ){ - sqlite3_value *pValue; + sqlite3_value *pValue = 0; u8 enc = ENC(sqlite3VdbeDb(v)); Column *pCol = &pTab->aCol[i]; VdbeComment((v, "%s.%s", pTab->zName, pCol->zName)); diff --git a/src/utf.c b/src/utf.c index 6d5b1bfe40..ecb3ea03b8 100644 --- a/src/utf.c +++ b/src/utf.c @@ -450,32 +450,6 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){ return m.z; } -/* -** Convert a UTF-8 string to the UTF-16 encoding specified by parameter -** enc. A pointer to the new string is returned, and the value of *pnOut -** is set to the length of the returned string in bytes. The call should -** arrange to call sqlite3DbFree() on the returned pointer when it is -** no longer required. -** -** If a malloc failure occurs, NULL is returned and the db.mallocFailed -** flag set. -*/ -#ifdef SQLITE_ENABLE_STAT3 -char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){ - Mem m; - memset(&m, 0, sizeof(m)); - m.db = db; - sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC); - if( sqlite3VdbeMemTranslate(&m, enc) ){ - assert( db->mallocFailed ); - return 0; - } - assert( m.z==m.zMalloc ); - *pnOut = m.n; - return m.z; -} -#endif - /* ** zIn is a UTF-16 encoded unicode string at least nChar characters long. ** Return the number of bytes in the first nChar unicode characters diff --git a/src/vdbe.c b/src/vdbe.c index c93ba42346..d7ce895b81 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -648,7 +648,7 @@ int sqlite3VdbeExec( assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); + assert( pOp->p2<=(p->nMem-p->nCursor) ); pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); VdbeMemRelease(pOut); @@ -659,30 +659,30 @@ int sqlite3VdbeExec( #ifdef SQLITE_DEBUG if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); - assert( pOp->p1<=p->nMem ); + assert( pOp->p1<=(p->nMem-p->nCursor) ); assert( memIsValid(&aMem[pOp->p1]) ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); + assert( pOp->p2<=(p->nMem-p->nCursor) ); assert( memIsValid(&aMem[pOp->p2]) ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); + assert( pOp->p3<=(p->nMem-p->nCursor) ); assert( memIsValid(&aMem[pOp->p3]) ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); + assert( pOp->p2<=(p->nMem-p->nCursor) ); memAboutToChange(p, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); + assert( pOp->p3<=(p->nMem-p->nCursor) ); memAboutToChange(p, &aMem[pOp->p3]); } #endif @@ -775,7 +775,7 @@ check_for_interrupt: ** and then jump to address P2. */ case OP_Gosub: { /* jump */ - assert( pOp->p1>0 && pOp->p1<=p->nMem ); + assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) ); pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); memAboutToChange(p, pIn1); @@ -987,7 +987,7 @@ case OP_Null: { /* out2-prerelease */ int cnt; u16 nullFlag; cnt = pOp->p3-pOp->p2; - assert( pOp->p3<=p->nMem ); + assert( pOp->p3<=(p->nMem-p->nCursor) ); pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null; while( cnt>0 ){ pOut++; @@ -1056,8 +1056,8 @@ case OP_Move: { pIn1 = &aMem[p1]; pOut = &aMem[p2]; while( n-- ){ - assert( pOut<=&aMem[p->nMem] ); - assert( pIn1<=&aMem[p->nMem] ); + assert( pOut<=&aMem[(p->nMem-p->nCursor)] ); + assert( pIn1<=&aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); memAboutToChange(p, pOut); zMalloc = pOut->zMalloc; @@ -1141,7 +1141,7 @@ case OP_ResultRow: { int i; assert( p->nResColumn==pOp->p2 ); assert( pOp->p1>0 ); - assert( pOp->p1+pOp->p2<=p->nMem+1 ); + assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 ); /* If this statement has violated immediate foreign key constraints, do ** not return the number of rows modified. And do not RELEASE the statement @@ -1415,11 +1415,11 @@ case OP_Function: { n = pOp->p5; apVal = p->apArg; assert( apVal || n==0 ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); - assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); + assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) ); assert( pOp->p3p2 || pOp->p3>=pOp->p2+n ); pArg = &aMem[pOp->p2]; for(i=0; imx ) mx = aPermute[k]; - assert( p1>0 && p1+mx<=p->nMem+1 ); - assert( p2>0 && p2+mx<=p->nMem+1 ); + assert( p1>0 && p1+mx<=(p->nMem-p->nCursor)+1 ); + assert( p2>0 && p2+mx<=(p->nMem-p->nCursor)+1 ); }else{ - assert( p1>0 && p1+n<=p->nMem+1 ); - assert( p2>0 && p2+n<=p->nMem+1 ); + assert( p1>0 && p1+n<=(p->nMem-p->nCursor)+1 ); + assert( p2>0 && p2+n<=(p->nMem-p->nCursor)+1 ); } #endif /* SQLITE_DEBUG */ for(i=0; inCursor ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); zRec = 0; @@ -2502,7 +2502,7 @@ case OP_Affinity: { assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (cAff = *(zAffinity++))!=0 ){ - assert( pIn1 <= &p->aMem[p->nMem] ); + assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); ExpandBlob(pIn1); applyAffinity(pIn1, cAff, encoding); @@ -2563,7 +2563,7 @@ case OP_MakeRecord: { nZero = 0; /* Number of zero bytes at the end of the record */ nField = pOp->p1; zAffinity = pOp->p4.z; - assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); + assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem-p->nCursor)+1 ); pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; @@ -2629,7 +2629,7 @@ case OP_MakeRecord: { } assert( i==nByte ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pOut->n = (int)nByte; pOut->flags = MEM_Blob | MEM_Dyn; pOut->xDel = 0; @@ -3209,7 +3209,7 @@ case OP_OpenWrite: { } if( pOp->p5 & OPFLAG_P2ISREG ){ assert( p2>0 ); - assert( p2<=p->nMem ); + assert( p2<=(p->nMem-p->nCursor) ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); @@ -3746,7 +3746,7 @@ case OP_IsUnique: { /* jump, in3 */ aMx = &aMem[pOp->p4.i]; /* Assert that the values of parameters P1 and P4 are in range. */ assert( pOp->p4type==P4_INT32 ); - assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); + assert( pOp->p4.i>0 && pOp->p4.i<=(p->nMem-p->nCursor) ); assert( pOp->p1>=0 && pOp->p1nCursor ); /* Find the index cursor. */ @@ -3949,7 +3949,7 @@ case OP_NewRowid: { /* out2-prerelease */ pMem = &pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=p->nMem ); + assert( pOp->p3<=(p->nMem-p->nCursor) ); pMem = &aMem[pOp->p3]; memAboutToChange(p, pMem); } @@ -4645,7 +4645,7 @@ case OP_IdxDelete: { UnpackedRecord r; assert( pOp->p3>0 ); - assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 ); + assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem-p->nCursor)+1 ); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); @@ -4853,6 +4853,7 @@ case OP_Clear: { nChange = 0; assert( p->readOnly==0 ); + assert( pOp->p1!=1 ); assert( (p->btreeMask & (((yDbMask)1)<p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) @@ -5053,7 +5054,7 @@ case OP_IntegrityCk: { assert( nRoot>0 ); aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) ); if( aRoot==0 ) goto no_mem; - assert( pOp->p3>0 && pOp->p3<=p->nMem ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pnErr = &aMem[pOp->p3]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); @@ -5477,7 +5478,7 @@ case OP_AggStep: { sqlite3VdbeMemStoreType(pRec); } ctx.pFunc = pOp->p4.pFunc; - assert( pOp->p3>0 && pOp->p3<=p->nMem ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); ctx.pMem = pMem = &aMem[pOp->p3]; pMem->n++; ctx.s.flags = MEM_Null; @@ -5524,7 +5525,7 @@ case OP_AggStep: { */ case OP_AggFinal: { Mem *pMem; - assert( pOp->p1>0 && pOp->p1<=p->nMem ); + assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) ); pMem = &aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); @@ -5941,7 +5942,7 @@ case OP_VColumn: { VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); - assert( pOp->p3>0 && pOp->p3<=p->nMem ); + assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); if( pCur->nullRow ){ diff --git a/src/vdbemem.c b/src/vdbemem.c index 0fa51e8ae1..e6ad291532 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1001,27 +1001,92 @@ sqlite3_value *sqlite3ValueNew(sqlite3 *db){ } /* -** Create a new sqlite3_value object, containing the value of pExpr. -** -** This only works for very simple expressions that consist of one constant -** token (i.e. "5", "5.1", "'a string'"). If the expression can -** be converted directly into a value, then the value is allocated and -** a pointer written to *ppVal. The caller is responsible for deallocating -** the value by passing it to sqlite3ValueFree() later on. If the expression -** cannot be converted to a value, then *ppVal is set to NULL. +** Context object passed by sqlite3Stat4ProbeSetValue() through to +** valueNew(). See comments above valueNew() for details. */ -int sqlite3ValueFromExpr( - sqlite3 *db, /* The database connection */ - Expr *pExpr, /* The expression to evaluate */ - u8 enc, /* Encoding to use */ - u8 affinity, /* Affinity to use */ - sqlite3_value **ppVal /* Write the new value here */ +struct ValueNewStat4Ctx { + Parse *pParse; + Index *pIdx; + UnpackedRecord **ppRec; + int iVal; +}; + +/* +** Allocate and return a pointer to a new sqlite3_value object. If +** the second argument to this function is NULL, the object is allocated +** by calling sqlite3ValueNew(). +** +** Otherwise, if the second argument is non-zero, then this function is +** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not +** already been allocated, allocate the UnpackedRecord structure that +** that function will return to its caller here. Then return a pointer +** an sqlite3_value within the UnpackedRecord.a[] array. +*/ +static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + if( p ){ + UnpackedRecord *pRec = p->ppRec[0]; + + if( pRec==0 ){ + Index *pIdx = p->pIdx; /* Index being probed */ + int nByte; /* Bytes of space to allocate */ + int i; /* Counter variable */ + int nCol = pIdx->nColumn+1; /* Number of index columns including rowid */ + + nByte = sizeof(Mem) * nCol + sizeof(UnpackedRecord); + pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte); + if( pRec ){ + pRec->pKeyInfo = sqlite3IndexKeyinfo(p->pParse, pIdx); + if( pRec->pKeyInfo ){ + assert( pRec->pKeyInfo->nField+1==nCol ); + pRec->pKeyInfo->enc = ENC(db); + pRec->flags = UNPACKED_PREFIX_MATCH; + pRec->aMem = (Mem *)&pRec[1]; + for(i=0; iaMem[i].flags = MEM_Null; + pRec->aMem[i].type = SQLITE_NULL; + pRec->aMem[i].db = db; + } + }else{ + sqlite3DbFree(db, pRec); + pRec = 0; + } + } + if( pRec==0 ) return 0; + p->ppRec[0] = pRec; + } + + pRec->nField = p->iVal+1; + return &pRec->aMem[p->iVal]; + } +#endif + return sqlite3ValueNew(db); +} + +/* +** Extract a value from the supplied expression in the manner described +** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object +** using valueNew(). +** +** If pCtx is NULL and an error occurs after the sqlite3_value object +** has been allocated, it is freed before returning. Or, if pCtx is not +** NULL, it is assumed that the caller will free any allocated object +** in all cases. +*/ +int valueFromExpr( + sqlite3 *db, /* The database connection */ + Expr *pExpr, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 affinity, /* Affinity to use */ + sqlite3_value **ppVal, /* Write the new value here */ + struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */ ){ int op; char *zVal = 0; sqlite3_value *pVal = 0; int negInt = 1; const char *zNeg = ""; + int rc = SQLITE_OK; if( !pExpr ){ *ppVal = 0; @@ -1029,11 +1094,11 @@ int sqlite3ValueFromExpr( } op = pExpr->op; - /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3. + /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT4. ** The ifdef here is to enable us to achieve 100% branch test coverage even - ** when SQLITE_ENABLE_STAT3 is omitted. + ** when SQLITE_ENABLE_STAT4 is omitted. */ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; @@ -1051,7 +1116,7 @@ int sqlite3ValueFromExpr( } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ - pVal = sqlite3ValueNew(db); + pVal = valueNew(db, pCtx); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); @@ -1068,11 +1133,13 @@ int sqlite3ValueFromExpr( } if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; if( enc!=SQLITE_UTF8 ){ - sqlite3VdbeChangeEncoding(pVal, enc); + rc = sqlite3VdbeChangeEncoding(pVal, enc); } }else if( op==TK_UMINUS ) { /* This branch happens for multiple negative signs. Ex: -(-5) */ - if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ + if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) + && pVal!=0 + ){ sqlite3VdbeMemNumerify(pVal); if( pVal->u.i==SMALLEST_INT64 ){ pVal->flags &= MEM_Int; @@ -1085,7 +1152,7 @@ int sqlite3ValueFromExpr( sqlite3ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ - pVal = sqlite3ValueNew(db); + pVal = valueNew(db, pCtx); if( pVal==0 ) goto no_mem; } #ifndef SQLITE_OMIT_BLOB_LITERAL @@ -1093,7 +1160,7 @@ int sqlite3ValueFromExpr( int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); - pVal = sqlite3ValueNew(db); + pVal = valueNew(db, pCtx); if( !pVal ) goto no_mem; zVal = &pExpr->u.zToken[2]; nVal = sqlite3Strlen30(zVal)-1; @@ -1107,16 +1174,199 @@ int sqlite3ValueFromExpr( sqlite3VdbeMemStoreType(pVal); } *ppVal = pVal; - return SQLITE_OK; + return rc; no_mem: db->mallocFailed = 1; sqlite3DbFree(db, zVal); - sqlite3ValueFree(pVal); - *ppVal = 0; + assert( *ppVal==0 ); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + if( pCtx==0 ) sqlite3ValueFree(pVal); +#else + assert( pCtx==0 ); sqlite3ValueFree(pVal); +#endif return SQLITE_NOMEM; } +/* +** Create a new sqlite3_value object, containing the value of pExpr. +** +** This only works for very simple expressions that consist of one constant +** token (i.e. "5", "5.1", "'a string'"). If the expression can +** be converted directly into a value, then the value is allocated and +** a pointer written to *ppVal. The caller is responsible for deallocating +** the value by passing it to sqlite3ValueFree() later on. If the expression +** cannot be converted to a value, then *ppVal is set to NULL. +*/ +int sqlite3ValueFromExpr( + sqlite3 *db, /* The database connection */ + Expr *pExpr, /* The expression to evaluate */ + u8 enc, /* Encoding to use */ + u8 affinity, /* Affinity to use */ + sqlite3_value **ppVal /* Write the new value here */ +){ + return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0); +} + +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +/* +** The implementation of the sqlite_record() function. This function accepts +** a single argument of any type. The return value is a formatted database +** record (a blob) containing the argument value. +** +** This is used to convert the value stored in the 'sample' column of the +** sqlite_stat3 table to the record format SQLite uses internally. +*/ +static void recordFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const int file_format = 1; + int iSerial; /* Serial type */ + int nSerial; /* Bytes of space for iSerial as varint */ + int nVal; /* Bytes of space required for argv[0] */ + int nRet; + sqlite3 *db; + u8 *aRet; + + iSerial = sqlite3VdbeSerialType(argv[0], file_format); + nSerial = sqlite3VarintLen(iSerial); + nVal = sqlite3VdbeSerialTypeLen(iSerial); + db = sqlite3_context_db_handle(context); + + nRet = 1 + nSerial + nVal; + aRet = sqlite3DbMallocRaw(db, nRet); + if( aRet==0 ){ + sqlite3_result_error_nomem(context); + }else{ + aRet[0] = nSerial+1; + sqlite3PutVarint(&aRet[1], iSerial); + sqlite3VdbeSerialPut(&aRet[1+nSerial], nVal, argv[0], file_format); + sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT); + sqlite3DbFree(db, aRet); + } +} + +/* +** Register built-in functions used to help read ANALYZE data. +*/ +void sqlite3AnalyzeFunctions(void){ + static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = { + FUNCTION(sqlite_record, 1, 0, 0, recordFunc), + }; + int i; + FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); + FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs); + for(i=0; idb, &alloc); + if( pVal ){ + sqlite3VdbeMemSetNull((Mem*)pVal); + *pbOk = 1; + } + }else if( pExpr->op==TK_VARIABLE + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) + ){ + Vdbe *v; + int iBindVar = pExpr->iColumn; + sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar); + if( (v = pParse->pReprepare)!=0 ){ + pVal = valueNew(pParse->db, &alloc); + if( pVal ){ + rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]); + if( rc==SQLITE_OK ){ + sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); + } + pVal->db = pParse->db; + *pbOk = 1; + sqlite3VdbeMemStoreType((Mem*)pVal); + } + }else{ + *pbOk = 0; + } + }else{ + sqlite3 *db = pParse->db; + rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc); + *pbOk = (pVal!=0); + } + + assert( pVal==0 || pVal->db==pParse->db ); + return rc; +} + +/* +** Unless it is NULL, the argument must be an UnpackedRecord object returned +** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes +** the object. +*/ +void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){ + if( pRec ){ + int i; + int nCol = pRec->pKeyInfo->nField+1; + Mem *aMem = pRec->aMem; + sqlite3 *db = aMem[0].db; + for(i=0; ipKeyInfo); + sqlite3DbFree(db, pRec); + } +} +#endif /* ifdef SQLITE_ENABLE_STAT4 */ + /* ** Change the string value of an sqlite3_value object */ diff --git a/src/where.c b/src/where.c index 05e6824f01..9e3a72e2d8 100644 --- a/src/where.c +++ b/src/where.c @@ -286,7 +286,7 @@ struct WhereTerm { #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 # define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ #else # define TERM_VNULL 0x00 /* Disabled if not using stat3 */ @@ -392,6 +392,10 @@ struct WhereLoopBuilder { ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + UnpackedRecord *pRec; /* Probe for stat4 (if required) */ + int nRecValid; /* Number of valid fields currently in pRec */ +#endif }; /* @@ -1205,8 +1209,10 @@ static int isMatchOfColumn( ** a join, then transfer the appropriate markings over to derived. */ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ - pDerived->flags |= pBase->flags & EP_FromJoin; - pDerived->iRightJoinTable = pBase->iRightJoinTable; + if( pDerived ){ + pDerived->flags |= pBase->flags & EP_FromJoin; + pDerived->iRightJoinTable = pBase->iRightJoinTable; + } } #if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY) @@ -1663,6 +1669,7 @@ static void exprAnalyze( pNewExpr = sqlite3PExpr(pParse, ops[i], sqlite3ExprDup(db, pExpr->pLeft, 0), sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0); + transferJoinMarkings(pNewExpr, pExpr); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); exprAnalyze(pSrc, pWC, idxNew); @@ -1730,6 +1737,7 @@ static void exprAnalyze( pNewExpr1 = sqlite3PExpr(pParse, TK_GE, sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName), pStr1, 0); + transferJoinMarkings(pNewExpr1, pExpr); idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew1==0 ); exprAnalyze(pSrc, pWC, idxNew1); @@ -1737,6 +1745,7 @@ static void exprAnalyze( pNewExpr2 = sqlite3PExpr(pParse, TK_LT, sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName), pStr2, 0); + transferJoinMarkings(pNewExpr2, pExpr); idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew2==0 ); exprAnalyze(pSrc, pWC, idxNew2); @@ -1786,7 +1795,7 @@ static void exprAnalyze( } #endif /* SQLITE_OMIT_VIRTUALTABLE */ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 /* When sqlite_stat3 histogram data is available an operator of the ** form "x IS NOT NULL" can sometimes be evaluated more efficiently ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a @@ -1826,7 +1835,7 @@ static void exprAnalyze( pNewTerm->prereqAll = pTerm->prereqAll; } } -#endif /* SQLITE_ENABLE_STAT */ +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. @@ -2394,7 +2403,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 /* ** Estimate the location of a particular key among all keys in an ** index. Store the results in aStat as follows: @@ -2404,141 +2413,70 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ ** ** Return SQLITE_OK on success. */ -static int whereKeyStats( +static void whereKeyStats( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ - sqlite3_value *pVal, /* Value to consider */ + UnpackedRecord *pRec, /* Vector of values to consider */ int roundUp, /* Round up if true. Round down if false */ tRowcnt *aStat /* OUT: stats written here */ ){ - tRowcnt n; - IndexSample *aSample; - int i, eType; - int isEq = 0; - i64 v; - double r, rS; + IndexSample *aSample = pIdx->aSample; + int iCol = pRec->nField-1; /* Index of required stats in anEq[] etc. */ + int iMin = 0; /* Smallest sample not yet tested */ + int i = pIdx->nSample; /* Smallest sample larger than or equal to pRec */ + int iTest; /* Next sample to test */ + int res; /* Result of comparison operation */ - assert( roundUp==0 || roundUp==1 ); assert( pIdx->nSample>0 ); - if( pVal==0 ) return SQLITE_ERROR; - n = pIdx->aiRowEst[0]; - aSample = pIdx->aSample; - eType = sqlite3_value_type(pVal); + assert( pRec->nField>0 && iColnSampleCol ); + do{ + iTest = (iMin+i)/2; + res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec); + if( res<0 ){ + iMin = iTest+1; + }else{ + i = iTest; + } + }while( res && iMinnSample; i++){ - if( aSample[i].eType==SQLITE_NULL ) continue; - if( aSample[i].eType>=SQLITE_TEXT ) break; - if( aSample[i].eType==SQLITE_INTEGER ){ - if( aSample[i].u.i>=v ){ - isEq = aSample[i].u.i==v; - break; - } - }else{ - assert( aSample[i].eType==SQLITE_FLOAT ); - if( aSample[i].u.r>=r ){ - isEq = aSample[i].u.r==r; - break; - } - } - } - }else if( eType==SQLITE_FLOAT ){ - r = sqlite3_value_double(pVal); - for(i=0; inSample; i++){ - if( aSample[i].eType==SQLITE_NULL ) continue; - if( aSample[i].eType>=SQLITE_TEXT ) break; - if( aSample[i].eType==SQLITE_FLOAT ){ - rS = aSample[i].u.r; - }else{ - rS = aSample[i].u.i; - } - if( rS>=r ){ - isEq = rS==r; - break; - } - } - }else if( eType==SQLITE_NULL ){ - i = 0; - if( aSample[0].eType==SQLITE_NULL ) isEq = 1; +#ifdef SQLITE_DEBUG + /* The following assert statements check that the binary search code + ** above found the right answer. This block serves no purpose other + ** than to invoke the asserts. */ + if( res==0 ){ + /* If (res==0) is true, then sample $i must be equal to pRec */ + assert( inSample ); + assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) + || pParse->db->mallocFailed ); }else{ - assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); - for(i=0; inSample; i++){ - if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){ - break; - } - } - if( inSample ){ - sqlite3 *db = pParse->db; - CollSeq *pColl; - const u8 *z; - if( eType==SQLITE_BLOB ){ - z = (const u8 *)sqlite3_value_blob(pVal); - pColl = db->pDfltColl; - assert( pColl->enc==SQLITE_UTF8 ); - }else{ - pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl); - /* If the collating sequence was unavailable, we should have failed - ** long ago and never reached this point. But we'll check just to - ** be doubly sure. */ - if( NEVER(pColl==0) ) return SQLITE_ERROR; - z = (const u8 *)sqlite3ValueText(pVal, pColl->enc); - if( !z ){ - return SQLITE_NOMEM; - } - assert( z && pColl && pColl->xCmp ); - } - n = sqlite3ValueBytes(pVal, pColl->enc); - - for(; inSample; i++){ - int c; - int eSampletype = aSample[i].eType; - if( eSampletypeenc!=SQLITE_UTF8 ){ - int nSample; - char *zSample = sqlite3Utf8to16( - db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample - ); - if( !zSample ){ - assert( db->mallocFailed ); - return SQLITE_NOMEM; - } - c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); - sqlite3DbFree(db, zSample); - }else -#endif - { - c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); - } - if( c>=0 ){ - if( c==0 ) isEq = 1; - break; - } - } - } + /* Otherwise, pRec must be smaller than sample $i and larger than + ** sample ($i-1). */ + assert( i==pIdx->nSample + || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 + || pParse->db->mallocFailed ); + assert( i==0 + || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 + || pParse->db->mallocFailed ); } +#endif /* ifdef SQLITE_DEBUG */ /* At this point, aSample[i] is the first sample that is greater than ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less - ** than pVal. If aSample[i]==pVal, then isEq==1. + ** than pVal. If aSample[i]==pVal, then res==0. */ - if( isEq ){ - assert( inSample ); - aStat[0] = aSample[i].nLt; - aStat[1] = aSample[i].nEq; + if( res==0 ){ + aStat[0] = aSample[i].anLt[iCol]; + aStat[1] = aSample[i].anEq[iCol]; }else{ tRowcnt iLower, iUpper, iGap; if( i==0 ){ iLower = 0; - iUpper = aSample[0].nLt; + iUpper = aSample[0].anLt[iCol]; }else{ - iUpper = i>=pIdx->nSample ? n : aSample[i].nLt; - iLower = aSample[i-1].nEq + aSample[i-1].nLt; + iUpper = i>=pIdx->nSample ? pIdx->aiRowEst[0] : aSample[i].anLt[iCol]; + iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol]; } - aStat[1] = pIdx->avgEq; + aStat[1] = (pIdx->nColumn>iCol ? pIdx->aAvgEq[iCol] : 1); if( iLower>=iUpper ){ iGap = 0; }else{ @@ -2551,44 +2489,8 @@ static int whereKeyStats( } aStat[0] = iLower + iGap; } - return SQLITE_OK; } -#endif /* SQLITE_ENABLE_STAT3 */ - -/* -** If expression pExpr represents a literal value, set *pp to point to -** an sqlite3_value structure containing the same value, with affinity -** aff applied to it, before returning. It is the responsibility of the -** caller to eventually release this structure by passing it to -** sqlite3ValueFree(). -** -** If the current parse is a recompile (sqlite3Reprepare()) and pExpr -** is an SQL variable that currently has a non-NULL value bound to it, -** create an sqlite3_value structure containing this value, again with -** affinity aff applied to it, instead. -** -** If neither of the above apply, set *pp to NULL. -** -** If an error occurs, return an error code. Otherwise, SQLITE_OK. -*/ -#ifdef SQLITE_ENABLE_STAT3 -static int valueFromExpr( - Parse *pParse, - Expr *pExpr, - u8 aff, - sqlite3_value **pp -){ - if( pExpr->op==TK_VARIABLE - || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) - ){ - int iVar = pExpr->iColumn; - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); - *pp = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, aff); - return SQLITE_OK; - } - return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); -} -#endif +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ /* ** This function is used to estimate the number of rows that will be visited @@ -2605,103 +2507,150 @@ static int valueFromExpr( ** If either of the upper or lower bound is not present, then NULL is passed in ** place of the corresponding WhereTerm. ** -** The nEq parameter is passed the index of the index column subject to the -** range constraint. Or, equivalently, the number of equality constraints -** optimized by the proposed index scan. For example, assuming index p is -** on t1(a, b), and the SQL query is: +** The value in (pBuilder->pNew->u.btree.nEq) is the index of the index +** column subject to the range constraint. Or, equivalently, the number of +** equality constraints optimized by the proposed index scan. For example, +** assuming index p is on t1(a, b), and the SQL query is: ** ** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ... ** -** then nEq should be passed the value 1 (as the range restricted column, -** b, is the second left-most column of the index). Or, if the query is: +** then nEq is set to 1 (as the range restricted column, b, is the second +** left-most column of the index). Or, if the query is: ** ** ... FROM t1 WHERE a > ? AND a < ? ... ** -** then nEq should be passed 0. +** then nEq is set to 0. ** -** The returned value is an integer divisor to reduce the estimated -** search space. A return value of 1 means that range constraints are -** no help at all. A return value of 2 means range constraints are -** expected to reduce the search space by half. And so forth... -** -** In the absence of sqlite_stat3 ANALYZE data, each range inequality -** reduces the search space by a factor of 4. Hence a single constraint (x>?) -** results in a return of 4 and a range constraint (x>? AND x? AND xaCol[] of the range-compared column */ + WhereLoopBuilder *pBuilder, WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ - WhereCost *pRangeDiv /* OUT: Reduce search space by this divisor */ + WhereCost *pnOut /* IN/OUT: Number of rows visited */ ){ int rc = SQLITE_OK; + int nOut = (int)*pnOut; -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + Index *p = pBuilder->pNew->u.btree.pIndex; + int nEq = pBuilder->pNew->u.btree.nEq; - if( nEq==0 && p->nSample && OptimizationEnabled(pParse->db, SQLITE_Stat3) ){ - sqlite3_value *pRangeVal; - tRowcnt iLower = 0; - tRowcnt iUpper = p->aiRowEst[0]; + if( nEq==pBuilder->nRecValid + && nEqnSampleCol + && p->nSample + && OptimizationEnabled(pParse->db, SQLITE_Stat3) + ){ + UnpackedRecord *pRec = pBuilder->pRec; tRowcnt a[2]; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; + /* Variable iLower will be set to the estimate of the number of rows in + ** the index that are less than the lower bound of the range query. The + ** lower bound being the concatenation of $P and $L, where $P is the + ** key-prefix formed by the nEq values matched against the nEq left-most + ** columns of the index, and $L is the value in pLower. + ** + ** Or, if pLower is NULL or $L cannot be extracted from it (because it + ** is not a simple variable or literal value), the lower bound of the + ** range is $P. Due to a quirk in the way whereKeyStats() works, even + ** if $L is available, whereKeyStats() is called for both ($P) and + ** ($P:$L) and the larger of the two returned values used. + ** + ** Similarly, iUpper is to be set to the estimate of the number of rows + ** less than the upper bound of the range query. Where the upper bound + ** is either ($P) or ($P:$U). Again, even if $U is available, both values + ** of iUpper are requested of whereKeyStats() and the smaller used. + */ + tRowcnt iLower; + tRowcnt iUpper; + + /* Determine iLower and iUpper using ($P) only. */ + if( nEq==0 ){ + iLower = 0; + iUpper = p->aiRowEst[0]; + }else{ + /* Note: this call could be optimized away - since the same values must + ** have been requested when testing key $P in whereEqualScanEst(). */ + whereKeyStats(pParse, p, pRec, 0, a); + iLower = a[0]; + iUpper = a[0] + a[1]; + } + + /* If possible, improve on the iLower estimate using ($P:$L). */ if( pLower ){ + int bOk; /* True if value is extracted from pExpr */ Expr *pExpr = pLower->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); 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)!=0 ) iLower += a[1]; + rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk); + if( rc==SQLITE_OK && bOk ){ + tRowcnt iNew; + whereKeyStats(pParse, p, pRec, 0, a); + iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0); + if( iNew>iLower ) iLower = iNew; } - sqlite3ValueFree(pRangeVal); } - if( rc==SQLITE_OK && pUpper ){ + + /* If possible, improve on the iUpper estimate using ($P:$U). */ + if( pUpper ){ + int bOk; /* True if value is extracted from pExpr */ Expr *pExpr = pUpper->pExpr->pRight; - rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal); 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)!=0 ) iUpper += a[1]; + rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk); + if( rc==SQLITE_OK && bOk ){ + tRowcnt iNew; + whereKeyStats(pParse, p, pRec, 1, a); + iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0); + if( iNewpRec = pRec; if( rc==SQLITE_OK ){ - WhereCost iBase = whereCost(p->aiRowEst[0]); + WhereCost nNew; if( iUpper>iLower ){ - iBase -= whereCost(iUpper - iLower); + nNew = whereCost(iUpper - iLower); + }else{ + nNew = 10; assert( 10==whereCost(2) ); } - *pRangeDiv = iBase; - WHERETRACE(0x100, ("range scan regions: %u..%u div=%d\n", - (u32)iLower, (u32)iUpper, *pRangeDiv)); + if( nNewwtFlags & TERM_VNULL)==0 ){ - *pRangeDiv += 20; assert( 20==whereCost(4) ); + nOut -= 20; assert( 20==whereCost(4) ); } if( pUpper ){ - *pRangeDiv += 20; assert( 20==whereCost(4) ); + nOut -= 20; assert( 20==whereCost(4) ); } + if( nOut<10 ) nOut = 10; + *pnOut = (WhereCost)nOut; return rc; } -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 /* ** Estimate the number of rows that will be returned based on ** an equality constraint x=VALUE and where that VALUE occurs in @@ -2721,37 +2670,53 @@ static int whereRangeScanEst( */ static int whereEqualScanEst( Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index whose left-most column is pTerm */ + WhereLoopBuilder *pBuilder, Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ tRowcnt *pnRow /* Write the revised row estimate here */ ){ - sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ + Index *p = pBuilder->pNew->u.btree.pIndex; + int nEq = pBuilder->pNew->u.btree.nEq; + UnpackedRecord *pRec = pBuilder->pRec; u8 aff; /* Column affinity */ int rc; /* Subfunction return code */ tRowcnt a[2]; /* Statistics */ + int bOk; + assert( nEq>=1 ); + assert( nEq<=(p->nColumn+1) ); assert( p->aSample!=0 ); assert( p->nSample>0 ); - aff = p->pTable->aCol[p->aiColumn[0]].affinity; - if( pExpr ){ - rc = valueFromExpr(pParse, pExpr, aff, &pRhs); - if( rc ) goto whereEqualScanEst_cancel; - }else{ - pRhs = sqlite3ValueNew(pParse->db); + assert( pBuilder->nRecValidnRecValid<(nEq-1) ){ + return SQLITE_NOTFOUND; } - if( pRhs==0 ) return SQLITE_NOTFOUND; - rc = whereKeyStats(pParse, p, pRhs, 0, a); - if( rc==SQLITE_OK ){ - WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1])); - *pnRow = a[1]; + + /* This is an optimization only. The call to sqlite3Stat4ProbeSetValue() + ** below would return the same value. */ + if( nEq>p->nColumn ){ + *pnRow = 1; + return SQLITE_OK; } -whereEqualScanEst_cancel: - sqlite3ValueFree(pRhs); + + aff = p->pTable->aCol[p->aiColumn[nEq-1]].affinity; + rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk); + pBuilder->pRec = pRec; + if( rc!=SQLITE_OK ) return rc; + if( bOk==0 ) return SQLITE_NOTFOUND; + pBuilder->nRecValid = nEq; + + whereKeyStats(pParse, p, pRec, 0, a); + WHERETRACE(0x100,("equality scan regions: %d\n", (int)a[1])); + *pnRow = a[1]; + return rc; } -#endif /* defined(SQLITE_ENABLE_STAT3) */ +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ -#ifdef SQLITE_ENABLE_STAT3 +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 /* ** Estimate the number of rows that will be returned based on ** an IN constraint where the right-hand side of the IN operator @@ -2770,10 +2735,12 @@ whereEqualScanEst_cancel: */ static int whereInScanEst( Parse *pParse, /* Parsing & code generating context */ - Index *p, /* The index whose left-most column is pTerm */ + WhereLoopBuilder *pBuilder, ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ tRowcnt *pnRow /* Write the revised row estimate here */ ){ + Index *p = pBuilder->pNew->u.btree.pIndex; + int nRecValid = pBuilder->nRecValid; int rc = SQLITE_OK; /* Subfunction return code */ tRowcnt nEst; /* Number of rows for a single term */ tRowcnt nRowEst = 0; /* New estimate of the number of rows */ @@ -2782,17 +2749,20 @@ static int whereInScanEst( assert( p->aSample!=0 ); for(i=0; rc==SQLITE_OK && inExpr; i++){ nEst = p->aiRowEst[0]; - rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst); + rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst); nRowEst += nEst; + pBuilder->nRecValid = nRecValid; } + if( rc==SQLITE_OK ){ if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; WHERETRACE(0x100,("IN row estimate: est=%g\n", nRowEst)); } + assert( pBuilder->nRecValid==nRecValid ); return rc; } -#endif /* defined(SQLITE_ENABLE_STAT3) */ +#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ /* ** Disable a term in the WHERE clause. Except, do not disable the term @@ -3030,7 +3000,7 @@ static int codeAllEqualityTerms( /* Evaluate the equality constraints */ - assert( zAff==0 || strlen(zAff)>=nEq ); + assert( zAff==0 || (int)strlen(zAff)>=nEq ); for(j=0; jaLTerm[j]; @@ -4337,12 +4307,18 @@ static int whereLoopAddBtreeIndex( rLogSize = estLog(whereCost(pProbe->aiRowEst[0])); for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){ int nIn = 0; - if( pTerm->prereqRight & pNew->maskSelf ) continue; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + int nRecValid = pBuilder->nRecValid; +#endif if( (pTerm->eOperator==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0) && (iCol<0 || pSrc->pTab->aCol[iCol].notNull) ){ continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */ } + if( pTerm->prereqRight & pNew->maskSelf ) continue; + + assert( pNew->nOut==saved_nOut ); + pNew->wsFlags = saved_wsFlags; pNew->u.btree.nEq = saved_nEq; pNew->nLTerm = saved_nLTerm; @@ -4399,25 +4375,30 @@ static int whereLoopAddBtreeIndex( } if( pNew->wsFlags & WHERE_COLUMN_RANGE ){ /* Adjust nOut and rRun for STAT3 range values */ - WhereCost rDiv; - whereRangeScanEst(pParse, pProbe, pNew->u.btree.nEq, - pBtm, pTop, &rDiv); - pNew->nOut = saved_nOut>rDiv+10 ? saved_nOut - rDiv : 10; + assert( pNew->nOut==saved_nOut ); + whereRangeScanEst(pParse, pBuilder, pBtm, pTop, &pNew->nOut); } -#ifdef SQLITE_ENABLE_STAT3 - if( pNew->u.btree.nEq==1 && pProbe->nSample - && OptimizationEnabled(db, SQLITE_Stat3) ){ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + if( nInMul==0 + && pProbe->nSample + && pNew->u.btree.nEq<=pProbe->nSampleCol + && OptimizationEnabled(db, SQLITE_Stat3) + ){ + Expr *pExpr = pTerm->pExpr; tRowcnt nOut = 0; if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){ testcase( pTerm->eOperator & WO_EQ ); testcase( pTerm->eOperator & WO_ISNULL ); - rc = whereEqualScanEst(pParse, pProbe, pTerm->pExpr->pRight, &nOut); + rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut); }else if( (pTerm->eOperator & WO_IN) - && !ExprHasProperty(pTerm->pExpr, EP_xIsSelect) ){ - rc = whereInScanEst(pParse, pProbe, pTerm->pExpr->x.pList, &nOut); + && !ExprHasProperty(pExpr, EP_xIsSelect) ){ + rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut); } assert( nOut==0 || rc==SQLITE_OK ); - if( nOut ) pNew->nOut = whereCost(nOut); + if( nOut ){ + nOut = whereCost(nOut); + pNew->nOut = MIN(nOut, saved_nOut); + } } #endif if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){ @@ -4434,6 +4415,10 @@ static int whereLoopAddBtreeIndex( ){ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn); } + pNew->nOut = saved_nOut; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + pBuilder->nRecValid = nRecValid; +#endif } pNew->prereq = saved_prereq; pNew->u.btree.nEq = saved_nEq; @@ -4663,7 +4648,13 @@ static int whereLoopAddBtree( if( rc ) break; } } + rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3Stat4ProbeFree(pBuilder->pRec); + pBuilder->nRecValid = 0; + pBuilder->pRec = 0; +#endif /* If there was an INDEXED BY clause, then only that one index is ** considered. */ @@ -5546,11 +5537,15 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ pLoop->rRun = 33; /* 33==whereCost(10) */ }else{ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->onError==OE_None || pIdx->pPartIdxWhere!=0 ) continue; + assert( pLoop->aLTermSpace==pLoop->aLTerm ); + assert( ArraySize(pLoop->aLTermSpace)==4 ); + if( pIdx->onError==OE_None + || pIdx->pPartIdxWhere!=0 + || pIdx->nColumn>ArraySize(pLoop->aLTermSpace) + ) continue; for(j=0; jnColumn; j++){ pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx); if( pTerm==0 ) break; - whereLoopResize(pWInfo->pParse->db, pLoop, j); pLoop->aLTerm[j] = pTerm; } if( j!=pIdx->nColumn ) continue; diff --git a/test/alter.test b/test/alter.test index aca71c4405..faa6b0d258 100644 --- a/test/alter.test +++ b/test/alter.test @@ -847,7 +847,7 @@ do_test alter-14.2 { set system_table_list {1 sqlite_master} catchsql ANALYZE ifcapable analyze { lappend system_table_list 2 sqlite_stat1 } -ifcapable stat3 { lappend system_table_list 4 sqlite_stat3 } +ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 } foreach {tn tbl} $system_table_list { do_test alter-15.$tn.1 { diff --git a/test/alter4.test b/test/alter4.test index cda45533c6..59704fed4c 100644 --- a/test/alter4.test +++ b/test/alter4.test @@ -143,6 +143,11 @@ do_test alter4-2.6 { alter table t1 add column d DEFAULT CURRENT_TIME; } } {1 {Cannot add a column with non-constant default}} +do_test alter4-2.7 { + catchsql { + alter table t1 add column d default (-+1); + } +} {1 {Cannot add a column with non-constant default}} do_test alter4-2.99 { execsql { DROP TABLE t1; diff --git a/test/analyze.test b/test/analyze.test index 362702a9c2..c445084263 100644 --- a/test/analyze.test +++ b/test/analyze.test @@ -288,7 +288,7 @@ do_test analyze-4.3 { } {} # Verify that DROP TABLE and DROP INDEX remove entries from the -# sqlite_stat1 and sqlite_stat3 tables. +# sqlite_stat1, sqlite_stat3 and sqlite_stat4 tables. # do_test analyze-5.0 { execsql { @@ -306,12 +306,13 @@ do_test analyze-5.0 { SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1; } } {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4} -ifcapable stat3 { +ifcapable stat4||stat3 { + ifcapable stat4 {set stat sqlite_stat4} else {set stat sqlite_stat3} do_test analyze-5.1 { - execsql { - SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1; - SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1; - } + execsql " + SELECT DISTINCT idx FROM $stat ORDER BY 1; + SELECT DISTINCT tbl FROM $stat ORDER BY 1; + " } {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4} } do_test analyze-5.2 { @@ -321,12 +322,12 @@ do_test analyze-5.2 { SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1; } } {t3i1 t3i3 t4i1 t4i2 t3 t4} -ifcapable stat3 { +ifcapable stat4||stat3 { do_test analyze-5.3 { - execsql { - SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1; - SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1; - } + execsql " + SELECT DISTINCT idx FROM $stat ORDER BY 1; + SELECT DISTINCT tbl FROM $stat ORDER BY 1; + " } {t3i1 t3i3 t4i1 t4i2 t3 t4} } do_test analyze-5.4 { @@ -336,12 +337,12 @@ do_test analyze-5.4 { SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1; } } {t4i1 t4i2 t4} -ifcapable stat3 { +ifcapable stat4||stat3 { do_test analyze-5.5 { - execsql { - SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1; - SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1; - } + execsql " + SELECT DISTINCT idx FROM $stat ORDER BY 1; + SELECT DISTINCT tbl FROM $stat ORDER BY 1; + " } {t4i1 t4i2 t4} } @@ -360,5 +361,4 @@ do_test analyze-99.1 { } } {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}} - finish_test diff --git a/test/analyze3.test b/test/analyze3.test index 1e95d591b7..fb26303ee3 100644 --- a/test/analyze3.test +++ b/test/analyze3.test @@ -17,7 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !stat3 { +ifcapable !stat4&&!stat3 { finish_test return } @@ -95,7 +95,13 @@ do_test analyze3-1.1.1 { COMMIT; ANALYZE; } -} {} + + ifcapable stat4 { + execsql { SELECT count(*)>0 FROM sqlite_stat4; } + } else { + execsql { SELECT count(*)>0 FROM sqlite_stat3; } + } +} {1} do_eqp_test analyze3-1.1.2 { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 @@ -312,7 +318,6 @@ do_test analyze3-3.1 { execsql COMMIT execsql ANALYZE } {} - do_test analyze3-3.2.1 { set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy] sqlite3_expired $S diff --git a/test/analyze5.test b/test/analyze5.test index c400780fff..ac175c07b5 100644 --- a/test/analyze5.test +++ b/test/analyze5.test @@ -10,14 +10,14 @@ #*********************************************************************** # # This file implements tests for SQLite library. The focus of the tests -# in this file is the use of the sqlite_stat3 histogram data on tables +# in this file is the use of the sqlite_stat4 histogram data on tables # with many repeated values and only a few distinct values. # set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !stat3 { +ifcapable !stat4&&!stat3 { finish_test return } @@ -28,6 +28,17 @@ proc eqp {sql {db db}} { uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db } +proc alpha {blob} { + set ret "" + foreach c [split $blob {}] { + if {[string is alpha $c]} {append ret $c} + } + return $ret +} +db func alpha alpha + +db func lindex lindex + unset -nocomplain i t u v w x y z do_test analyze5-1.0 { db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)} @@ -55,17 +66,40 @@ do_test analyze5-1.0 { CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3 ANALYZE; - SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt; + } + ifcapable stat4 { + db eval { + SELECT DISTINCT lindex(test_decode(sample),0) + FROM sqlite_stat4 WHERE idx='t1u' ORDER BY nlt; + } + } else { + db eval { + SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt; + } } } {alpha bravo charlie delta} do_test analyze5-1.1 { - db eval {SELECT DISTINCT lower(sample) FROM sqlite_stat3 WHERE idx='t1v' - ORDER BY 1} + ifcapable stat4 { + db eval { + SELECT DISTINCT lower(lindex(test_decode(sample), 0)) + FROM sqlite_stat4 WHERE idx='t1v' ORDER BY 1 + } + } else { + db eval { + SELECT lower(sample) FROM sqlite_stat3 WHERE idx='t1v' ORDER BY 1 + } + } } {alpha bravo charlie delta} -do_test analyze5-1.2 { - db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1} -} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4} +ifcapable stat4 { + do_test analyze5-1.2 { + db eval {SELECT idx, count(*) FROM sqlite_stat4 GROUP BY 1 ORDER BY 1} + } {t1t 8 t1u 8 t1v 8 t1w 8 t1x 8 t1y 9 t1z 8} +} else { + do_test analyze5-1.2 { + db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1} + } {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4} +} # Verify that range queries generate the correct row count estimates # diff --git a/test/analyze6.test b/test/analyze6.test index 55e4347c6b..d2c96518be 100644 --- a/test/analyze6.test +++ b/test/analyze6.test @@ -17,7 +17,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !stat3 { +ifcapable !stat4&&!stat3 { finish_test return } diff --git a/test/analyze7.test b/test/analyze7.test index ddfe398a9b..76664546a5 100644 --- a/test/analyze7.test +++ b/test/analyze7.test @@ -82,14 +82,14 @@ do_test analyze7-3.1 { do_test analyze7-3.2.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} -ifcapable stat3 { - # If ENABLE_STAT3 is defined, SQLite comes up with a different estimated +ifcapable stat4||stat3 { + # If ENABLE_STAT4 is defined, SQLite comes up with a different estimated # row count for (c=2) than it does for (c=?). do_test analyze7-3.2.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}} } else { - # If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the + # If ENABLE_STAT4 is not defined, the expected row count for (c=2) is the # same as that for (c=?). do_test analyze7-3.2.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} @@ -98,7 +98,8 @@ ifcapable stat3 { do_test analyze7-3.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}} -ifcapable {!stat3} { + +ifcapable {!stat4 && !stat3} { do_test analyze7-3.4 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}} diff --git a/test/analyze8.test b/test/analyze8.test index 94054e1c83..4384c39676 100644 --- a/test/analyze8.test +++ b/test/analyze8.test @@ -16,7 +16,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !stat3 { +ifcapable !stat4&&!stat3 { finish_test return } @@ -84,7 +84,19 @@ do_test 2.1 { # There are many more values of c between 0 and 100000 than there are # between 800000 and 900000. So t1c is more selective for the latter # range. +# +# Test 3.2 is a little unstable. It depends on the planner estimating +# that (b BETWEEN 50 AND 54) will match more rows than (c BETWEEN +# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so +# the planner could get it wrong with an unlucky set of samples. This +# case happens to work, but others ("b BETWEEN 40 AND 44" for example) +# will fail. # +do_execsql_test 3.0 { + SELECT count(*) FROM t1 WHERE b BETWEEN 50 AND 54; + SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000; + SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000; +} {50 376 32} do_test 3.1 { eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b? AND b? AND c0; diff --git a/test/mallocA.test b/test/mallocA.test index 89951276f8..1e05723e13 100644 --- a/test/mallocA.test +++ b/test/mallocA.test @@ -15,6 +15,7 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl +set testprefix mallocA # Only run these tests if memory debugging is turned on. # @@ -40,7 +41,6 @@ db eval { db close copy_file test.db test.db.bu - do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody { ANALYZE } @@ -53,6 +53,7 @@ do_malloc_test mallocA-1.2 -testdb test.db.bu -sqlbody { do_malloc_test mallocA-1.3 -testdb test.db.bu -sqlbody { ANALYZE main.t1 } + ifcapable reindex { do_malloc_test mallocA-2 -testdb test.db.bu -sqlbody { REINDEX; @@ -68,6 +69,35 @@ ifcapable reindex { } } +reset_db +sqlite3_db_config_lookaside db 0 0 0 +do_execsql_test 6-prep { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + INSERT INTO t1 VALUES('abc', 'w'); -- rowid=1 + INSERT INTO t1 VALUES('abc', 'x'); -- rowid=2 + INSERT INTO t1 VALUES('abc', 'y'); -- rowid=3 + INSERT INTO t1 VALUES('abc', 'z'); -- rowid=4 + + INSERT INTO t1 VALUES('def', 'w'); -- rowid=5 + INSERT INTO t1 VALUES('def', 'x'); -- rowid=6 + INSERT INTO t1 VALUES('def', 'y'); -- rowid=7 + INSERT INTO t1 VALUES('def', 'z'); -- rowid=8 + + ANALYZE; +} + +do_faultsim_test 6.1 -faults oom* -body { + execsql { SELECT rowid FROM t1 WHERE a='abc' AND b='x' } +} -test { + faultsim_test_result [list 0 2] +} +do_faultsim_test 6.2 -faults oom* -body { + execsql { SELECT rowid FROM t1 WHERE a='abc' AND b<'y' } +} -test { + faultsim_test_result [list 0 {1 2}] +} + # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} diff --git a/test/permutations.test b/test/permutations.test index cd55406fa7..e6491b7480 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -503,6 +503,8 @@ test_suite "utf16" -description { pragma encoding = 'UTF-16' } -files { alter.test alter3.test + analyze.test analyze3.test analyze4.test analyze5.test analyze6.test + analyze7.test analyze8.test analyze9.test analyzeA.test auth.test bind.test blob.test capi2.test capi3.test collate1.test collate2.test collate3.test collate4.test collate5.test collate6.test conflict.test date.test delete.test expr.test fkey1.test func.test diff --git a/test/shell5.test b/test/shell5.test index 6256b281bb..bee4563372 100644 --- a/test/shell5.test +++ b/test/shell5.test @@ -242,4 +242,30 @@ SELECT * FROM t1;} 5,"this is 5"}} } +# Import columns containing quoted strings +do_test shell5-1.9 { + set out [open shell5.csv w] + puts $out {1,"",11} + puts $out {2,"x",22} + puts $out {3,"""",33} + puts $out {4,"hello",44} + puts $out "5,55,\"\"\r" + puts $out {6,66,"x"} + puts $out {7,77,""""} + puts $out {8,88,"hello"} + puts $out {"",9,99} + puts $out {"x",10,110} + puts $out {"""",11,121} + puts $out {"hello",12,132} + close $out + file delete -force test.db + catchcmd test.db {.mode csv + CREATE TABLE t1(a,b,c); +.import shell5.csv t1 + } + sqlite3 db test.db + db eval {SELECT *, '|' FROM t1 ORDER BY rowid} +} {1 {} 11 | 2 x 22 | 3 {"} 33 | 4 hello 44 | 5 55 {} | 6 66 x | 7 77 {"} | 8 88 hello | {} 9 99 | x 10 110 | {"} 11 121 | hello 12 132 |} +db close + finish_test diff --git a/test/table.test b/test/table.test index 4826cb927d..e186450a2f 100644 --- a/test/table.test +++ b/test/table.test @@ -268,6 +268,7 @@ do_test table-5.2.1 { DROP TABLE IF EXISTS sqlite_stat1; DROP TABLE IF EXISTS sqlite_stat2; DROP TABLE IF EXISTS sqlite_stat3; + DROP TABLE IF EXISTS sqlite_stat4; SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_stat*'; } } {} diff --git a/test/tester.tcl b/test/tester.tcl index 4486bcd687..a62300e183 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -14,7 +14,7 @@ # $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $ #------------------------------------------------------------------------- -# The commands provided by the code in this file to help with creating +# The commands provided by the code in this file to help with creating # test cases are as follows: # # Commands to manipulate the db and the file-system at a high level: @@ -42,6 +42,7 @@ # # Commands to execute/explain SQL statements: # +# memdbsql SQL # stepsql DB SQL # execsql2 SQL # explain_no_trace SQL @@ -80,7 +81,7 @@ # presql # -# Set the precision of FP arithmatic used by the interpreter. And +# Set the precision of FP arithmatic used by the interpreter. And # configure SQLite to take database file locks on the page that begins # 64KB into the database file instead of the one 1GB in. This means # the code that handles that special case can be tested without creating @@ -90,7 +91,7 @@ set tcl_precision 15 sqlite3_test_control_pending_byte 0x0010000 -# If the pager codec is available, create a wrapper for the [sqlite3] +# If the pager codec is available, create a wrapper for the [sqlite3] # command that appends "-key {xyzzy}" to the command line. i.e. this: # # sqlite3 db test.db @@ -122,7 +123,7 @@ if {[info command sqlite_orig]==""} { } set res } else { - # This command is not opening a new database connection. Pass the + # This command is not opening a new database connection. Pass the # arguments through to the C implementation as the are. # uplevel 1 sqlite_orig $args @@ -291,6 +292,66 @@ proc do_delete_file {force args} { } } +if {$::tcl_platform(platform) eq "windows"} { + proc do_remove_win32_dir {args} { + set nRetry [getFileRetries] ;# Maximum number of retries. + set nDelay [getFileRetryDelay] ;# Delay in ms before retrying. + + foreach dirName $args { + # On windows, sometimes even a [remove_win32_dir] can fail just after + # a directory is emptied. The cause is usually "tag-alongs" - programs + # like anti-virus software, automatic backup tools and various explorer + # extensions that keep a file open a little longer than we expect, + # causing the delete to fail. + # + # The solution is to wait a short amount of time before retrying the + # removal. + # + if {$nRetry > 0} { + for {set i 0} {$i < $nRetry} {incr i} { + set rc [catch { + remove_win32_dir $dirName + } msg] + if {$rc == 0} break + if {$nDelay > 0} { after $nDelay } + } + if {$rc} { error $msg } + } else { + remove_win32_dir $dirName + } + } + } + + proc do_delete_win32_file {args} { + set nRetry [getFileRetries] ;# Maximum number of retries. + set nDelay [getFileRetryDelay] ;# Delay in ms before retrying. + + foreach fileName $args { + # On windows, sometimes even a [delete_win32_file] can fail just after + # a file is closed. The cause is usually "tag-alongs" - programs like + # anti-virus software, automatic backup tools and various explorer + # extensions that keep a file open a little longer than we expect, + # causing the delete to fail. + # + # The solution is to wait a short amount of time before retrying the + # delete. + # + if {$nRetry > 0} { + for {set i 0} {$i < $nRetry} {incr i} { + set rc [catch { + delete_win32_file $fileName + } msg] + if {$rc == 0} break + if {$nDelay > 0} { after $nDelay } + } + if {$rc} { error $msg } + } else { + delete_win32_file $fileName + } + } + } +} + proc execpresql {handle args} { trace remove execution $handle enter [list execpresql $handle] if {[info exists ::G(perm:presql)]} { @@ -312,8 +373,8 @@ proc do_not_use_codec {} { # if {[info exists cmdlinearg]==0} { - # Parse any options specified in the $argv array. This script accepts the - # following options: + # Parse any options specified in the $argv array. This script accepts the + # following options: # # --pause # --soft-heap-limit=NN @@ -342,7 +403,7 @@ if {[info exists cmdlinearg]==0} { foreach a $argv { switch -regexp -- $a { {^-+pause$} { - # Wait for user input before continuing. This is to give the user an + # Wait for user input before continuing. This is to give the user an # opportunity to connect profiling tools to the process. puts -nonewline "Press RETURN to begin..." flush stdout @@ -405,8 +466,8 @@ if {[info exists cmdlinearg]==0} { # Install the malloc layer used to inject OOM errors. And the 'automatic' # extensions. This only needs to be done once for the process. # - sqlite3_shutdown - install_malloc_faultsim 1 + sqlite3_shutdown + install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions @@ -516,7 +577,7 @@ proc incr_ntest {} { } -# Invoke the do_test procedure to run a single test +# Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { global argv cmdlinearg @@ -525,7 +586,7 @@ proc do_test {name cmd expected} { sqlite3_memdebug_settitle $name -# if {[llength $argv]==0} { +# if {[llength $argv]==0} { # set go 1 # } else { # set go 0 @@ -628,7 +689,7 @@ proc do_realnum_test {name cmd expected} { proc fix_testname {varname} { upvar $varname testname - if {[info exists ::testprefix] + if {[info exists ::testprefix] && [string is digit [string range $testname 0 0]] } { set testname "${::testprefix}-$testname" @@ -640,7 +701,7 @@ proc normalize_list {L} { foreach l $L {lappend L2 $l} set L2 } - + proc do_execsql_test {testname sql {result {}}} { fix_testname testname uplevel do_test [list $testname] [list "execsql {$sql}"] [list [list {*}$result]] @@ -726,7 +787,7 @@ proc delete_all_data {} { } } -# Run an SQL script. +# Run an SQL script. # Return the number of microseconds per statement. # proc speed_trial {name numstmt units sql} { @@ -990,6 +1051,15 @@ proc execsql2 {sql} { return $result } +# Use a temporary in-memory database to execute SQL statements +# +proc memdbsql {sql} { + sqlite3 memdb :memory: + set result [memdb eval $sql] + memdb close + return $result +} + # Use the non-callback API to execute multiple SQL statements # proc stepsql {dbptr sql} { @@ -1104,7 +1174,7 @@ proc crashsql {args} { set crashfile "" set dc "" set sql [lindex $args end] - + for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} { set z [lindex $args $ii] set n [string length $z] @@ -1123,7 +1193,7 @@ proc crashsql {args} { error "Compulsory option -file missing" } - # $crashfile gets compared to the native filename in + # $crashfile gets compared to the native filename in # cfSync(), which can be different then what TCL uses by # default, so here we force it to the "nativename" format. set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]] @@ -1158,7 +1228,7 @@ proc crashsql {args} { set r [catch { exec [info nameofexec] crash.tcl >@stdout } msg] - + # Windows/ActiveState TCL returns a slightly different # error message. We map that to the expected message # so that we don't have to change all of the test @@ -1168,7 +1238,7 @@ proc crashsql {args} { set msg "child process exited abnormally" } } - + lappend r $msg } @@ -1194,7 +1264,7 @@ proc run_ioerr_prep {} { # Usage: do_ioerr_test # # This proc is used to implement test cases that check that IO errors -# are correctly handled. The first argument, , is an integer +# are correctly handled. The first argument, , is an integer # used to name the tests executed by this proc. Options are as follows: # # -tclprep TCL script to run to prepare test. @@ -1223,7 +1293,7 @@ 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 {} @@ -1247,7 +1317,7 @@ proc do_ioerr_test {testname args} { set ::TN $n incr ::ioerropts(-count) -1 if {$::ioerropts(-count)<0} break - + # Skip this IO error if it was specified with the "-exclude" option. if {[info exists ::ioerropts(-exclude)]} { if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue @@ -1256,7 +1326,7 @@ proc do_ioerr_test {testname args} { restore_prng_state } - # Delete the files test.db and test2.db, then execute the TCL and + # 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 { run_ioerr_prep @@ -1274,7 +1344,7 @@ proc do_ioerr_test {testname args} { }] $n # Execute the TCL script created for the body of this test. If - # at least N IO operations performed by SQLite as a result of + # 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 @@ -1328,12 +1398,12 @@ proc do_ioerr_test {testname args} { set ::sqlite_io_error_hit 0 set ::sqlite_io_error_pending 0 - # Check that no page references were leaked. There should be - # a single reference if there is still an active transaction, + # Check that no page references were leaked. There should be + # a single reference if there is still an active transaction, # or zero otherwise. # # UPDATE: If the IO error occurs after a 'BEGIN' but before any - # locks are established on database files (i.e. if the error + # locks are established on database files (i.e. if the error # occurs while attempting to detect a hot-journal file), then # there may 0 page references and an active transaction according # to [sqlite3_get_autocommit]. @@ -1349,7 +1419,7 @@ proc do_ioerr_test {testname args} { } {1} } - # If there is an open database handle and no open transaction, + # If there is an open database handle and no open transaction, # and the pager is not running in exclusive-locking mode, # check that the pager is in "unlocked" state. Theoretically, # if a call to xUnlock() failed due to an IO error the underlying @@ -1453,7 +1523,7 @@ proc allcksum {{db db}} { } # Generate a checksum based on the contents of a single database with -# a database connection. The name of the database is $dbname. +# a database connection. The name of the database is $dbname. # Examples of $dbname are "temp" or "main". # proc dbcksum {db dbname} { @@ -1547,8 +1617,8 @@ proc drop_all_tables {{db db}} { #------------------------------------------------------------------------- # If a test script is executed with global variable $::G(perm:name) set to -# "wal", then the tests are run in WAL mode. Otherwise, they should be run -# in rollback mode. The following Tcl procs are used to make this less +# "wal", then the tests are run in WAL mode. Otherwise, they should be run +# in rollback mode. The following Tcl procs are used to make this less # intrusive: # # wal_set_journal_mode ?DB? @@ -1563,9 +1633,9 @@ proc drop_all_tables {{db db}} { # Otherwise (if not running a WAL permutation) this is a no-op. # # wal_is_wal_mode -# +# # Returns true if this test should be run in WAL mode. False otherwise. -# +# proc wal_is_wal_mode {} { expr {[permutation] eq "wal"} } @@ -1666,10 +1736,10 @@ proc slave_test_file {zFile} { } set ::sqlite_open_file_count 0 - # Test that the global "shared-cache" setting was not altered by + # Test that the global "shared-cache" setting was not altered by # the test script. # - ifcapable shared_cache { + ifcapable shared_cache { set res [expr {[sqlite3_enable_shared_cache] == $scs}] do_test ${tail}-sharedcachesetting [list set {} $res] 1 } diff --git a/test/tkt-9f2eb3abac.test b/test/tkt-9f2eb3abac.test new file mode 100644 index 0000000000..5b93733afe --- /dev/null +++ b/test/tkt-9f2eb3abac.test @@ -0,0 +1,79 @@ + +# 2013 August 29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/malloc_common.tcl +set ::testprefix tkt-9f2eb3abac + +do_execsql_test 1.1 { + CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,d,e)); + SELECT * FROM t1 WHERE a=? AND b=? AND c=? AND d=? AND e=?; +} {} + +do_execsql_test 1.2 { + CREATE TABLE "a" ( + "b" integer NOT NULL, + "c" integer NOT NULL, + PRIMARY KEY ("b", "c") + ); + + CREATE TABLE "d" ( + "e" integer NOT NULL, + "g" integer NOT NULL, + "f" integer NOT NULL, + "h" integer NOT NULL, + "i" character(10) NOT NULL, + "j" int, + PRIMARY KEY ("e", "g", "f", "h") + ); + + CREATE TABLE "d_to_a" ( + "f_e" integer NOT NULL, + "f_g" integer NOT NULL, + "f_f" integer NOT NULL, + "f_h" integer NOT NULL, + "t_b" integer NOT NULL, + "t_c" integer NOT NULL, + "r" character NOT NULL, + "s" integer, + PRIMARY KEY ("f_e", "f_g", "f_f", "f_h", "t_b", "t_c") + ); + + INSERT INTO d (g, e, h, f, j, i) VALUES ( 1, 1, 1, 1, 1, 1 ); + INSERT INTO a (b, c) VALUES ( 1, 1 ); + INSERT INTO d_to_a VALUES (1, 1, 1, 1, 1, 1, 1, 1); + + DELETE FROM d_to_a + WHERE f_g = 1 AND f_e = 1 AND f_h = 1 AND f_f = 1 AND t_b = 1 AND t_c = 1; + + SELECT * FROM d_to_a; +} {} + +faultsim_delete_and_reopen +do_execsql_test 2.0 { CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,d,e)) } +do_execsql_test 2.1 { CREATE TABLE t2(x) } +faultsim_save_and_close + +do_faultsim_test 3 -faults oom* -prep { + faultsim_restore_and_reopen + execsql { SELECT 1 FROM sqlite_master } +} -body { + execsql { SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=? AND e=? } +} -test { + faultsim_test_result {0 {}} +} + +finish_test + diff --git a/test/tkt-cbd054fa6b.test b/test/tkt-cbd054fa6b.test index 51e01991d4..2951233a5b 100644 --- a/test/tkt-cbd054fa6b.test +++ b/test/tkt-cbd054fa6b.test @@ -16,11 +16,26 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !stat3 { +ifcapable !stat4&&!stat3 { finish_test return } +proc s {blob} { + set ret "" + binary scan $blob c* bytes + foreach b $bytes { + set t [binary format c $b] + if {[string is print $t]} { + append ret $t + } else { + append ret . + } + } + return $ret +} +db function s s + do_test tkt-cbd05-1.1 { db eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT UNIQUE NOT NULL); @@ -39,18 +54,30 @@ do_test tkt-cbd05-1.1 { } } {10} do_test tkt-cbd05-1.2 { - db eval { - ANALYZE; + db eval { ANALYZE; } + ifcapable stat4 { + db eval { + PRAGMA writable_schema = 1; + CREATE VIEW vvv AS + SELECT tbl,idx,neq,nlt,ndlt,test_extract(sample,0) AS sample + FROM sqlite_stat4; + PRAGMA writable_schema = 0; + } + } else { + db eval { + CREATE VIEW vvv AS + SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat3; + } } } {} do_test tkt-cbd05-1.3 { execsql { - SELECT tbl,idx,group_concat(sample,' ') - FROM sqlite_stat3 + SELECT tbl,idx,group_concat(s(sample),' ') + FROM vvv WHERE idx = 't1_x' GROUP BY tbl,idx } -} {/t1 t1_x .[ ABCDEFGHI]{10}./} +} {t1 t1_x { A B C D E F G H I}} do_test tkt-cbd05-2.1 { db eval { @@ -77,11 +104,11 @@ do_test tkt-cbd05-2.2 { } {} do_test tkt-cbd05-2.3 { execsql { - SELECT tbl,idx,group_concat(sample,' ') - FROM sqlite_stat3 + SELECT tbl,idx,group_concat(s(sample),' ') + FROM vvv WHERE idx = 't1_x' GROUP BY tbl,idx } -} {/t1 t1_x .[ ABCDEFGHI]{10}./} +} {t1 t1_x { A B C D E F G H I}} finish_test diff --git a/test/where9.test b/test/where9.test index 5b000d967e..1c32ff82f7 100644 --- a/test/where9.test +++ b/test/where9.test @@ -781,11 +781,11 @@ do_test where9-6.8.2 { OR (b NOT NULL AND c NOT NULL AND d IS NULL) } } {1 {no query solution}} -ifcapable stat3 { +ifcapable stat4||stat3 { # When STAT3 is enabled, the "b NOT NULL" terms get translated # into b>NULL, which can be satified by the index t1b. It is a very # expensive way to do the query, but it works, and so a solution is possible. - do_test where9-6.8.3-stat3 { + do_test where9-6.8.3-stat4 { catchsql { UPDATE t1 INDEXED BY t1b SET a=a+100 WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) @@ -793,7 +793,7 @@ ifcapable stat3 { OR (b NOT NULL AND c NOT NULL AND d IS NULL) } } {0 {}} - do_test where9-6.8.4-stat3 { + do_test where9-6.8.4-stat4 { catchsql { DELETE FROM t1 INDEXED BY t1b WHERE (b IS NULL AND c NOT NULL AND d NOT NULL) @@ -851,6 +851,11 @@ do_test where9-7.0 { INSERT INTO t6 SELECT * FROM t5; ANALYZE t5; } + ifcapable stat3 { + sqlite3 db2 test.db + db2 eval { DROP TABLE IF EXISTS sqlite_stat3 } + db2 close + } } {} do_test where9-7.1.1 { count_steps { @@ -951,4 +956,28 @@ do_test where9-9.1 { } } {1 2 3 4 8 9} +# Fix for ticket [bc878246eafe0f52c519e29049b2fe4a99491b27] +# Incorrect result when OR is used in a join to the right of a LEFT JOIN +# +do_test where9-10.1 { + db eval { + CREATE TABLE t101 (id INTEGER PRIMARY KEY); + INSERT INTO t101 VALUES (1); + SELECT * FROM t101 AS t0 + LEFT JOIN t101 AS t1 ON t1.id BETWEEN 10 AND 20 + JOIN t101 AS t2 ON (t2.id = t0.id OR (t2.id<>555 AND t2.id=t1.id)); + } +} {1 {} 1} +do_test where9-10.2 { + db eval { + CREATE TABLE t102 (id TEXT UNIQUE NOT NULL); + INSERT INTO t102 VALUES ('1'); + SELECT * FROM t102 AS t0 + LEFT JOIN t102 AS t1 ON t1.id GLOB 'abc%' + JOIN t102 AS t2 ON (t2.id = t0.id OR (t2.id<>555 AND t2.id=t1.id)); + } +} {1 {} 1} + + + finish_test diff --git a/test/win32longpath.test b/test/win32longpath.test new file mode 100644 index 0000000000..0a6a8f98e6 --- /dev/null +++ b/test/win32longpath.test @@ -0,0 +1,102 @@ +# 2013 August 27 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is testing the file name handling provided +# by the "win32-longpath" VFS. +# + +if {$tcl_platform(platform)!="windows"} return + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix win32longpath + +db close +set path [file nativename [get_pwd]] +sqlite3 db [file join $path test.db] -vfs win32-longpath + +do_test 1.1 { + db eval { + BEGIN EXCLUSIVE; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t1 VALUES(3); + INSERT INTO t1 VALUES(4); + SELECT x FROM t1 ORDER BY x; + COMMIT; + } +} {1 2 3 4} + +set longPath(1) \\\\?\\$path\\[pid] +make_win32_dir $longPath(1) + +set longPath(2) $longPath(1)\\[string repeat X 255] +make_win32_dir $longPath(2) + +set longPath(3) $longPath(2)\\[string repeat Y 255] +make_win32_dir $longPath(3) + +set fileName $longPath(3)\\test.db + +do_test 1.2 { + list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg +} {1 {unable to open database file}} + +sqlite3 db3 $fileName -vfs win32-longpath + +do_test 1.3 { + db3 eval { + BEGIN EXCLUSIVE; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(5); + INSERT INTO t1 VALUES(6); + INSERT INTO t1 VALUES(7); + INSERT INTO t1 VALUES(8); + SELECT x FROM t1 ORDER BY x; + COMMIT; + } +} {5 6 7 8} + +db3 close +# puts " Database exists \{[exists_win32_path $fileName]\}" + +sqlite3 db3 $fileName -vfs win32-longpath + +do_test 1.4 { + db3 eval { + PRAGMA journal_mode = WAL; + } +} {wal} + +do_test 1.5 { + db3 eval { + BEGIN EXCLUSIVE; + INSERT INTO t1 VALUES(9); + INSERT INTO t1 VALUES(10); + INSERT INTO t1 VALUES(11); + INSERT INTO t1 VALUES(12); + SELECT x FROM t1 ORDER BY x; + COMMIT; + } +} {5 6 7 8 9 10 11 12} + +db3 close +# puts " Database exists \{[exists_win32_path $fileName]\}" + +do_delete_win32_file $fileName +# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}" + +do_remove_win32_dir $longPath(3) +do_remove_win32_dir $longPath(2) +do_remove_win32_dir $longPath(1) + +finish_test